xref: /illumos-gate/usr/src/uts/common/io/scsi/impl/scsi_resource.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 2004 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 struct scsi_pkt *
128 scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *in_pktp,
129     struct buf *bp, int cmdlen, int statuslen, int pplen,
130     int flags, int (*callback)(caddr_t), caddr_t callback_arg)
131 {
132 	struct scsi_pkt *pktp;
133 	scsi_hba_tran_t *tranp = ap->a_hba_tran;
134 	int		(*func)(caddr_t);
135 
136 	TRACE_5(TR_FAC_SCSI_RES, TR_SCSI_INIT_PKT_START,
137 "scsi_init_pkt_start: addr %p in_pktp %p cmdlen %d statuslen %d pplen %d",
138 	    ap, in_pktp, cmdlen, statuslen, pplen);
139 
140 #if defined(__i386) || defined(__amd64)
141 	if (flags & PKT_CONSISTENT_OLD) {
142 		flags &= ~PKT_CONSISTENT_OLD;
143 		flags |= PKT_CONSISTENT;
144 	}
145 #endif
146 
147 	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;
148 
149 	pktp = (*tranp->tran_init_pkt) (ap, in_pktp, bp, cmdlen,
150 		statuslen, pplen, flags, func, NULL);
151 	if (pktp == NULL) {
152 		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
153 			ddi_set_callback(callback, callback_arg,
154 				&scsi_callback_id);
155 		}
156 	}
157 
158 	TRACE_1(TR_FAC_SCSI_RES, TR_SCSI_INIT_PKT_END,
159 		"scsi_init_pkt_end: pktp %p", pktp);
160 	return (pktp);
161 }
162 
163 void
164 scsi_destroy_pkt(struct scsi_pkt *pkt)
165 {
166 	struct scsi_address	*ap = P_TO_ADDR(pkt);
167 
168 	TRACE_1(TR_FAC_SCSI_RES, TR_SCSI_DESTROY_PKT_START,
169 		"scsi_destroy_pkt_start: pkt %p", pkt);
170 
171 	(*A_TO_TRAN(ap)->tran_destroy_pkt)(ap, pkt);
172 
173 	if (scsi_callback_id != 0) {
174 		ddi_run_callback(&scsi_callback_id);
175 	}
176 
177 	TRACE_0(TR_FAC_SCSI_RES, TR_SCSI_DESTROY_PKT_END,
178 		"scsi_destroy_pkt_end");
179 }
180 
181 
182 /*
183  *	Generic Resource Allocation Routines
184  */
185 
186 struct scsi_pkt *
187 scsi_resalloc(struct scsi_address *ap, int cmdlen, int statuslen,
188     opaque_t dmatoken, int (*callback)())
189 {
190 	register struct	scsi_pkt *pkt;
191 	register scsi_hba_tran_t *tranp = ap->a_hba_tran;
192 	register int			(*func)(caddr_t);
193 
194 	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;
195 
196 	pkt = (*tranp->tran_init_pkt) (ap, NULL, (struct buf *)dmatoken,
197 		cmdlen, statuslen, 0, 0, func, NULL);
198 	if (pkt == NULL) {
199 		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
200 			ddi_set_callback(callback, NULL, &scsi_callback_id);
201 		}
202 	}
203 
204 	return (pkt);
205 }
206 
207 struct scsi_pkt *
208 scsi_pktalloc(struct scsi_address *ap, int cmdlen, int statuslen,
209     int (*callback)())
210 {
211 	struct scsi_pkt		*pkt;
212 	struct scsi_hba_tran	*tran = ap->a_hba_tran;
213 	register int			(*func)(caddr_t);
214 
215 	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;
216 
217 	pkt = (*tran->tran_init_pkt) (ap, NULL, NULL, cmdlen,
218 		statuslen, 0, 0, func, NULL);
219 	if (pkt == NULL) {
220 		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
221 			ddi_set_callback(callback, NULL, &scsi_callback_id);
222 		}
223 	}
224 
225 	return (pkt);
226 }
227 
228 struct scsi_pkt *
229 scsi_dmaget(struct scsi_pkt *pkt, opaque_t dmatoken, int (*callback)())
230 {
231 	struct scsi_pkt		*new_pkt;
232 	register int		(*func)(caddr_t);
233 
234 	func = (callback == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC;
235 
236 	new_pkt = (*P_TO_TRAN(pkt)->tran_init_pkt) (&pkt->pkt_address,
237 		pkt, (struct buf *)dmatoken,
238 		0, 0, 0, 0, func, NULL);
239 	ASSERT(new_pkt == pkt || new_pkt == NULL);
240 	if (new_pkt == NULL) {
241 		if (callback != NULL_FUNC && callback != SLEEP_FUNC) {
242 			ddi_set_callback(callback, NULL, &scsi_callback_id);
243 		}
244 	}
245 
246 	return (new_pkt);
247 }
248 
249 
250 /*
251  *	Generic Resource Deallocation Routines
252  */
253 
254 void
255 scsi_dmafree(struct scsi_pkt *pkt)
256 {
257 	register struct scsi_address	*ap = P_TO_ADDR(pkt);
258 	(*A_TO_TRAN(ap)->tran_dmafree)(ap, pkt);
259 
260 	if (scsi_callback_id != 0) {
261 		ddi_run_callback(&scsi_callback_id);
262 	}
263 }
264 
265 void
266 scsi_sync_pkt(struct scsi_pkt *pkt)
267 {
268 	register struct scsi_address	*ap = P_TO_ADDR(pkt);
269 	(*A_TO_TRAN(ap)->tran_sync_pkt)(ap, pkt);
270 }
271 
272 void
273 scsi_resfree(struct scsi_pkt *pkt)
274 {
275 	register struct scsi_address	*ap = P_TO_ADDR(pkt);
276 	(*A_TO_TRAN(ap)->tran_destroy_pkt)(ap, pkt);
277 
278 	if (scsi_callback_id != 0) {
279 		ddi_run_callback(&scsi_callback_id);
280 	}
281 }
282