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