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
iscsi_init_queue(iscsi_queue_t * queue)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
iscsi_destroy_queue(iscsi_queue_t * queue)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
iscsi_enqueue_pending_cmd(iscsi_sess_t * isp,iscsi_cmd_t * icmdp)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
iscsi_dequeue_pending_cmd(iscsi_sess_t * isp,iscsi_cmd_t * icmdp)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
iscsi_enqueue_active_cmd(iscsi_conn_t * icp,iscsi_cmd_t * icmdp)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
iscsi_dequeue_active_cmd(iscsi_conn_t * icp,iscsi_cmd_t * icmdp)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
iscsi_enqueue_idm_aborting_cmd(iscsi_conn_t * icp,iscsi_cmd_t * icmdp)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
iscsi_dequeue_idm_aborting_cmd(iscsi_conn_t * icp,iscsi_cmd_t * icmdp)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
iscsi_enqueue_completed_cmd(iscsi_sess_t * isp,iscsi_cmd_t * icmdp)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
iscsi_move_queue(iscsi_queue_t * src_queue,iscsi_queue_t * dst_queue)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
iscsi_dequeue_cmd(iscsi_cmd_t ** head,iscsi_cmd_t ** tail,iscsi_cmd_t * icmdp)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
iscsi_enqueue_cmd_head(iscsi_cmd_t ** head,iscsi_cmd_t ** tail,iscsi_cmd_t * icmdp)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
iscsi_enqueue_cmd_tail(iscsi_cmd_t ** head,iscsi_cmd_t ** tail,iscsi_cmd_t * icmdp)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