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