xref: /titanic_51/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_queue.c (revision 49311b3511690f5b23558b0fba067bc8067c7a87)
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  * Copyright 2000 by Cisco Systems, Inc.  All rights reserved.
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * iSCSI Software Initiator
27  */
28 
29 #include "iscsi.h"		/* main header */
30 
31 static void iscsi_enqueue_cmd_tail(iscsi_cmd_t **head, iscsi_cmd_t **tail,
32     iscsi_cmd_t *icmdp);
33 
34 
35 /*
36  * +--------------------------------------------------------------------+
37  * | public queue functions						|
38  * +--------------------------------------------------------------------+
39  *
40  * Public queue locking rules.  When acquiring multiple queue locks
41  * they MUST always be acquired in a forward order.  If a lock is
42  * aquire in a reverese order it could lead to a deadlock panic.
43  * The forward order of locking is described as shown below.
44  *
45  *		 pending -> cmdsn -> active -> completion
46  *
47  * If a cmd_mutex is held, it is either held after the pending queue
48  * mutex or after the active queue mutex.
49  */
50 
51 /*
52  * iscsi_init_queue - used to initialize iscsi queue
53  */
54 void
55 iscsi_init_queue(iscsi_queue_t *queue)
56 {
57 	ASSERT(queue != NULL);
58 
59 	queue->head = NULL;
60 	queue->tail = NULL;
61 	queue->count = 0;
62 	mutex_init(&queue->mutex, NULL, MUTEX_DRIVER, NULL);
63 }
64 
65 /*
66  * iscsi_destroy_queue - used to terminate iscsi queue
67  */
68 void
69 iscsi_destroy_queue(iscsi_queue_t *queue)
70 {
71 	ASSERT(queue != NULL);
72 	ASSERT(queue->count == 0);
73 
74 	mutex_destroy(&queue->mutex);
75 }
76 
77 /*
78  * iscsi_enqueue_pending_cmd - used to add a command in a pending queue
79  */
80 void
81 iscsi_enqueue_pending_cmd(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
82 {
83 	ASSERT(isp != NULL);
84 	ASSERT(icmdp != NULL);
85 	ASSERT(mutex_owned(&isp->sess_queue_pending.mutex));
86 
87 	icmdp->cmd_state = ISCSI_CMD_STATE_PENDING;
88 	if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
89 		iscsi_enqueue_cmd_tail(&isp->sess_queue_pending.head,
90 		    &isp->sess_queue_pending.tail, icmdp);
91 		isp->sess_queue_pending.count++;
92 		KSTAT_WAITQ_ENTER(isp);
93 	} else {
94 		iscsi_enqueue_cmd_head(&isp->sess_queue_pending.head,
95 		    &isp->sess_queue_pending.tail, icmdp);
96 		isp->sess_queue_pending.count++;
97 		KSTAT_WAITQ_ENTER(isp);
98 	}
99 	iscsi_sess_redrive_io(isp);
100 }
101 
102 
103 /*
104  * iscsi_dequeue_pending_cmd - used to remove a command from a pending queue
105  */
106 void
107 iscsi_dequeue_pending_cmd(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
108 {
109 	iscsi_status_t rval = ISCSI_STATUS_SUCCESS;
110 
111 	ASSERT(isp != NULL);
112 	ASSERT(icmdp != NULL);
113 	ASSERT(mutex_owned(&isp->sess_queue_pending.mutex));
114 
115 	rval = iscsi_dequeue_cmd(&isp->sess_queue_pending.head,
116 	    &isp->sess_queue_pending.tail, icmdp);
117 	if (ISCSI_SUCCESS(rval)) {
118 		isp->sess_queue_pending.count--;
119 		if (((kstat_io_t *)(&isp->stats.ks_io_data))->wcnt) {
120 			KSTAT_WAITQ_EXIT(isp);
121 		} else {
122 			cmn_err(CE_WARN,
123 			    "kstat wcnt == 0 when exiting waitq,"
124 			    " please check\n");
125 		}
126 	} else {
127 		ASSERT(FALSE);
128 	}
129 }
130 
131 /*
132  * iscsi_enqueue_active_cmd - used to add a command in a active queue
133  *
134  * This interface attempts to keep newer items are on the tail,
135  * older items are on the head.  But, Do not assume that the list
136  * is completely sorted.  If someone attempts to enqueue an item
137  * that already has cmd_lbolt_active assigned and is older than
138  * the current head, otherwise add to the tail.
139  */
140 void
141 iscsi_enqueue_active_cmd(iscsi_conn_t *icp, iscsi_cmd_t *icmdp)
142 {
143 	iscsi_sess_t		*isp    = NULL;
144 
145 	ASSERT(icp != NULL);
146 	ASSERT(icmdp != NULL);
147 	isp = icp->conn_sess;
148 	ASSERT(isp != NULL);
149 
150 	/*
151 	 * When receiving data associated to a command it
152 	 * is temporarily removed from the active queue.
153 	 * Then once the data receive is completed it may
154 	 * be returned to the active queue.  If this was
155 	 * an aborting command we need to preserve its
156 	 * state.
157 	 */
158 	if (icmdp->cmd_state != ISCSI_CMD_STATE_ABORTING) {
159 		icmdp->cmd_state = ISCSI_CMD_STATE_ACTIVE;
160 	}
161 
162 	/*
163 	 * It's possible that this is not a newly issued icmdp - we may
164 	 * have tried to abort it but the abort failed or was rejected
165 	 * and we are putting it back on the active list. So if it is older
166 	 * than the head of the active queue, put it at the head to keep
167 	 * the CommandTimeout valid.
168 	 */
169 	if (icmdp->cmd_lbolt_active == 0) {
170 		icmdp->cmd_lbolt_active = ddi_get_lbolt();
171 		iscsi_enqueue_cmd_tail(&icp->conn_queue_active.head,
172 		    &icp->conn_queue_active.tail, icmdp);
173 	} else if ((icp->conn_queue_active.head != NULL) &&
174 	    (icmdp->cmd_lbolt_active <
175 	    icp->conn_queue_active.head->cmd_lbolt_active)) {
176 		iscsi_enqueue_cmd_head(&icp->conn_queue_active.head,
177 		    &icp->conn_queue_active.tail, icmdp);
178 	} else {
179 		iscsi_enqueue_cmd_tail(&icp->conn_queue_active.head,
180 		    &icp->conn_queue_active.tail, icmdp);
181 	}
182 	icp->conn_queue_active.count++;
183 
184 	if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
185 		KSTAT_RUNQ_ENTER(isp);
186 	}
187 }
188 
189 /*
190  * iscsi_dequeue_active_cmd - used to remove a command from a active queue
191  */
192 void
193 iscsi_dequeue_active_cmd(iscsi_conn_t *icp, iscsi_cmd_t *icmdp)
194 {
195 	iscsi_status_t	rval	= ISCSI_STATUS_SUCCESS;
196 	iscsi_sess_t	*isp	= NULL;
197 
198 	ASSERT(icp != NULL);
199 	ASSERT(icmdp != NULL);
200 	isp = icp->conn_sess;
201 	ASSERT(isp != NULL);
202 	ASSERT(mutex_owned(&icp->conn_queue_active.mutex));
203 
204 	rval = iscsi_dequeue_cmd(&icp->conn_queue_active.head,
205 	    &icp->conn_queue_active.tail, icmdp);
206 
207 	if (ISCSI_SUCCESS(rval)) {
208 		icp->conn_queue_active.count--;
209 
210 		if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
211 			if (((kstat_io_t *)(&isp->stats.ks_io_data))->rcnt) {
212 				KSTAT_RUNQ_EXIT(isp);
213 			} else {
214 				cmn_err(CE_WARN,
215 				    "kstat rcnt == 0 when exiting runq,"
216 				    " please check\n");
217 			}
218 		}
219 	} else {
220 		ASSERT(FALSE);
221 	}
222 }
223 
224 /*
225  * iscsi_enqueue_idm_aborting_cmd - used to add a command to the queue
226  * representing command waiting for a callback from IDM for aborting
227  *
228  * Not sorted
229  */
230 void
231 iscsi_enqueue_idm_aborting_cmd(iscsi_conn_t *icp, iscsi_cmd_t *icmdp)
232 {
233 	iscsi_sess_t		*isp    = NULL;
234 
235 	ASSERT(icp != NULL);
236 	ASSERT(icmdp != NULL);
237 	isp = icp->conn_sess;
238 	ASSERT(isp != NULL);
239 	ASSERT(icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI);
240 	ASSERT(mutex_owned(&icp->conn_queue_idm_aborting.mutex));
241 
242 	icmdp->cmd_state = ISCSI_CMD_STATE_IDM_ABORTING;
243 	icmdp->cmd_lbolt_idm_aborting = ddi_get_lbolt();
244 	iscsi_enqueue_cmd_tail(&icp->conn_queue_idm_aborting.head,
245 	    &icp->conn_queue_idm_aborting.tail, icmdp);
246 	icp->conn_queue_idm_aborting.count++;
247 }
248 
249 /*
250  * iscsi_dequeue_idm_aborting_cmd - used to remove a command from the queue
251  * representing commands waiting for a callback from IDM for aborting.
252  */
253 void
254 iscsi_dequeue_idm_aborting_cmd(iscsi_conn_t *icp, iscsi_cmd_t *icmdp)
255 {
256 	iscsi_sess_t	*isp	= NULL;
257 
258 	ASSERT(icp != NULL);
259 	ASSERT(icmdp != NULL);
260 	isp = icp->conn_sess;
261 	ASSERT(isp != NULL);
262 	ASSERT(mutex_owned(&icp->conn_queue_idm_aborting.mutex));
263 
264 	(void) iscsi_dequeue_cmd(&icp->conn_queue_idm_aborting.head,
265 	    &icp->conn_queue_idm_aborting.tail, icmdp);
266 	icp->conn_queue_idm_aborting.count--;
267 }
268 
269 /*
270  * iscsi_enqueue_completed_cmd - used to add a command in completion queue
271  */
272 void
273 iscsi_enqueue_completed_cmd(iscsi_sess_t *isp, iscsi_cmd_t *icmdp)
274 {
275 	ASSERT(isp != NULL);
276 	ASSERT(icmdp != NULL);
277 
278 	mutex_enter(&isp->sess_queue_completion.mutex);
279 	if (icmdp->cmd_state != ISCSI_CMD_STATE_COMPLETED) {
280 		icmdp->cmd_state = ISCSI_CMD_STATE_COMPLETED;
281 	} else {
282 		/*
283 		 * This command has already been completed, probably
284 		 * through the abort code path. It should  be in
285 		 * the process of being returned to to the upper
286 		 * layers, so do nothing.
287 		 */
288 		mutex_exit(&isp->sess_queue_completion.mutex);
289 		return;
290 	}
291 	iscsi_enqueue_cmd_tail(&isp->sess_queue_completion.head,
292 	    &isp->sess_queue_completion.tail, icmdp);
293 	++isp->sess_queue_completion.count;
294 	mutex_exit(&isp->sess_queue_completion.mutex);
295 
296 	(void) iscsi_thread_send_wakeup(isp->sess_ic_thread);
297 }
298 
299 /*
300  * iscsi_move_queue - used to move the whole contents of a queue
301  *
302  *   The source queue has to be initialized.  Its mutex is entered before
303  * doing the actual move.  The destination queue should be initialized.
304  * This function is intended to move a queue located in a shared location
305  * into local space.  No mutex is needed for the destination queue.
306  */
307 void
308 iscsi_move_queue(
309 	iscsi_queue_t	*src_queue,
310 	iscsi_queue_t	*dst_queue
311 )
312 {
313 	ASSERT(src_queue != NULL);
314 	ASSERT(dst_queue != NULL);
315 	mutex_enter(&src_queue->mutex);
316 	dst_queue->count = src_queue->count;
317 	dst_queue->head  = src_queue->head;
318 	dst_queue->tail  = src_queue->tail;
319 	src_queue->count = 0;
320 	src_queue->head  = NULL;
321 	src_queue->tail  = NULL;
322 	mutex_exit(&src_queue->mutex);
323 }
324 
325 /*
326  * +--------------------------------------------------------------------+
327  * | private functions							|
328  * +--------------------------------------------------------------------+
329  */
330 
331 /*
332  * iscsi_dequeue_cmd - used to remove a command from a queue
333  */
334 iscsi_status_t
335 iscsi_dequeue_cmd(iscsi_cmd_t **head, iscsi_cmd_t **tail, iscsi_cmd_t *icmdp)
336 {
337 #ifdef DEBUG
338 	iscsi_cmd_t	*tp	= NULL;
339 #endif
340 
341 	ASSERT(head != NULL);
342 	ASSERT(tail != NULL);
343 	ASSERT(icmdp != NULL);
344 
345 	if (*head == NULL) {
346 		/* empty queue, error */
347 		return (ISCSI_STATUS_INTERNAL_ERROR);
348 	} else if (*head == *tail) {
349 		/* one element queue */
350 		if (*head == icmdp) {
351 			*head = NULL;
352 			*tail = NULL;
353 		} else {
354 			return (ISCSI_STATUS_INTERNAL_ERROR);
355 		}
356 	} else {
357 		/* multi-element queue */
358 		if (*head == icmdp) {
359 			/* at the head */
360 			*head = icmdp->cmd_next;
361 			(*head)->cmd_prev = NULL;
362 		} else if (*tail == icmdp) {
363 			*tail = icmdp->cmd_prev;
364 			(*tail)->cmd_next = NULL;
365 		} else {
366 #ifdef DEBUG
367 			/* in the middle? */
368 			for (tp = (*head)->cmd_next; (tp != NULL) &&
369 			    (tp != icmdp); tp = tp->cmd_next)
370 				;
371 			if (tp == NULL) {
372 				/* not found */
373 				return (ISCSI_STATUS_INTERNAL_ERROR);
374 			}
375 #endif
376 			if (icmdp->cmd_prev == NULL) {
377 				return (ISCSI_STATUS_INTERNAL_ERROR);
378 			}
379 			icmdp->cmd_prev->cmd_next = icmdp->cmd_next;
380 			if (icmdp->cmd_next == NULL) {
381 				return (ISCSI_STATUS_INTERNAL_ERROR);
382 			}
383 			icmdp->cmd_next->cmd_prev = icmdp->cmd_prev;
384 		}
385 	}
386 
387 	/* icmdp no longer in the queue */
388 	icmdp->cmd_prev = NULL;
389 	icmdp->cmd_next = NULL;
390 	return (ISCSI_STATUS_SUCCESS);
391 }
392 
393 /*
394  * iscsi_enqueue_cmd_head - used to add a command to the head of a queue
395  */
396 void
397 iscsi_enqueue_cmd_head(iscsi_cmd_t **head, iscsi_cmd_t **tail,
398     iscsi_cmd_t *icmdp)
399 {
400 	ASSERT(icmdp != NULL);
401 	ASSERT(icmdp->cmd_next == NULL);
402 	ASSERT(icmdp->cmd_prev == NULL);
403 	ASSERT(icmdp != *head);
404 	ASSERT(icmdp != *tail);
405 
406 	if (*head == NULL) {
407 		/* empty queue */
408 		*head = *tail = icmdp;
409 		icmdp->cmd_prev = NULL;
410 		icmdp->cmd_next = NULL;
411 	} else {
412 		/* non-empty queue */
413 		icmdp->cmd_next = *head;
414 		icmdp->cmd_prev = NULL;
415 		(*head)->cmd_prev = icmdp;
416 		*head = icmdp;
417 	}
418 }
419 
420 /*
421  * iscsi_enqueue_cmd_tail - used to add a command to the tail of a queue
422  */
423 static void
424 iscsi_enqueue_cmd_tail(iscsi_cmd_t **head, iscsi_cmd_t **tail,
425     iscsi_cmd_t *icmdp)
426 {
427 	ASSERT(icmdp != NULL);
428 	ASSERT(icmdp->cmd_next == NULL);
429 	ASSERT(icmdp->cmd_prev == NULL);
430 	ASSERT(icmdp != *head);
431 	ASSERT(icmdp != *tail);
432 
433 	if (*head == NULL) {
434 		/* empty queue */
435 		*head = *tail = icmdp;
436 		icmdp->cmd_prev = NULL;
437 		icmdp->cmd_next = NULL;
438 	} else {
439 		/* non-empty queue */
440 		icmdp->cmd_next = NULL;
441 		icmdp->cmd_prev = *tail;
442 		(*tail)->cmd_next = icmdp;
443 		*tail = icmdp;
444 	}
445 }
446