xref: /illumos-gate/usr/src/uts/intel/io/dktp/hba/ghd/ghd_waitq.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 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/types.h>
30 #include <sys/kmem.h>
31 #include <sys/note.h>
32 
33 #include "ghd.h"
34 
35 
36 
37 /*ARGSUSED*/
38 gtgt_t *
39 ghd_target_init(dev_info_t	*hba_dip,
40 		dev_info_t	*tgt_dip,
41 		ccc_t		*cccp,
42 		size_t		 tgt_private_size,
43 		void		*hba_private,
44 		ushort_t	 target,
45 		uchar_t		 lun)
46 {
47 	_NOTE(ARGUNUSED(hba_dip))
48 	gtgt_t	*gtgtp;
49 	size_t	 size = sizeof (*gtgtp) + tgt_private_size;
50 	gdev_t	*gdevp;
51 	ulong_t	 maxactive;
52 
53 	gtgtp = kmem_zalloc(size, KM_SLEEP);
54 
55 	/*
56 	 * initialize the per instance structure
57 	 */
58 
59 	gtgtp->gt_tgt_private = (void *)(gtgtp + 1);
60 	gtgtp->gt_size = size;
61 	gtgtp->gt_hba_private = hba_private;
62 	gtgtp->gt_target = target;
63 	gtgtp->gt_lun = lun;
64 	gtgtp->gt_ccc = cccp;
65 
66 	/*
67 	 * set the queue's maxactive to 1 if
68 	 * property not specified on target or hba devinfo node
69 	 */
70 	maxactive = ddi_getprop(DDI_DEV_T_ANY, tgt_dip, 0, "ghd-maxactive", 1);
71 	gtgtp->gt_maxactive = maxactive;
72 
73 	/* initialize the linked list pointers */
74 	GTGT_INIT(gtgtp);
75 
76 	/*
77 	 * grab both mutexes so the queue structures
78 	 * stay stable while adding this instance to the linked lists
79 	 */
80 	mutex_enter(&cccp->ccc_hba_mutex);
81 	mutex_enter(&cccp->ccc_waitq_mutex);
82 
83 	/*
84 	 * Search the HBA's linked list of device structures.
85 	 *
86 	 * If this device is already attached then link this instance
87 	 * to the existing per-device-structure on the ccc_devs list.
88 	 *
89 	 */
90 	gdevp = CCCP2GDEVP(cccp);
91 	while (gdevp != NULL) {
92 		if (gdevp->gd_target == target && gdevp->gd_lun == lun) {
93 			GDBG_WAITQ(("ghd_target_init(%d,%d) found gdevp 0x%p"
94 				" gtgtp 0x%p max %lu\n",
95 					target, lun, gdevp, gtgtp, maxactive));
96 
97 			goto foundit;
98 		}
99 		gdevp = GDEV_NEXTP(gdevp);
100 	}
101 
102 	/*
103 	 * Not found. This is the first instance for this device.
104 	 */
105 
106 
107 	/* allocate the per-device-structure */
108 
109 	gdevp = kmem_zalloc(sizeof (*gdevp), KM_SLEEP);
110 	gdevp->gd_target = target;
111 	gdevp->gd_lun = lun;
112 
113 	/*
114 	 * link this second level queue to the HBA's first
115 	 * level queue
116 	 */
117 	GDEV_QATTACH(gdevp, cccp, maxactive);
118 
119 	GDBG_WAITQ(("ghd_target_init(%d,%d) new gdevp 0x%p gtgtp 0x%p"
120 		    " max %lu\n", target, lun, gdevp, gtgtp, maxactive));
121 
122 foundit:
123 
124 	/* save the ptr to the per device structure */
125 	gtgtp->gt_gdevp = gdevp;
126 
127 	/* Add the per instance structure to the per device list  */
128 	GTGT_ATTACH(gtgtp, gdevp);
129 
130 	ghd_waitq_process_and_mutex_exit(cccp);
131 
132 	return (gtgtp);
133 }
134 
135 /*ARGSUSED*/
136 void
137 ghd_target_free(dev_info_t	*hba_dip,
138 		dev_info_t	*tgt_dip,
139 		ccc_t		*cccp,
140 		gtgt_t		*gtgtp)
141 {
142 	_NOTE(ARGUNUSED(hba_dip,tgt_dip))
143 
144 	gdev_t	*gdevp = gtgtp->gt_gdevp;
145 
146 	GDBG_WAITQ(("ghd_target_free(%d,%d) gdevp-0x%p gtgtp 0x%p\n",
147 		gtgtp->gt_target, gtgtp->gt_lun, gdevp, gtgtp));
148 
149 	/*
150 	 * grab both mutexes so the queue structures
151 	 * stay stable while deleting this instance
152 	 */
153 	mutex_enter(&cccp->ccc_hba_mutex);
154 	mutex_enter(&cccp->ccc_waitq_mutex);
155 
156 	ASSERT(gdevp->gd_ninstances > 0);
157 
158 	/*
159 	 * remove this per-instance structure from the device list and
160 	 * free the memory
161 	 */
162 	GTGT_DEATTACH(gtgtp, gdevp);
163 	kmem_free((caddr_t)gtgtp, gtgtp->gt_size);
164 
165 	if (gdevp->gd_ninstances == 1) {
166 		GDBG_WAITQ(("ghd_target_free: N=1 gdevp 0x%p\n", gdevp));
167 		/*
168 		 * If there's now just one instance left attached to this
169 		 * device then reset the queue's max active value
170 		 * from that instance's saved value.
171 		 */
172 		gtgtp = GDEVP2GTGTP(gdevp);
173 		GDEV_MAXACTIVE(gdevp) = gtgtp->gt_maxactive;
174 
175 	} else if (gdevp->gd_ninstances == 0) {
176 		/* else no instances left */
177 		GDBG_WAITQ(("ghd_target_free: N=0 gdevp 0x%p\n", gdevp));
178 
179 		/* detach this per-dev-structure from the HBA's dev list */
180 		GDEV_QDETACH(gdevp, cccp);
181 		kmem_free(gdevp, sizeof (*gdevp));
182 
183 	}
184 #if defined(GHD_DEBUG) || defined(__lint)
185 	else {
186 		/* leave maxactive set to 1 */
187 		GDBG_WAITQ(("ghd_target_free: N>1 gdevp 0x%p\n", gdevp));
188 	}
189 #endif
190 
191 	ghd_waitq_process_and_mutex_exit(cccp);
192 }
193 
194 void
195 ghd_waitq_shuffle_up(ccc_t *cccp, gdev_t *gdevp)
196 {
197 	gcmd_t	*gcmdp;
198 
199 	ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
200 
201 	GDBG_WAITQ(("ghd_waitq_shuffle_up: cccp 0x%p gdevp 0x%p N %ld "
202 	    "max %ld\n", cccp, gdevp, GDEV_NACTIVE(gdevp),
203 	    GDEV_MAXACTIVE(gdevp)));
204 	for (;;) {
205 		/*
206 		 * Now check the device wait queue throttle to see if I can
207 		 * shuffle up a request to the HBA wait queue.
208 		 */
209 		if (GDEV_NACTIVE(gdevp) >= GDEV_MAXACTIVE(gdevp)) {
210 			GDBG_WAITQ(("ghd_waitq_shuffle_up: N>MAX gdevp 0x%p\n",
211 				gdevp));
212 			return;
213 		}
214 
215 		/*
216 		 * single thread requests while multiple instances
217 		 * because the different target drives might have
218 		 * conflicting maxactive throttles.
219 		 */
220 		if (gdevp->gd_ninstances > 1 && GDEV_NACTIVE(gdevp) > 0) {
221 			GDBG_WAITQ(("ghd_waitq_shuffle_up: multi gdevp 0x%p\n",
222 				gdevp));
223 			return;
224 		}
225 
226 		/*
227 		 * promote the topmost request from the device queue to
228 		 * the HBA queue.
229 		 */
230 		if ((gcmdp = L2_remove_head(&GDEV_QHEAD(gdevp))) == NULL) {
231 			/* the device is empty so we're done */
232 			GDBG_WAITQ(("ghd_waitq_shuffle_up: MT gdevp 0x%p\n",
233 				gdevp));
234 			return;
235 		}
236 		L2_add(&GHBA_QHEAD(cccp), &gcmdp->cmd_q, gcmdp);
237 		GDEV_NACTIVE(gdevp)++;
238 		gcmdp->cmd_waitq_level++;
239 		GDBG_WAITQ(("ghd_waitq_shuffle_up: gdevp 0x%p gcmdp 0x%p\n",
240 			gdevp, gcmdp));
241 	}
242 }
243 
244 
245 void
246 ghd_waitq_delete(ccc_t *cccp, gcmd_t *gcmdp)
247 {
248 	gtgt_t	*gtgtp = GCMDP2GTGTP(gcmdp);
249 	gdev_t	*gdevp = gtgtp->gt_gdevp;
250 #if defined(GHD_DEBUG) || defined(__lint)
251 	Q_t	*qp = &gdevp->gd_waitq;
252 #endif
253 
254 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
255 	mutex_enter(&cccp->ccc_waitq_mutex);
256 
257 	/*
258 	 * Adjust all queue counters. If this request is being aborted
259 	 * it might only have made it to the target queue. Otherwise,
260 	 * both the target and hba queue have to be adjusted when a
261 	 * request is completed normally. The cmd_waitq_level value
262 	 * indicates which queue counters need to be adjusted. It's
263 	 * incremented as the request progresses up the queues.
264 	 */
265 	switch (gcmdp->cmd_waitq_level) {
266 	case 0:
267 		break;
268 	case 1:
269 		/*
270 		 * If this is an early-timeout, or early-abort, the request
271 		 * is still linked onto a waitq. Remove it now. If it's
272 		 * an active request and no longer on the waitq then calling
273 		 * L2_delete a second time does no harm.
274 		 */
275 		L2_delete(&gcmdp->cmd_q);
276 		break;
277 
278 	case 2:
279 		L2_delete(&gcmdp->cmd_q);
280 #if defined(GHD_DEBUG) || defined(__lint)
281 		if (GDEV_NACTIVE(gdevp) == 0)
282 			debug_enter("\n\nGHD WAITQ DELETE\n\n");
283 #endif
284 		GDEV_NACTIVE(gdevp)--;
285 		break;
286 
287 	case 3:
288 		/* it's an active or completed command */
289 #if defined(GHD_DEBUG) || defined(__lint)
290 		if (GDEV_NACTIVE(gdevp) == 0 || GHBA_NACTIVE(cccp) == 0)
291 			debug_enter("\n\nGHD WAITQ DELETE\n\n");
292 #endif
293 		GDEV_NACTIVE(gdevp)--;
294 		GHBA_NACTIVE(cccp)--;
295 		break;
296 
297 	default:
298 		/* this shouldn't happen */
299 #if defined(GHD_DEBUG) || defined(__lint)
300 		debug_enter("\n\nGHD WAITQ LEVEL > 3\n\n");
301 #endif
302 		break;
303 	}
304 
305 	GDBG_WAITQ(("ghd_waitq_delete: gcmdp 0x%p qp 0x%p level %ld\n",
306 		gcmdp, qp, gcmdp->cmd_waitq_level));
307 
308 
309 	/*
310 	 * There's probably now more room in the HBA queue. Move
311 	 * up as many requests as possible.
312 	 */
313 	ghd_waitq_shuffle_up(cccp, gdevp);
314 
315 	mutex_exit(&cccp->ccc_waitq_mutex);
316 }
317 
318 
319 int
320 ghd_waitq_process_and_mutex_hold(ccc_t *cccp)
321 {
322 	gcmd_t	*gcmdp;
323 	int	 rc = FALSE;
324 
325 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
326 	ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
327 
328 	for (;;) {
329 		if (L2_EMPTY(&GHBA_QHEAD(cccp))) {
330 			/* return if the list is empty */
331 			GDBG_WAITQ(("ghd_waitq_proc: MT cccp 0x%p qp 0x%p\n",
332 				cccp, &cccp->ccc_waitq));
333 			break;
334 		}
335 		if (GHBA_NACTIVE(cccp) >= GHBA_MAXACTIVE(cccp)) {
336 			/* return if the HBA is too active */
337 			GDBG_WAITQ(("ghd_waitq_proc: N>M cccp 0x%p qp 0x%p"
338 				" N %ld max %ld\n", cccp, &cccp->ccc_waitq,
339 					GHBA_NACTIVE(cccp),
340 					GHBA_MAXACTIVE(cccp)));
341 			break;
342 		}
343 
344 		/*
345 		 * bail out if the wait queue has been
346 		 * "held" by the HBA driver
347 		 */
348 		if (cccp->ccc_waitq_held) {
349 			GDBG_WAITQ(("ghd_waitq_proc: held"));
350 			return (rc);
351 		}
352 
353 		if (cccp->ccc_waitq_frozen) {
354 
355 			clock_t lbolt, delay_in_hz, time_to_wait;
356 
357 			delay_in_hz =
358 			    drv_usectohz(cccp->ccc_waitq_freezedelay * 1000);
359 
360 			lbolt = ddi_get_lbolt();
361 			time_to_wait = delay_in_hz -
362 			    (lbolt - cccp->ccc_waitq_freezetime);
363 
364 			if (time_to_wait > 0) {
365 				/*
366 				 * stay frozen; we'll be called again
367 				 * by ghd_timeout_softintr()
368 				 */
369 				GDBG_WAITQ(("ghd_waitq_proc: frozen"));
370 				return (rc);
371 			} else {
372 				/* unfreeze and continue */
373 				GDBG_WAITQ(("ghd_waitq_proc: unfreezing"));
374 				cccp->ccc_waitq_freezetime = 0;
375 				cccp->ccc_waitq_freezedelay = 0;
376 				cccp->ccc_waitq_frozen = 0;
377 			}
378 		}
379 
380 		gcmdp = (gcmd_t *)L2_remove_head(&GHBA_QHEAD(cccp));
381 		GHBA_NACTIVE(cccp)++;
382 		gcmdp->cmd_waitq_level++;
383 		mutex_exit(&cccp->ccc_waitq_mutex);
384 
385 		/*
386 		 * Start up the next I/O request
387 		 */
388 		ASSERT(gcmdp != NULL);
389 		gcmdp->cmd_state = GCMD_STATE_ACTIVE;
390 		if (!(*cccp->ccc_hba_start)(cccp->ccc_hba_handle, gcmdp)) {
391 			/* if the HBA rejected the request, requeue it */
392 			gcmdp->cmd_state = GCMD_STATE_WAITQ;
393 			mutex_enter(&cccp->ccc_waitq_mutex);
394 			GHBA_NACTIVE(cccp)--;
395 			gcmdp->cmd_waitq_level--;
396 			L2_add_head(&GHBA_QHEAD(cccp), &gcmdp->cmd_q, gcmdp);
397 			GDBG_WAITQ(("ghd_waitq_proc: busy cccp 0x%p gcmdp 0x%p"
398 				" handle 0x%p\n", cccp, gcmdp,
399 					cccp->ccc_hba_handle));
400 			break;
401 		}
402 		rc = TRUE;
403 		mutex_enter(&cccp->ccc_waitq_mutex);
404 		GDBG_WAITQ(("ghd_waitq_proc: ++ cccp 0x%p gcmdp 0x%p N %ld\n",
405 			cccp, gcmdp, GHBA_NACTIVE(cccp)));
406 	}
407 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
408 	ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
409 	return (rc);
410 }
411 
412 void
413 ghd_waitq_process_and_mutex_exit(ccc_t *cccp)
414 {
415 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
416 	ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
417 
418 	GDBG_WAITQ(("ghd_waitq_process_and_mutex_exit: cccp 0x%p\n", cccp));
419 
420 	(void) ghd_waitq_process_and_mutex_hold(cccp);
421 
422 	/*
423 	 * Release the mutexes in the opposite order that they
424 	 * were acquired to prevent requests queued by
425 	 * ghd_transport() from getting hung up in the wait queue.
426 	 */
427 	mutex_exit(&cccp->ccc_hba_mutex);
428 	mutex_exit(&cccp->ccc_waitq_mutex);
429 }
430