1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/scsi/scsi.h> 30 #include <sys/vtrace.h> 31 32 33 #define A_TO_TRAN(ap) ((ap)->a_hba_tran) 34 #define P_TO_TRAN(pkt) ((pkt)->pkt_address.a_hba_tran) 35 #define P_TO_ADDR(pkt) (&((pkt)->pkt_address)) 36 37 /* 38 * Callback id 39 */ 40 uintptr_t scsi_callback_id = 0; 41 42 extern ddi_dma_attr_t scsi_alloc_attr; 43 44 struct buf * 45 scsi_alloc_consistent_buf(struct scsi_address *ap, 46 struct buf *in_bp, size_t datalen, uint_t bflags, 47 int (*callback)(caddr_t), caddr_t callback_arg) 48 { 49 dev_info_t *pdip; 50 struct buf *bp; 51 int kmflag; 52 size_t rlen; 53 54 TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_ALLOC_CONSISTENT_BUF_START, 55 "scsi_alloc_consistent_buf_start"); 56 57 if (!in_bp) { 58 kmflag = (callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP; 59 if ((bp = getrbuf(kmflag)) == NULL) { 60 goto no_resource; 61 } 62 } else { 63 bp = in_bp; 64 65 /* we are establishing a new buffer memory association */ 66 bp->b_flags &= ~(B_PAGEIO | B_PHYS | B_REMAPPED | B_SHADOW); 67 bp->b_proc = NULL; 68 bp->b_pages = NULL; 69 bp->b_shadow = NULL; 70 } 71 72 /* limit bits that can be set by bflags argument */ 73 ASSERT(!(bflags & ~(B_READ | B_WRITE))); 74 bflags &= (B_READ | B_WRITE); 75 bp->b_un.b_addr = 0; 76 77 if (datalen) { 78 pdip = (A_TO_TRAN(ap))->tran_hba_dip; 79 80 /* 81 * use i_ddi_mem_alloc() for now until we have an interface to 82 * allocate memory for DMA which doesn't require a DMA handle. 83 * ddi_iopb_alloc() is obsolete and we want more flexibility in 84 * controlling the DMA address constraints. 85 */ 86 while (i_ddi_mem_alloc(pdip, &scsi_alloc_attr, datalen, 87 ((callback == SLEEP_FUNC) ? 1 : 0), 0, NULL, 88 &bp->b_un.b_addr, &rlen, NULL) != DDI_SUCCESS) { 89 if (callback == SLEEP_FUNC) { 90 delay(drv_usectohz(10000)); 91 } else { 92 if (!in_bp) 93 freerbuf(bp); 94 goto no_resource; 95 } 96 } 97 bp->b_flags |= bflags; 98 } 99 bp->b_bcount = datalen; 100 bp->b_resid = 0; 101 102 TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_ALLOC_CONSISTENT_BUF_END, 103 "scsi_alloc_consistent_buf_end"); 104 return (bp); 105 106 no_resource: 107 108 if (callback != NULL_FUNC && callback != SLEEP_FUNC) { 109 ddi_set_callback(callback, callback_arg, 110 &scsi_callback_id); 111 } 112 TRACE_0(TR_FAC_SCSI_RES, 113 TR_SCSI_ALLOC_CONSISTENT_BUF_RETURN1_END, 114 "scsi_alloc_consistent_buf_end (return1)"); 115 return (NULL); 116 } 117 118 void 119 scsi_free_consistent_buf(struct buf *bp) 120 { 121 TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_FREE_CONSISTENT_BUF_START, 122 "scsi_free_consistent_buf_start"); 123 if (!bp) 124 return; 125 if (bp->b_un.b_addr) 126 i_ddi_mem_free((caddr_t)bp->b_un.b_addr, 0); 127 freerbuf(bp); 128 if (scsi_callback_id != 0) { 129 ddi_run_callback(&scsi_callback_id); 130 } 131 TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_FREE_CONSISTENT_BUF_END, 132 "scsi_free_consistent_buf_end"); 133 } 134 135 void scsi_free_cache_pkt(struct scsi_address *, struct scsi_pkt *); 136 137 struct scsi_pkt * 138 scsi_init_cache_pkt(struct scsi_address *ap, struct scsi_pkt *in_pktp, 139 struct buf *bp, int cmdlen, int statuslen, int pplen, 140 int flags, int (*callback)(caddr_t), caddr_t callback_arg) 141 { 142 struct scsi_pkt_cache_wrapper *pktw; 143 scsi_hba_tran_t *tranp = ap->a_hba_tran; 144 int (*func)(caddr_t); 145 146 func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC; 147 148 if (in_pktp == NULL) { 149 int kf; 150 151 if (callback == SLEEP_FUNC) 152 kf = KM_SLEEP; 153 else 154 kf = KM_NOSLEEP; 155 pktw = kmem_cache_alloc(tranp->tran_pkt_cache_ptr, 156 kf); 157 if (pktw == NULL) 158 goto fail1; 159 160 pktw->pcw_kmflags = 0; 161 in_pktp = &(pktw->pcw_pkt); 162 /* 163 * target drivers should initialize pkt_comp and 164 * pkt_time, but sometimes they don't so initialize 165 * them here to be safe. 166 */ 167 in_pktp->pkt_address = *ap; 168 in_pktp->pkt_flags = 0; 169 in_pktp->pkt_time = 0; 170 in_pktp->pkt_resid = 0; 171 in_pktp->pkt_state = 0; 172 in_pktp->pkt_statistics = 0; 173 in_pktp->pkt_reason = 0; 174 175 in_pktp->pkt_cdblen = cmdlen; 176 if ((tranp->tran_hba_flags & SCSI_HBA_TRAN_CDB) && 177 (cmdlen > DEFAULT_CDBLEN)) { 178 pktw->pcw_kmflags |= NEED_EXT_CDB; 179 in_pktp->pkt_cdbp = kmem_alloc(cmdlen, kf); 180 if (in_pktp->pkt_cdbp == NULL) 181 goto fail2; 182 } 183 in_pktp->pkt_tgtlen = pplen; 184 if (pplen > DEFAULT_PRIVLEN) { 185 pktw->pcw_kmflags |= NEED_EXT_TGT; 186 in_pktp->pkt_private = kmem_alloc(pplen, kf); 187 if (in_pktp->pkt_private == NULL) 188 goto fail3; 189 } 190 in_pktp->pkt_scblen = statuslen; 191 if ((tranp->tran_hba_flags & SCSI_HBA_TRAN_SCB) && 192 (statuslen > DEFAULT_SCBLEN)) { 193 pktw->pcw_kmflags |= NEED_EXT_SCB; 194 in_pktp->pkt_scbp = kmem_alloc(statuslen, kf); 195 if (in_pktp->pkt_scbp == NULL) 196 goto fail4; 197 } 198 if ((*tranp->tran_setup_pkt) (in_pktp, 199 func, NULL) == -1) { 200 goto fail5; 201 } 202 if (cmdlen) 203 bzero((void *)in_pktp->pkt_cdbp, cmdlen); 204 if (pplen) 205 bzero((void *)in_pktp->pkt_private, pplen); 206 if (statuslen) 207 bzero((void *)in_pktp->pkt_scbp, statuslen); 208 } 209 if (bp && bp->b_bcount) { 210 if ((*tranp->tran_setup_bp) (in_pktp, bp, 211 flags, func, NULL) == -1) { 212 scsi_free_cache_pkt(ap, in_pktp); 213 in_pktp = NULL; 214 } 215 } 216 return (in_pktp); 217 218 fail5: 219 if (pktw->pcw_kmflags & NEED_EXT_SCB) { 220 kmem_free(in_pktp->pkt_scbp, statuslen); 221 in_pktp->pkt_scbp = (opaque_t)((char *)in_pktp + 222 tranp->tran_hba_len + DEFAULT_PRIVLEN + 223 sizeof (struct scsi_pkt)); 224 if ((A_TO_TRAN(ap))->tran_hba_flags & SCSI_HBA_TRAN_CDB) 225 in_pktp->pkt_scbp = (opaque_t)((in_pktp->pkt_scbp) + 226 DEFAULT_CDBLEN); 227 in_pktp->pkt_scblen = 0; 228 } 229 fail4: 230 if (pktw->pcw_kmflags & NEED_EXT_TGT) { 231 kmem_free(in_pktp->pkt_private, pplen); 232 in_pktp->pkt_tgtlen = 0; 233 in_pktp->pkt_private = NULL; 234 } 235 fail3: 236 if (pktw->pcw_kmflags & NEED_EXT_CDB) { 237 kmem_free(in_pktp->pkt_cdbp, cmdlen); 238 in_pktp->pkt_cdbp = (opaque_t)((char *)in_pktp + 239 tranp->tran_hba_len + 240 sizeof (struct scsi_pkt)); 241 in_pktp->pkt_cdblen = 0; 242 } 243 pktw->pcw_kmflags &= 244 ~(NEED_EXT_CDB|NEED_EXT_TGT|NEED_EXT_SCB); 245 fail2: 246 kmem_cache_free(tranp->tran_pkt_cache_ptr, pktw); 247 fail1: 248 if (callback != NULL_FUNC && callback != SLEEP_FUNC) { 249 ddi_set_callback(callback, callback_arg, 250 &scsi_callback_id); 251 } 252 253 return (NULL); 254 } 255 256 void 257 scsi_free_cache_pkt(struct scsi_address *ap, struct scsi_pkt *pktp) 258 { 259 struct scsi_pkt_cache_wrapper *pktw; 260 261 (*A_TO_TRAN(ap)->tran_teardown_pkt)(pktp); 262 pktw = (struct scsi_pkt_cache_wrapper *)pktp; 263 264 /* 265 * if we allocated memory for anything that wouldn't fit, free 266 * the memory and restore the pointers 267 */ 268 if (pktw->pcw_kmflags & NEED_EXT_SCB) { 269 kmem_free(pktp->pkt_scbp, pktp->pkt_scblen); 270 pktp->pkt_scbp = (opaque_t)((char *)pktp + 271 (A_TO_TRAN(ap))->tran_hba_len + 272 DEFAULT_PRIVLEN + sizeof (struct scsi_pkt_cache_wrapper)); 273 if ((A_TO_TRAN(ap))->tran_hba_flags & SCSI_HBA_TRAN_CDB) 274 pktp->pkt_scbp = (opaque_t)((pktp->pkt_scbp) + 275 DEFAULT_CDBLEN); 276 pktp->pkt_scblen = 0; 277 } 278 if (pktw->pcw_kmflags & NEED_EXT_TGT) { 279 kmem_free(pktp->pkt_private, pktp->pkt_tgtlen); 280 pktp->pkt_tgtlen = 0; 281 pktp->pkt_private = NULL; 282 } 283 if (pktw->pcw_kmflags & NEED_EXT_CDB) { 284 kmem_free(pktp->pkt_cdbp, pktp->pkt_cdblen); 285 pktp->pkt_cdbp = (opaque_t)((char *)pktp + 286 (A_TO_TRAN(ap))->tran_hba_len + 287 sizeof (struct scsi_pkt_cache_wrapper)); 288 pktp->pkt_cdblen = 0; 289 } 290 pktw->pcw_kmflags &= 291 ~(NEED_EXT_CDB|NEED_EXT_TGT|NEED_EXT_SCB); 292 ASSERT(pktw->pcw_kmflags == 0); 293 kmem_cache_free(A_TO_TRAN(ap)->tran_pkt_cache_ptr, pktw); 294 295 if (scsi_callback_id != 0) { 296 ddi_run_callback(&scsi_callback_id); 297 } 298 299 } 300 301 struct scsi_pkt * 302 scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *in_pktp, 303 struct buf *bp, int cmdlen, int statuslen, int pplen, 304 int flags, int (*callback)(caddr_t), caddr_t callback_arg) 305 { 306 struct scsi_pkt *pktp; 307 scsi_hba_tran_t *tranp = ap->a_hba_tran; 308 int (*func)(caddr_t); 309 310 TRACE_5(TR_FAC_SCSI_RES, TR_SCSI_INIT_PKT_START, 311 "scsi_init_pkt_start: addr %p in_pktp %p cmdlen %d statuslen %d pplen %d", 312 ap, in_pktp, cmdlen, statuslen, pplen); 313 314 #if defined(__i386) || defined(__amd64) 315 if (flags & PKT_CONSISTENT_OLD) { 316 flags &= ~PKT_CONSISTENT_OLD; 317 flags |= PKT_CONSISTENT; 318 } 319 #endif 320 321 func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC; 322 323 pktp = (*tranp->tran_init_pkt) (ap, in_pktp, bp, cmdlen, 324 statuslen, pplen, flags, func, NULL); 325 if (pktp == NULL) { 326 if (callback != NULL_FUNC && callback != SLEEP_FUNC) { 327 ddi_set_callback(callback, callback_arg, 328 &scsi_callback_id); 329 } 330 } 331 332 TRACE_1(TR_FAC_SCSI_RES, TR_SCSI_INIT_PKT_END, 333 "scsi_init_pkt_end: pktp %p", pktp); 334 return (pktp); 335 } 336 337 void 338 scsi_destroy_pkt(struct scsi_pkt *pkt) 339 { 340 struct scsi_address *ap = P_TO_ADDR(pkt); 341 342 TRACE_1(TR_FAC_SCSI_RES, TR_SCSI_DESTROY_PKT_START, 343 "scsi_destroy_pkt_start: pkt %p", pkt); 344 345 (*A_TO_TRAN(ap)->tran_destroy_pkt)(ap, pkt); 346 347 if (scsi_callback_id != 0) { 348 ddi_run_callback(&scsi_callback_id); 349 } 350 351 TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_DESTROY_PKT_END, 352 "scsi_destroy_pkt_end"); 353 } 354 355 356 /* 357 * Generic Resource Allocation Routines 358 */ 359 360 struct scsi_pkt * 361 scsi_resalloc(struct scsi_address *ap, int cmdlen, int statuslen, 362 opaque_t dmatoken, int (*callback)()) 363 { 364 register struct scsi_pkt *pkt; 365 register scsi_hba_tran_t *tranp = ap->a_hba_tran; 366 register int (*func)(caddr_t); 367 368 func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC; 369 370 pkt = (*tranp->tran_init_pkt) (ap, NULL, (struct buf *)dmatoken, 371 cmdlen, statuslen, 0, 0, func, NULL); 372 if (pkt == NULL) { 373 if (callback != NULL_FUNC && callback != SLEEP_FUNC) { 374 ddi_set_callback(callback, NULL, &scsi_callback_id); 375 } 376 } 377 378 return (pkt); 379 } 380 381 struct scsi_pkt * 382 scsi_pktalloc(struct scsi_address *ap, int cmdlen, int statuslen, 383 int (*callback)()) 384 { 385 struct scsi_pkt *pkt; 386 struct scsi_hba_tran *tran = ap->a_hba_tran; 387 register int (*func)(caddr_t); 388 389 func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC; 390 391 pkt = (*tran->tran_init_pkt) (ap, NULL, NULL, cmdlen, 392 statuslen, 0, 0, func, NULL); 393 if (pkt == NULL) { 394 if (callback != NULL_FUNC && callback != SLEEP_FUNC) { 395 ddi_set_callback(callback, NULL, &scsi_callback_id); 396 } 397 } 398 399 return (pkt); 400 } 401 402 struct scsi_pkt * 403 scsi_dmaget(struct scsi_pkt *pkt, opaque_t dmatoken, int (*callback)()) 404 { 405 struct scsi_pkt *new_pkt; 406 register int (*func)(caddr_t); 407 408 func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC; 409 410 new_pkt = (*P_TO_TRAN(pkt)->tran_init_pkt) (&pkt->pkt_address, 411 pkt, (struct buf *)dmatoken, 412 0, 0, 0, 0, func, NULL); 413 ASSERT(new_pkt == pkt || new_pkt == NULL); 414 if (new_pkt == NULL) { 415 if (callback != NULL_FUNC && callback != SLEEP_FUNC) { 416 ddi_set_callback(callback, NULL, &scsi_callback_id); 417 } 418 } 419 420 return (new_pkt); 421 } 422 423 424 /* 425 * Generic Resource Deallocation Routines 426 */ 427 428 void 429 scsi_dmafree(struct scsi_pkt *pkt) 430 { 431 register struct scsi_address *ap = P_TO_ADDR(pkt); 432 (*A_TO_TRAN(ap)->tran_dmafree)(ap, pkt); 433 434 if (scsi_callback_id != 0) { 435 ddi_run_callback(&scsi_callback_id); 436 } 437 } 438 439 void 440 scsi_sync_pkt(struct scsi_pkt *pkt) 441 { 442 register struct scsi_address *ap = P_TO_ADDR(pkt); 443 (*A_TO_TRAN(ap)->tran_sync_pkt)(ap, pkt); 444 } 445 446 void 447 scsi_resfree(struct scsi_pkt *pkt) 448 { 449 register struct scsi_address *ap = P_TO_ADDR(pkt); 450 (*A_TO_TRAN(ap)->tran_destroy_pkt)(ap, pkt); 451 452 if (scsi_callback_id != 0) { 453 ddi_run_callback(&scsi_callback_id); 454 } 455 } 456