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 2008 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_completed_cmd - used to add a command in completion queue 226 */ 227 void 228 iscsi_enqueue_completed_cmd(iscsi_sess_t *isp, iscsi_cmd_t *icmdp) 229 { 230 ASSERT(isp != NULL); 231 ASSERT(icmdp != NULL); 232 233 mutex_enter(&isp->sess_queue_completion.mutex); 234 if (icmdp->cmd_state != ISCSI_CMD_STATE_COMPLETED) { 235 icmdp->cmd_state = ISCSI_CMD_STATE_COMPLETED; 236 } else { 237 /* 238 * This command has already been completed, probably 239 * through the abort code path. It should be in 240 * the process of being returned to to the upper 241 * layers, so do nothing. 242 */ 243 mutex_exit(&isp->sess_queue_completion.mutex); 244 return; 245 } 246 iscsi_enqueue_cmd_tail(&isp->sess_queue_completion.head, 247 &isp->sess_queue_completion.tail, icmdp); 248 ++isp->sess_queue_completion.count; 249 mutex_exit(&isp->sess_queue_completion.mutex); 250 251 iscsi_thread_send_wakeup(isp->sess_ic_thread); 252 } 253 254 /* 255 * iscsi_move_queue - used to move the whole contents of a queue 256 * 257 * The source queue has to be initialized. Its mutex is entered before 258 * doing the actual move. The destination queue should be initialized. 259 * This function is intended to move a queue located in a shared location 260 * into local space. No mutex is needed for the destination queue. 261 */ 262 void 263 iscsi_move_queue( 264 iscsi_queue_t *src_queue, 265 iscsi_queue_t *dst_queue 266 ) 267 { 268 ASSERT(src_queue != NULL); 269 ASSERT(dst_queue != NULL); 270 mutex_enter(&src_queue->mutex); 271 dst_queue->count = src_queue->count; 272 dst_queue->head = src_queue->head; 273 dst_queue->tail = src_queue->tail; 274 src_queue->count = 0; 275 src_queue->head = NULL; 276 src_queue->tail = NULL; 277 mutex_exit(&src_queue->mutex); 278 } 279 280 /* 281 * +--------------------------------------------------------------------+ 282 * | private functions | 283 * +--------------------------------------------------------------------+ 284 */ 285 286 /* 287 * iscsi_dequeue_cmd - used to remove a command from a queue 288 */ 289 iscsi_status_t 290 iscsi_dequeue_cmd(iscsi_cmd_t **head, iscsi_cmd_t **tail, iscsi_cmd_t *icmdp) 291 { 292 #ifdef DEBUG 293 iscsi_cmd_t *tp = NULL; 294 #endif 295 296 ASSERT(head != NULL); 297 ASSERT(tail != NULL); 298 ASSERT(icmdp != NULL); 299 300 if (*head == NULL) { 301 /* empty queue, error */ 302 return (ISCSI_STATUS_INTERNAL_ERROR); 303 } else if (*head == *tail) { 304 /* one element queue */ 305 if (*head == icmdp) { 306 *head = NULL; 307 *tail = NULL; 308 } else { 309 return (ISCSI_STATUS_INTERNAL_ERROR); 310 } 311 } else { 312 /* multi-element queue */ 313 if (*head == icmdp) { 314 /* at the head */ 315 *head = icmdp->cmd_next; 316 (*head)->cmd_prev = NULL; 317 } else if (*tail == icmdp) { 318 *tail = icmdp->cmd_prev; 319 (*tail)->cmd_next = NULL; 320 } else { 321 #ifdef DEBUG 322 /* in the middle? */ 323 for (tp = (*head)->cmd_next; (tp != NULL) && 324 (tp != icmdp); tp = tp->cmd_next) 325 ; 326 if (tp == NULL) { 327 /* not found */ 328 return (ISCSI_STATUS_INTERNAL_ERROR); 329 } 330 #endif 331 if (icmdp->cmd_prev == NULL) { 332 return (ISCSI_STATUS_INTERNAL_ERROR); 333 } 334 icmdp->cmd_prev->cmd_next = icmdp->cmd_next; 335 if (icmdp->cmd_next == NULL) { 336 return (ISCSI_STATUS_INTERNAL_ERROR); 337 } 338 icmdp->cmd_next->cmd_prev = icmdp->cmd_prev; 339 } 340 } 341 342 /* icmdp no longer in the queue */ 343 icmdp->cmd_prev = NULL; 344 icmdp->cmd_next = NULL; 345 return (ISCSI_STATUS_SUCCESS); 346 } 347 348 /* 349 * iscsi_enqueue_cmd_head - used to add a command to the head of a queue 350 */ 351 void 352 iscsi_enqueue_cmd_head(iscsi_cmd_t **head, iscsi_cmd_t **tail, 353 iscsi_cmd_t *icmdp) 354 { 355 ASSERT(icmdp != NULL); 356 ASSERT(icmdp->cmd_next == NULL); 357 ASSERT(icmdp->cmd_prev == NULL); 358 ASSERT(icmdp != *head); 359 ASSERT(icmdp != *tail); 360 361 if (*head == NULL) { 362 /* empty queue */ 363 *head = *tail = icmdp; 364 icmdp->cmd_prev = NULL; 365 icmdp->cmd_next = NULL; 366 } else { 367 /* non-empty queue */ 368 icmdp->cmd_next = *head; 369 icmdp->cmd_prev = NULL; 370 (*head)->cmd_prev = icmdp; 371 *head = icmdp; 372 } 373 } 374 375 /* 376 * iscsi_enqueue_cmd_tail - used to add a command to the tail of a queue 377 */ 378 static void 379 iscsi_enqueue_cmd_tail(iscsi_cmd_t **head, iscsi_cmd_t **tail, 380 iscsi_cmd_t *icmdp) 381 { 382 ASSERT(icmdp != NULL); 383 ASSERT(icmdp->cmd_next == NULL); 384 ASSERT(icmdp->cmd_prev == NULL); 385 ASSERT(icmdp != *head); 386 ASSERT(icmdp != *tail); 387 388 if (*head == NULL) { 389 /* empty queue */ 390 *head = *tail = icmdp; 391 icmdp->cmd_prev = NULL; 392 icmdp->cmd_next = NULL; 393 } else { 394 /* non-empty queue */ 395 icmdp->cmd_next = NULL; 396 icmdp->cmd_prev = *tail; 397 (*tail)->cmd_next = icmdp; 398 *tail = icmdp; 399 } 400 } 401