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 2008 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 <stdio.h> 30 #include <stdlib.h> 31 #include <strings.h> 32 #include <unistd.h> 33 #include <assert.h> 34 #include <syslog.h> 35 #include <synch.h> 36 #include <time.h> 37 #include <umem.h> 38 39 #include "queue.h" 40 #include "iscsi_conn.h" 41 #include "utility.h" 42 #include "target.h" 43 #include "t10.h" 44 45 /* 46 * Constants 47 */ 48 static const timespec_t usec = {0, 1000}; 49 50 FILE *qlog = NULL; 51 int qlog_lvl = 0; 52 53 pthread_mutex_t q_mutex; 54 int queue_num; 55 56 void 57 queue_init() 58 { 59 (void) pthread_mutex_init(&q_mutex, NULL); 60 queue_log(True); 61 } 62 63 target_queue_t * 64 queue_alloc() 65 { 66 target_queue_t *q = 67 (target_queue_t *)calloc(1, sizeof (target_queue_t)); 68 69 if (q == NULL) 70 return (NULL); 71 72 (void) pthread_mutex_lock(&q_mutex); 73 q->q_num = queue_num++; 74 (void) pthread_mutex_unlock(&q_mutex); 75 76 (void) sema_init(&q->q_sema, 0, USYNC_THREAD, NULL); 77 (void) pthread_mutex_init(&q->q_mutex, NULL); 78 79 return (q); 80 } 81 82 void 83 queue_log(Boolean_t on) 84 { 85 (void) pthread_mutex_lock(&q_mutex); 86 if ((on == True) && (qlog == NULL) && (qlog_lvl != 0)) { 87 qlog = fopen(target_log, "ab"); 88 } else if ((on == False) && (qlog != NULL)) { 89 (void) fclose(qlog); 90 qlog = NULL; 91 } 92 (void) pthread_mutex_unlock(&q_mutex); 93 } 94 95 /* 96 * []---- 97 * | queue_message_set -- add a given message to the queue. 98 * []---- 99 */ 100 void 101 queue_message_set(target_queue_t *q, uint32_t lvl, msg_type_t type, 102 void *data) 103 { 104 msg_t *msg; 105 106 if ((msg = umem_cache_alloc(queue_cache, 0)) == NULL) 107 return; 108 109 bzero(msg, sizeof (*msg)); 110 msg->msg_pri_level = lvl; 111 msg->msg_type = type; 112 msg->msg_data = data; 113 114 (void) pthread_mutex_lock(&q->q_mutex); 115 116 if (q->q_head == NULL) { 117 q->q_head = msg; 118 assert(q->q_tail == NULL); 119 q->q_tail = msg; 120 } else if (lvl & Q_HIGH) { 121 msg->msg_next = q->q_head; 122 q->q_head->msg_prev = msg; 123 q->q_head = msg; 124 } else { 125 q->q_tail->msg_next = msg; 126 msg->msg_prev = q->q_tail; 127 q->q_tail = msg; 128 } 129 130 (void) pthread_mutex_unlock(&q->q_mutex); 131 132 (void) sema_post(&q->q_sema); 133 } 134 135 /* 136 * []---- 137 * | queue_message_get -- retrieve the first message in the queue 138 * []---- 139 */ 140 msg_t * 141 queue_message_get(target_queue_t *q) 142 { 143 msg_t *m; 144 145 while (sema_wait(&q->q_sema) == -1) 146 (void) nanosleep(&usec, 0); 147 (void) pthread_mutex_lock(&q->q_mutex); 148 m = q->q_head; 149 if (m == NULL) { 150 assert(q->q_tail == NULL); 151 (void) pthread_mutex_unlock(&q->q_mutex); 152 return (NULL); 153 } 154 q->q_head = m->msg_next; 155 if (q->q_head == NULL) 156 q->q_tail = NULL; 157 (void) pthread_mutex_unlock(&q->q_mutex); 158 159 return (m); 160 } 161 162 /* 163 * []---- 164 * | queue_message_try_get -- see if there's a message available 165 * []---- 166 */ 167 msg_t * 168 queue_message_try_get(target_queue_t *q) 169 { 170 msg_t *m; 171 172 if (sema_trywait(&q->q_sema) != 0) 173 return (NULL); 174 (void) pthread_mutex_lock(&q->q_mutex); 175 m = q->q_head; 176 q->q_head = m->msg_next; 177 if (q->q_head == NULL) 178 q->q_tail = NULL; 179 (void) pthread_mutex_unlock(&q->q_mutex); 180 181 return (m); 182 } 183 184 /* 185 * []---- 186 * | queue_walker_free -- Run through a queue and free certain messages. 187 * | 188 * | Users of the queues should not walk the queue structure themselves 189 * | unless they also need to grab the lock. To prevent that level of 190 * | knowledge of the queue structures this method is provided to enable 191 * | other subsystems to walk the queue looking for messages which need 192 * | to be deleted. 193 * []---- 194 */ 195 void 196 queue_walker_free(target_queue_t *q, Boolean_t (*func)(msg_t *m, void *v), 197 void *v1) 198 { 199 msg_t *m; /* current working message */ 200 msg_t *n; /* next message */ 201 202 (void) pthread_mutex_lock(&q->q_mutex); 203 m = q->q_head; 204 while (m) { 205 if ((*func)(m, v1) == True) { 206 if (m == q->q_head) { 207 q->q_head = m->msg_next; 208 if (m->msg_next == NULL) 209 q->q_tail = NULL; 210 else 211 m->msg_next->msg_prev = NULL; 212 } else { 213 m->msg_prev->msg_next = m->msg_next; 214 if (m->msg_next == NULL) 215 q->q_tail = m->msg_prev; 216 else 217 m->msg_next->msg_prev = m->msg_prev; 218 } 219 n = m->msg_next; 220 queue_message_free(m); 221 m = n; 222 } else { 223 m = m->msg_next; 224 } 225 } 226 (void) pthread_mutex_unlock(&q->q_mutex); 227 } 228 229 /* 230 * []---- 231 * | queue_reset -- Flush a queue of all command messages messages. 232 * []---- 233 */ 234 void 235 queue_reset(target_queue_t *q) 236 { 237 msg_t *m; 238 msg_t *n; 239 240 (void) pthread_mutex_lock(&q->q_mutex); 241 m = q->q_head; 242 while (m != NULL) { 243 244 switch (m->msg_type) { 245 case msg_cmd_data_out: 246 case msg_cmd_send: 247 if (m == q->q_head) { 248 q->q_head = m->msg_next; 249 if (m->msg_next == NULL) 250 q->q_tail = NULL; 251 else 252 m->msg_next->msg_prev = NULL; 253 } else { 254 assert(m->msg_prev != NULL); 255 m->msg_prev->msg_next = m->msg_next; 256 if (m->msg_next == NULL) 257 q->q_tail = m->msg_prev; 258 else 259 m->msg_next->msg_prev = m->msg_prev; 260 } 261 n = m->msg_next; 262 queue_message_free(m); 263 m = n; 264 (void) sema_wait(&q->q_sema); 265 break; 266 267 case msg_reset_lu: 268 case msg_shutdown: 269 case msg_lu_add: 270 case msg_lu_remove: 271 case msg_lu_online: 272 case msg_thick_provo: 273 /* 274 * Don't flush the control messages 275 */ 276 m = m->msg_next; 277 break; 278 279 default: 280 queue_prt(mgmtq, Q_STE_ERRS, 281 "---- Unexpected msg type %d ----", m->msg_type); 282 m = m->msg_next; 283 break; 284 } 285 } 286 287 (void) pthread_mutex_unlock(&q->q_mutex); 288 } 289 290 void 291 queue_message_free(msg_t *m) 292 { 293 umem_cache_free(queue_cache, m); 294 } 295 296 /* 297 * []---- 298 * | queue_free -- free resources used by queue structure 299 * []---- 300 */ 301 void 302 queue_free(target_queue_t *q, void (*free_func)(msg_t *)) 303 { 304 msg_t *m; 305 msg_t *n; 306 307 (void) pthread_mutex_lock(&q->q_mutex); 308 m = q->q_head; 309 while (m != NULL) { 310 if (free_func != NULL) 311 (*free_func)(m); 312 n = m->msg_next; 313 queue_message_free(m); 314 m = n; 315 } 316 (void) pthread_mutex_unlock(&q->q_mutex); 317 318 (void) pthread_mutex_destroy(&q->q_mutex); 319 (void) sema_destroy(&q->q_sema); 320 free(q); 321 } 322 323 void 324 queue_prt(target_queue_t *q, int type, char *fmt, ...) 325 { 326 va_list ap; 327 char buf[80]; 328 329 va_start(ap, fmt); 330 /* LINTED variable format specifier */ 331 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 332 queue_str(q, type, msg_log, buf); 333 va_end(ap); 334 } 335 336 /* 337 * []---- 338 * | queue_str -- helper function which sends a string to the queue 339 * []---- 340 */ 341 void 342 queue_str(target_queue_t *q, uint32_t lvl, msg_type_t type, char *fmt) 343 { 344 int len; 345 char *m; 346 hrtime_t h = gethrtime(); 347 hrtime_t delta; 348 static hrtime_t last_h = 0; 349 time_t tval = time((time_t *)0); 350 char debug[80]; 351 352 (void) pthread_mutex_lock(&q_mutex); 353 if ((qlog) && (qlog_lvl & lvl)) { 354 (void) ctime_r(&tval, debug, sizeof (debug)); 355 (void) fprintf(qlog, "%s %s", debug, fmt); 356 (void) fflush(qlog); 357 } 358 (void) pthread_mutex_unlock(&q_mutex); 359 360 if ((dbg_timestamps == True) && (lvl != 0) && ((lvl & Q_HIGH) == 0)) { 361 len = strlen(fmt) + 12; 362 m = malloc(len); 363 delta = h - last_h; 364 last_h = h; 365 (void) snprintf(m, len, "%9.3f %s", 366 (double)delta / (double)1000000.0, fmt); 367 queue_message_set(q, lvl, type, (void *)m); 368 } else { 369 len = strlen(fmt) + 1; 370 m = malloc(len); 371 (void) strncpy(m, fmt, len); 372 queue_message_set(q, lvl, type, (void *)m); 373 } 374 } 375 376 /* 377 * []------------------------------------------------------------------[] 378 * | Specialized free routines for queue data. | 379 * | It is possible for a shutdown to start because the STE thread | 380 * | receives an error while reading from the socket. If at the same | 381 * | time the connection poll thread is processing a PDU it could place | 382 * | a msg_ste_datain package on the STE queue. When the STE hits the | 383 * | shutdown message first it will exit and we need to clean up | 384 * | anything on that queue which means freeing memory in the | 385 * | appropriate manner. This is just one example and there are several | 386 * | others. Another method to deal with this would be to have a closed | 387 * | flag such that any futher calls to queue_message_set would return | 388 * | an error. This would require any calls to queue_message_set() deal | 389 * | with this condition. The approach used here seems cleaner. | 390 * | The drawback to this approach is that if any new messages are | 391 * | added then the developer had better add it to these routines as | 392 * | appropriate. | 393 * []------------------------------------------------------------------[] 394 */ 395 396 /* 397 * []---- 398 * | sess_queue_data_remove -- free any message data left on the sess queue 399 * | 400 * | XXX This should be recoded so that we're doing the cleanup within 401 * | the session code. Peal off any messages and deal with them there. 402 * []---- 403 */ 404 void 405 sess_queue_data_remove(msg_t *m) 406 { 407 mgmt_request_t *mq; 408 char **buf; 409 410 syslog(LOG_ERR, "sess_queue_data: type %d", m->msg_type); 411 switch (m->msg_type) { 412 default: 413 syslog(LOG_ERR, "Unknown session type data being free'd, %d", 414 m->msg_type); 415 free(m->msg_data); 416 break; 417 418 case msg_shutdown: 419 case msg_shutdown_rsp: 420 case msg_ste_media_error: 421 syslog(LOG_ERR, "Impossible message left in session queue" 422 " of type %d", m->msg_type); 423 break; 424 425 case msg_cmd_data_out: 426 break; 427 428 case msg_initiator_name: 429 case msg_initiator_alias: 430 case msg_target_name: 431 free(((name_request_t *)m->msg_data)->nr_name); 432 break; 433 434 case msg_mgmt_rqst: 435 mq = (mgmt_request_t *)m->msg_data; 436 (void) pthread_mutex_lock(&mq->m_resp_mutex); 437 tgt_buf_add(mq->m_u.m_resp, "queue_freed", NULL); 438 (void) pthread_mutex_unlock(&mq->m_resp_mutex); 439 queue_message_set(mq->m_q, 0, msg_mgmt_rply, 0); 440 break; 441 442 case msg_mgmt_rply: 443 mq = (mgmt_request_t *)m->msg_data; 444 buf = mq->m_u.m_resp; 445 tgt_buf_add_tag(buf, XML_ELEMENT_STATS, Tag_End); 446 tgt_buf_add_tag(buf, XML_ELEMENT_CONN, Tag_End); 447 448 (void) pthread_mutex_unlock(&mq->m_resp_mutex); 449 queue_message_set(mq->m_q, 0, msg_mgmt_rply, 0); 450 break; 451 452 case msg_reset_targ: 453 case msg_reset_lu: 454 /* ---- these are safe to ignore, no data to free ---- */ 455 break; 456 } 457 } 458 459 /* 460 * []---- 461 * | conn_queue_data_remove -- free any message data left on the conn queue 462 * []---- 463 */ 464 void 465 conn_queue_data_remove(msg_t *m) 466 { 467 mgmt_request_t *mq; 468 469 syslog(LOG_ERR, "conn_queue_data: type %d", m->msg_type); 470 switch (m->msg_type) { 471 case msg_cmd_data_rqst: 472 case msg_cmd_data_out: 473 case msg_cmd_cmplt: 474 syslog(LOG_ERR, "Free'ing data which should already be gone"); 475 free(m->msg_data); 476 break; 477 478 case msg_mgmt_rqst: 479 mq = (mgmt_request_t *)m->msg_data; 480 (void) pthread_mutex_lock(&mq->m_resp_mutex); 481 if (mq->m_u.m_resp != NULL) 482 tgt_buf_add(mq->m_u.m_resp, "queue_freed", NULL); 483 (void) pthread_mutex_unlock(&mq->m_resp_mutex); 484 queue_message_set(mq->m_q, 0, msg_mgmt_rply, 0); 485 break; 486 487 default: 488 syslog(LOG_ERR, "Unknown connection message being free'd: %d", 489 m->msg_type); 490 free(m->msg_data); 491 break; 492 } 493 } 494