xref: /illumos-gate/usr/src/uts/common/io/scsi/impl/scsi_resource.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
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