Home | History | Annotate | Line # | Download | only in libisns
isns_pdu.c revision 1.1
      1 /*	$NetBSD: isns_pdu.c,v 1.1 2011/01/16 01:22:50 agc Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2004,2009 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Wasabi Systems, Inc.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * isns_pdu.c
     34  */
     35 
     36 
     37 #include <sys/cdefs.h>
     38 __RCSID("$NetBSD: isns_pdu.c,v 1.1 2011/01/16 01:22:50 agc Exp $");
     39 
     40 
     41 #include <sys/types.h>
     42 #include <sys/param.h>
     43 
     44 #include <assert.h>
     45 #include <errno.h>
     46 #include <string.h>
     47 
     48 #include "isns.h"
     49 #include "isns_config.h"
     50 
     51 
     52 /*
     53  * Local function prototypes.
     54  */
     55 static struct isns_buffer_list_s *isns_lookup_buffer_list(int);
     56 
     57 static struct isns_pdu_s *isns_init_pdu(struct isns_buffer_s *,
     58     struct isns_config_s *, uint16_t, uint16_t, uint16_t);
     59 static int isns_add_pdu_payload_data(struct isns_trans_s *, const void *, int);
     60 static void isns_get_tlv_info_advance(struct isns_get_tlv_info_s *);
     61 static int isns_get_tlv_uint32(struct isns_get_tlv_info_s *, uint32_t *);
     62 static int isns_get_tlv_data(struct isns_get_tlv_info_s *, int, void **);
     63 
     64 static void isns_add_pdu_list(struct isns_pdu_s **, struct isns_pdu_s *);
     65 static struct isns_buffer_s *isns_get_pdu_head_buffer(struct isns_pdu_s *);
     66 #if 0
     67 static struct isns_buffer_s *isns_get_pdu_tail_buffer(struct isns_pdu_s *);
     68 #endif
     69 static struct isns_buffer_s *isns_get_pdu_active_buffer(struct isns_pdu_s *);
     70 
     71 static uint32_t isns_get_next_trans_id(void);
     72 
     73 /*
     74  * Buffer pool structures and global (static) var.
     75  */
     76 struct isns_buffer_list_s {
     77 	int buf_size;
     78 	int alloc_count;
     79 	struct isns_buffer_s *head;
     80 	struct isns_buffer_list_s *next;
     81 };
     82 
     83 struct isns_buffer_pool_s {
     84 	int active;
     85 	struct isns_buffer_list_s *list_p;
     86 	pthread_mutex_t mutex;
     87 };
     88 
     89 static struct isns_buffer_pool_s G_buffer_pool;
     90 
     91 
     92 /*
     93  * isns_init_buffer_pool - initialize buffer pool for use
     94  */
     95 void
     96 isns_init_buffer_pool()
     97 {
     98 	pthread_mutexattr_t mutexattr;
     99 
    100 	DBG("isns_init_buffer_pool: entered\n");
    101 
    102 	assert(!G_buffer_pool.active);
    103 
    104 	pthread_mutexattr_init(&mutexattr);
    105 	pthread_mutexattr_settype(&mutexattr, ISNS_MUTEX_TYPE_NORMAL);
    106 	pthread_mutex_init(&G_buffer_pool.mutex, &mutexattr);
    107 
    108 	G_buffer_pool.active = 1;
    109 }
    110 
    111 
    112 /*
    113  * isns_lookup_buffer_list - locates the pool buffer list for the buf_size
    114  *			     specified.
    115  *
    116  * Returns: pointer to list in pool containing buf_size buffers
    117  *	    NULL if no list for size indicated exists
    118  */
    119 static struct isns_buffer_list_s *
    120 isns_lookup_buffer_list(int buf_size)
    121 {
    122 	struct isns_buffer_list_s *list_p;
    123 
    124 	/*
    125 	 * WARNING: G_buffer_pool.mutex MUST already be locked.
    126 	 */
    127 
    128 	list_p = G_buffer_pool.list_p;
    129 	while (list_p != NULL) {
    130 		if (list_p->buf_size == buf_size)
    131 			break;
    132 		list_p = list_p->next;
    133 	}
    134 
    135 	return list_p;
    136 }
    137 
    138 
    139 /*
    140  * isns_add_buffer_pool - allocates buffers of in pool
    141  *
    142  * If a list containing buf_size buffers already exists in pool, additional
    143  * buffers are added (allocated) to the list.
    144  */
    145 int
    146 isns_add_buffer_pool(int buf_size, int count)
    147 {
    148 	struct isns_buffer_list_s *list_p, *p, *p_next;
    149 	struct isns_buffer_s *buf_p;
    150 	int n;
    151 
    152 	DBG("isns_add_buffer_pool: buf_size=%d, count=%d\n", buf_size, count);
    153 
    154 	assert(G_buffer_pool.active);
    155 
    156 	/* Make our buffer lengths always a multiple of 4. */
    157 	buf_size = (buf_size + 0x03) & ~0x03;
    158 
    159 	/*
    160 	 * Lookup existing list for size specified.  If no exists, allocate
    161 	 * a new list and initialize.
    162 	 */
    163 	pthread_mutex_lock(&G_buffer_pool.mutex);
    164 	list_p = isns_lookup_buffer_list(buf_size);
    165 	if (list_p == NULL) {
    166 		pthread_mutex_unlock(&G_buffer_pool.mutex);
    167 		list_p = (struct isns_buffer_list_s *)
    168 		    isns_malloc(sizeof(struct isns_buffer_list_s));
    169 		if (list_p == NULL) {
    170 			DBG("isns_add_buffer_pool: error on isns_malloc()\n");
    171 			return ENOMEM;
    172 		}
    173 		list_p->buf_size = buf_size;
    174 		list_p->alloc_count = 0;
    175 		list_p->head = NULL;
    176 	}
    177 
    178 	/* If this is a new list, insert into pool in buf_size order. */
    179 	if (list_p->alloc_count == 0) {
    180 		pthread_mutex_lock(&G_buffer_pool.mutex);
    181 		if (G_buffer_pool.list_p == NULL) {
    182 			G_buffer_pool.list_p = list_p;
    183 			list_p->next = NULL;
    184 		} else if (G_buffer_pool.list_p->buf_size > buf_size) {
    185 			list_p->next = G_buffer_pool.list_p;
    186 			G_buffer_pool.list_p = list_p;
    187 		} else {
    188 			p = G_buffer_pool.list_p;
    189 			while (p->next != NULL) {
    190 				p_next = p->next;
    191 				if (p_next->buf_size > buf_size) {
    192 					p->next = list_p;
    193 					list_p->next = p_next;
    194 					break;
    195 				}
    196 				p = p->next;
    197 			}
    198 			if (p->next == NULL) {
    199 				p->next = list_p;
    200 				list_p->next = NULL;
    201 			}
    202 		}
    203 	}
    204 
    205 	/* Allocate (possibly additional) buffers for list. */
    206 	for (n = 0; n < count; n++) {
    207 		buf_p = (struct isns_buffer_s *)
    208 		    isns_malloc(buf_size + sizeof(struct isns_buffer_s));
    209 		if (buf_p == NULL)
    210 			break;
    211 		buf_p->next = list_p->head;
    212 		list_p->head = buf_p;
    213 	}
    214 	list_p->alloc_count += n;
    215 	pthread_mutex_unlock(&G_buffer_pool.mutex);
    216 
    217 	DBG("isns_init_buffer_pool: %d %d-byte buffers allocated\n",
    218 	    n, buf_size);
    219 
    220 	return (n > 0 ? 0 : ENOMEM);
    221 }
    222 
    223 
    224 /*
    225  * isns_destroy_buffer_pool - destroys previously allocated buffer pool
    226  */
    227 void
    228 isns_destroy_buffer_pool(void)
    229 {
    230 	struct isns_buffer_list_s *list_p;
    231 	struct isns_buffer_s *buf_p;
    232 #ifdef ISNS_DEBUG
    233 	char dbg_buffer[1024] = { 0 };
    234 #endif
    235 
    236 	DBG("isns_destroy_buffer_pool: entered\n");
    237 
    238 	assert(G_buffer_pool.active);
    239 
    240 	pthread_mutex_lock(&G_buffer_pool.mutex);
    241 	while (G_buffer_pool.list_p != NULL) {
    242 		list_p = G_buffer_pool.list_p;
    243 		while (list_p->head != NULL) {
    244 			buf_p = list_p->head;
    245 			list_p->head = buf_p->next;
    246 			list_p->alloc_count--;
    247 			isns_free(buf_p);
    248 		}
    249 #ifdef ISNS_DEBUG
    250 		if (list_p->alloc_count > 0) {
    251 			snprintf(&dbg_buffer[(int) strlen(dbg_buffer)],
    252 			    (sizeof(dbg_buffer) - strlen(dbg_buffer)),
    253 			    "isns_destroy_buffer_pool: "
    254 			    "%d %d-byte buffer(s) not freed\n",
    255 			    list_p->alloc_count, list_p->buf_size);
    256 		}
    257 #endif
    258 		G_buffer_pool.list_p = list_p->next;
    259 		isns_free(list_p);
    260 	}
    261 	G_buffer_pool.active = 0;
    262 
    263 	pthread_mutex_unlock(&G_buffer_pool.mutex);
    264 	pthread_mutex_destroy(&G_buffer_pool.mutex);
    265 
    266 	DBG(dbg_buffer);
    267 }
    268 
    269 
    270 /*
    271  * isns_new_buffer - allocates a new ISNS buffer
    272  *
    273  * Typically, the buffer is returned from the pool, but if no free buffers
    274  * are available in the pool, or a buf size larger than the largest pool buffer
    275  * size is requested, a normal malloc is used to allocate the buffer.  The
    276  * buffer type is recorded so that a subsequent isns_free_buffer will correctly
    277  * free the buffer or return it to the pool.
    278  */
    279 struct isns_buffer_s *
    280 isns_new_buffer(int buf_size)
    281 {
    282 	struct isns_buffer_list_s *list_p;
    283 	struct isns_buffer_s *buf_p;
    284 	int buf_type;
    285 
    286 	if (buf_size == 0)
    287 		buf_size = ISNS_BUF_SIZE;
    288 	buf_p = NULL;
    289 
    290 	pthread_mutex_lock(&G_buffer_pool.mutex);
    291 	list_p = G_buffer_pool.list_p;
    292 	while (list_p != NULL) {
    293 		if ((list_p->head != NULL)
    294 		    && (list_p->buf_size >= buf_size)) {
    295 			buf_p = list_p->head;
    296 			list_p->head = buf_p->next;
    297 			buf_size = list_p->buf_size;
    298 			buf_type = ISNS_BUF_POOL;
    299 			break;
    300 		}
    301 		list_p = list_p->next;
    302 	}
    303 	pthread_mutex_unlock(&G_buffer_pool.mutex);
    304 
    305 	if (buf_p == NULL) {
    306 		buf_p = (struct isns_buffer_s *)isns_malloc(
    307 		    buf_size + sizeof(struct isns_buffer_s));
    308 		buf_type = ISNS_BUF_MALLOC;
    309 	}
    310 
    311 	if (buf_p != NULL)
    312 		ISNS_INIT_BUFFER(buf_p, buf_size, buf_type);
    313 
    314 	DBG("isns_new_buffer: %p (buf_size=%d, type=%d)\n", buf_p, buf_size,
    315 	    buf_type);
    316 
    317 	return buf_p;
    318 }
    319 
    320 
    321 /*
    322  * isns_free_buffer - free a ISNS buffer
    323  */
    324 void
    325 isns_free_buffer(struct isns_buffer_s *buf_p)
    326 {
    327 	struct isns_buffer_list_s *list_p;
    328 
    329 	DBG("isns_free_buffer: %p (type=%d, alloc_len=%d)\n",
    330 	    buf_p, (buf_p == NULL ? 0 : buf_p->buf_type),
    331 	    (buf_p == NULL ? 0 : buf_p->alloc_len));
    332 
    333 	if (buf_p != NULL) {
    334 		switch (buf_p->buf_type) {
    335 		case ISNS_BUF_POOL:
    336 			/* Return buffer to proper pool list. */
    337 			pthread_mutex_lock(&G_buffer_pool.mutex);
    338 			list_p = isns_lookup_buffer_list((int)buf_p->alloc_len);
    339 			if (list_p != NULL) {
    340 				buf_p->next = list_p->head;
    341 				list_p->head = buf_p;
    342 			}
    343 			pthread_mutex_unlock(&G_buffer_pool.mutex);
    344 			break;
    345 		case ISNS_BUF_MALLOC:
    346 			/* Malloc allocated buf, so free normally. */
    347 			isns_free(buf_p);
    348 			break;
    349 		case ISNS_BUF_STATIC:
    350 			/* Static buf with no allocation, so do nothing here. */
    351 			break;
    352 		}
    353 	}
    354 }
    355 
    356 
    357 /*
    358  * isns_new_trans - create a new ISNS transaction
    359  */
    360 ISNS_TRANS
    361 isns_new_trans(ISNS_HANDLE isns_handle, uint16_t func_id, uint16_t pdu_flags)
    362 {
    363 	struct isns_trans_s *trans_p;
    364 	struct isns_pdu_s *pdu_p;
    365 	struct isns_buffer_s *buf_p;
    366 
    367 	if (isns_handle == ISNS_INVALID_HANDLE) {
    368 		DBG("isns_new_trans: error - handle=%p\n", isns_handle);
    369 		return ISNS_INVALID_TRANS;
    370 	}
    371 
    372 	buf_p = isns_new_buffer((int)sizeof(struct isns_trans_s));
    373 	if (buf_p == NULL) {
    374 		DBG("isns_new_trans: error on isns_new_buffer()\n");
    375 		return ISNS_INVALID_TRANS;
    376 	}
    377 
    378 	trans_p = (struct isns_trans_s *)isns_buffer_data(buf_p, 0);
    379 	trans_p->id = isns_get_next_trans_id();
    380 	trans_p->func_id = func_id;
    381 	trans_p->flags = 0;
    382 	trans_p->cfg_p = (struct isns_config_s *)isns_handle;
    383 	trans_p->pdu_req_list = NULL;
    384 	trans_p->pdu_rsp_list = NULL;
    385 	trans_p->disconnect_cnt = 0;
    386 
    387 	trans_p->get_tlv_info.pdu_p = NULL;
    388 	trans_p->get_tlv_info.buf_p = NULL;
    389 	trans_p->get_tlv_info.extra_buf_list = NULL;
    390 	trans_p->get_tlv_info.buf_ofs = 0;
    391 
    392 	buf_p->cur_len = sizeof(struct isns_trans_s);
    393 
    394 	/*
    395 	 * Mask off all but the AUTH and possibly REPLACE_REG pdu flags.  Then,
    396 	 * set the appropriate server/client sender flag.  The first/last PDU
    397 	 * flags will be set when the PDU is sent.
    398 	 */
    399 	if (func_id == isnsp_DevAttrReg)
    400 		pdu_flags &= (ISNS_FLAG_AUTH | ISNS_FLAG_REPLACE_REG);
    401 	else
    402 		pdu_flags &= ISNS_FLAG_AUTH;
    403 
    404 	if (trans_p->cfg_p->is_server)
    405 		pdu_flags |= ISNS_FLAG_SND_SERVER;
    406 	else
    407 		pdu_flags |= ISNS_FLAG_SND_CLIENT;
    408 
    409 	pdu_p = isns_new_pdu(trans_p->cfg_p, trans_p->id, func_id, pdu_flags);
    410 	if (pdu_p == NULL) {
    411 		DBG("isns_new_trans: error on isns_new_pdu()\n");
    412 		isns_free_buffer(buf_p);
    413 		return ISNS_INVALID_TRANS;
    414 	}
    415 
    416 	isns_add_pdu_request((ISNS_TRANS)trans_p, pdu_p);
    417 
    418 	DBG("isns_new_trans: %p\n", trans_p);
    419 
    420 	return (ISNS_TRANS)trans_p;
    421 }
    422 
    423 
    424 /*
    425  * isns_free_trans - free ISNS transaction created with isns_new_trans
    426  */
    427 void
    428 isns_free_trans(ISNS_TRANS trans)
    429 {
    430 	struct isns_trans_s *trans_p;
    431 	struct isns_pdu_s *pdu_p;
    432 	struct isns_buffer_s *buf_p, *free_buf_p;
    433 	uint32_t trans_flags;
    434 
    435 	DBG("isns_free_trans: %p\n", trans);
    436 
    437 	if (trans != ISNS_INVALID_TRANS) {
    438 		trans_p = (struct isns_trans_s *)trans;
    439 
    440 		trans_flags = isns_set_trans_flags(trans_p,
    441 		    ISNS_TRANSF_FREE_WHEN_COMPLETE);
    442 
    443 		if ((trans_flags & ISNS_TRANSF_COMPLETE) == 0) {
    444 			DBG("isns_free_trans: deferred - trans not complete\n");
    445 			return;
    446 		}
    447 
    448 		DBG("isns_free_trans: pdu_req_list=%p\n",
    449 		    trans_p->pdu_req_list);
    450 		while ((pdu_p = trans_p->pdu_req_list) != NULL) {
    451 			trans_p->pdu_req_list = pdu_p->next;
    452 			isns_free_pdu(pdu_p);
    453 		}
    454 		DBG("isns_free_trans: pdu_rsp_list=%p\n",
    455 		    trans_p->pdu_rsp_list);
    456 		while ((pdu_p = trans_p->pdu_rsp_list) != NULL) {
    457 			trans_p->pdu_rsp_list = pdu_p->next;
    458 			isns_free_pdu(pdu_p);
    459 		}
    460 		DBG("isns_free_trans: extra_buf_list=%p\n",
    461 		    trans_p->get_tlv_info.extra_buf_list);
    462 		buf_p = trans_p->get_tlv_info.extra_buf_list;
    463 		while (buf_p != NULL) {
    464 			free_buf_p = buf_p;
    465 			buf_p = buf_p->next;
    466 			isns_free_buffer(free_buf_p);
    467 		}
    468 
    469 		DBG("isns_free_trans: freeing base trans buffer\n");
    470 		buf_p = ((struct isns_buffer_s *)(void *)(trans_p))-1;
    471 		isns_free_buffer(buf_p);
    472 	}
    473 }
    474 
    475 
    476 /*
    477  * isns_send_trans - send ISNS transaction PDU(s) and optionally wait
    478  *
    479  * If a successful wait occurs (i.e., the transaction completes without
    480  * a timeout), then the response PDU status is place in *status_p.  For
    481  * all other cases, the data returned in *status_p is undefined.
    482  *
    483  */
    484 int
    485 isns_send_trans(ISNS_TRANS trans, const struct timespec *timeout_p,
    486     uint32_t *status_p)
    487 {
    488 	struct isns_trans_s *trans_p;
    489 	struct isns_pdu_s *pdu_p;
    490 	int rval;
    491 
    492 	trans_p = (struct isns_trans_s *)trans;
    493 
    494 	DBG("isns_send_trans: trans_p=%p, timeout_p=%p\n", trans_p, timeout_p);
    495 
    496 	if (status_p != NULL)
    497 		*status_p = 0;
    498 
    499 	if (!isns_is_socket_init_done(trans_p->cfg_p)) {
    500 		DBG("isns_send_trans: socket not initialized\n");
    501 		isns_complete_trans(trans_p);
    502 		return EINVAL;
    503 	}
    504 
    505 	if ((pdu_p = isns_get_pdu_request(trans)) == NULL) {
    506 		DBG("isns_send_trans: no request PDU\n");
    507 		return EINVAL;
    508 	}
    509 
    510 	/* Set the FIRST_PDU flag in the first PDU. */
    511 	pdu_p->hdr.flags |= ISNS_FLAG_FIRST_PDU;
    512 
    513 	/* Set our PDU sequence numbers for the PDU chain. */
    514 	while (pdu_p->next != NULL) {
    515 		pdu_p->next->hdr.seq_id = pdu_p->hdr.seq_id + 1;
    516 		pdu_p = pdu_p->next;
    517 	}
    518 
    519 	/* Set the LAST_PDU flag in the last PDU. */
    520 	pdu_p->hdr.flags |= ISNS_FLAG_LAST_PDU;
    521 
    522 	rval = isns_send_pdu(trans, isns_get_pdu_request(trans), timeout_p);
    523 	if ((rval == 0) && (status_p != NULL))
    524 		isns_get_pdu_response_status(trans, status_p);
    525 
    526 	return rval;
    527 }
    528 
    529 
    530 
    531 void
    532 isns_complete_trans(struct isns_trans_s *trans_p)
    533 {
    534 	uint32_t flags;
    535 
    536 	DBG("isns_complete_trans: trans_p=%p\n", trans_p);
    537 
    538 	flags = isns_set_trans_flags(trans_p, ISNS_TRANSF_COMPLETE);
    539 
    540 	if ((flags & ISNS_TRANSF_FREE_WHEN_COMPLETE) != 0)
    541 		isns_free_trans(trans_p);
    542 }
    543 
    544 
    545 int
    546 isns_abort_trans(struct isns_config_s *cfg_p, uint16_t trans_id)
    547 {
    548 	struct isns_task_s *task_p;
    549 
    550 	/* First, look at current task. */
    551 	if (((task_p = cfg_p->curtask_p) != NULL)
    552 	    && (task_p->task_type == ISNS_TASK_SEND_PDU)
    553 	    && (task_p->var.send_pdu.trans_p->id == trans_id)) {
    554 		isns_complete_trans(task_p->var.send_pdu.trans_p);
    555 		isns_end_task(task_p);
    556 		return 0;
    557 	}
    558 
    559 	/* If not current task, look in task queue. */
    560 	task_p = isns_taskq_remove_trans(cfg_p, trans_id);
    561 	if (task_p) {
    562 		isns_complete_trans(task_p->var.send_pdu.trans_p);
    563 		isns_end_task(task_p);
    564 		return 0;
    565 	}
    566 
    567 	return EINVAL;
    568 }
    569 
    570 /*
    571  * isns_add_string - add a TLV which is a C string
    572  *
    573  * Wrapper around isns_add_tlv()
    574  */
    575 int
    576 isns_add_string(ISNS_TRANS trans, uint32_t tag, const char *s)
    577 {
    578 	/* Add string, including required NULL. */
    579 	return isns_add_tlv(trans, tag, (int)strlen(s)+1, s);
    580 }
    581 
    582 
    583 /*
    584  * isns_add_tlv - adds a TLV to an existing transaction
    585  */
    586 int
    587 isns_add_tlv(ISNS_TRANS trans, uint32_t tag, int data_len, const void *data_p)
    588 {
    589 	struct isns_trans_s *trans_p;
    590 	uint8_t tlv_buf[ISNS_TLV_HDR_SIZE];
    591 	int rval;
    592 
    593 	DBG("isns_add_tlv: trans=%p, tag=%d, data_len=%d, data_p=%p\n",
    594 	    trans, tag, data_len, data_p);
    595 
    596 	if (trans == ISNS_INVALID_TRANS) {
    597 		DBG("isns_add_tlv: error - trans=%p\n", trans);
    598 		return EINVAL;
    599 	}
    600 
    601 	if ((data_len > 0) && (data_p == NULL)) {
    602 		DBG("isns_add_tlv: error data_len=%d, data_p=%p\n",
    603 		    data_len, data_p);
    604 		return EINVAL;
    605 	}
    606 
    607 	/* Set tag, length in header buffer and add to PDU payload data. */
    608 	trans_p = (struct isns_trans_s *)trans;
    609 	ISNS_TLV_SET_TAG(tlv_buf, tag);
    610 	ISNS_TLV_SET_LEN(tlv_buf, ISNS_PAD4_LEN(data_len));
    611 	rval = isns_add_pdu_payload_data(trans_p, tlv_buf, ISNS_TLV_HDR_SIZE);
    612 
    613 	/* If header added okay, add value portion to PDU payload data. */
    614 	if ((rval == 0) && (data_len > 0))
    615 		rval = isns_add_pdu_payload_data(trans_p, data_p, data_len);
    616 
    617 	return rval;
    618 }
    619 
    620 
    621 /*
    622  * isns_get_tlv - get TLV value from response PDU for transaction
    623  *
    624  * returns:
    625  *   0 - success
    626  *   ENOENT - no (more) TLVs in this transaction
    627  *   EINVAL - invalid arg
    628  *   EPERM  - operation not permitted - transaction not complete
    629  *   ENOMEM - could not allocate storage for spanning TLV data
    630  */
    631 int
    632 isns_get_tlv(ISNS_TRANS trans, int which_tlv, uint32_t *tag_p, int *data_len_p,
    633     void **data_pp)
    634 {
    635 	struct isns_trans_s *trans_p;
    636 	struct isns_get_tlv_info_s *info_p;
    637 	struct isns_pdu_s *pdu_p;
    638 	int rval;
    639 
    640 	if (trans == ISNS_INVALID_TRANS) {
    641 		DBG("isns_get_tlv: error - trans=%p\n", trans);
    642 		return EINVAL;
    643 	}
    644 
    645 	trans_p = (struct isns_trans_s *)trans;
    646 	if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0) {
    647 		DBG("isns_get_tlv: error - trans not complete\n");
    648 		return EPERM;
    649 	}
    650 
    651 	/* Get response PDU for this transaction. */
    652 	pdu_p = isns_get_pdu_response(trans_p);
    653 	if (pdu_p == NULL) {
    654 		DBG("isns_get_tlv: error - no response PDU in transaction\n");
    655 		return EINVAL;
    656 	}
    657 
    658 	if (pdu_p->payload_p->cur_len == 0) {
    659 		DBG("isns_get_tlv: error - zero length PDU payload\n");
    660 		return EINVAL;
    661 	}
    662 
    663 	/* If get_tlv_info unset, treat ISNS_TLV_NEXT as ISNS_TLV_FIRST. */
    664 	info_p = &trans_p->get_tlv_info;
    665 	if ((which_tlv == ISNS_TLV_NEXT) && (info_p->pdu_p == NULL))
    666 		which_tlv = ISNS_TLV_FIRST;
    667 
    668 	/*!!! make sure PDU uses TLVs here */
    669 
    670 	switch (which_tlv) {
    671 	case ISNS_TLV_NEXT:
    672 		/* For next TLV, nothing to do here - use get_tlv_info as-is. */
    673 		break;
    674 
    675 	case ISNS_TLV_FIRST:
    676 		/* For first TLV, reset get_tlv_info. */
    677 		info_p->pdu_p = pdu_p;
    678 		info_p->buf_p = isns_get_pdu_head_buffer(pdu_p);
    679 		info_p->buf_ofs = 4;
    680 		break;
    681 
    682 	default:
    683 		DBG("isns_get_tlv: invalid arg (which_tlv=%d)\n", which_tlv);
    684 		return EINVAL;
    685 	}
    686 
    687 	/*
    688 	 * Get the type, length, and data (value) for the TLV.  The get calls
    689 	 * below will advance the pointers in get_tlv_info_s *info_p.  Note that
    690 	 * if the get of the TAG type fails, ENOENT is returned indicating that
    691 	 * no more TLVs exist for this request PDU.
    692 	 */
    693 	if ((rval = isns_get_tlv_uint32(info_p, tag_p)) != 0) {
    694 		DBG("isns_get_tlv: error on isns_get_tlv_uint32() tag\n");
    695 		return ENOENT;
    696 	}
    697 
    698 	if ((rval = isns_get_tlv_uint32(info_p, (uint32_t *)data_len_p)) != 0) {
    699 		DBG("isns_get_tlv: error on isns_get_tlv_uint32() data len\n");
    700 		return rval;
    701 	}
    702 
    703 	rval = isns_get_tlv_data(info_p, *data_len_p, data_pp);
    704 	if (rval != 0) {
    705 		DBG("isns_get_tlv: error on isns_get_tlv_data()\n");
    706 		return rval;
    707 	}
    708 
    709 	return 0;
    710 }
    711 
    712 
    713 /*
    714  * isns_set_trans_flags - sets flag bit(s) in transaction flags member
    715  */
    716 uint32_t
    717 isns_set_trans_flags(struct isns_trans_s *trans_p, uint32_t flags)
    718 {
    719 	pthread_mutex_lock(&trans_p->cfg_p->trans_mutex);
    720 	trans_p->flags |= flags;
    721 	flags = trans_p->flags;
    722 	pthread_mutex_unlock(&trans_p->cfg_p->trans_mutex);
    723 
    724 	return flags;
    725 }
    726 
    727 
    728 /*
    729  * isns_add_pdu_request - adds PDU to transaction request PDU list
    730  */
    731 void
    732 isns_add_pdu_request(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p)
    733 {
    734 	DBG("isns_add_pdu_request: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p);
    735 
    736 	isns_add_pdu_list(&trans_p->pdu_req_list, pdu_p);
    737 }
    738 
    739 
    740 /*
    741  * isns_add_pdu_response - adds PDU to transaction response PDU list
    742  */
    743 void
    744 isns_add_pdu_response(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p)
    745 {
    746 	DBG("isns_add_pdu_response: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p);
    747 
    748 	isns_add_pdu_list(&trans_p->pdu_rsp_list, pdu_p);
    749 }
    750 
    751 
    752 /*
    753  * isns_get_pdu_request_tail - returns last PDU in request PDU chain
    754  */
    755 struct isns_pdu_s *
    756 isns_get_pdu_request_tail(struct isns_trans_s *trans_p)
    757 {
    758 	struct isns_pdu_s *pdu_p;
    759 
    760 	if ((pdu_p = isns_get_pdu_request(trans_p)) != NULL) {
    761 		while (pdu_p->next != NULL)
    762 			pdu_p = pdu_p->next;
    763 	}
    764 
    765 	return pdu_p;
    766 }
    767 
    768 
    769 /*
    770  * isns_new_pdu - allocates a new PDU and assigns funtion ID and flags
    771  */
    772 struct isns_pdu_s *
    773 isns_new_pdu(struct isns_config_s *cfg_p, uint16_t trans_id, uint16_t func_id,
    774     uint16_t flags)
    775 {
    776 	struct isns_buffer_s *buf_p;
    777 
    778 	/*
    779 	 * Allocate a buffer at least large enough for our isns_pdu_s struct
    780 	 * and the embedded isns_buffer_s struct for the PDU payload data and 4
    781 	 * bytes of actual payload data.
    782 	 */
    783 	buf_p = isns_new_buffer(/* CONSTCOND */(int)MAX(ISNS_BUF_SIZE,
    784 	    sizeof(struct isns_pdu_s) + sizeof(struct isns_buffer_s) + 4));
    785 	if (buf_p == NULL) {
    786 		DBG("isns_new_pdu: error on isns_new_buffer()\n");
    787 		return NULL;
    788 	}
    789 
    790 	return isns_init_pdu(buf_p, cfg_p, trans_id, func_id, flags);
    791 }
    792 
    793 
    794 /*
    795  * isns_free_pdu - frees a PDU and all associated buffers
    796  */
    797 void
    798 isns_free_pdu(struct isns_pdu_s *pdu_p)
    799 {
    800 	struct isns_buffer_s *buf_p, *free_buf_p;
    801 
    802 	DBG("isns_free_pdu: %p\n", pdu_p);
    803 
    804 	if (pdu_p != NULL) {
    805 		/* Free all payload buffers. */
    806 		buf_p = pdu_p->payload_p;
    807 		while (buf_p != NULL) {
    808 			free_buf_p = buf_p;
    809 			buf_p = buf_p->next;
    810 			isns_free_buffer(free_buf_p);
    811 		}
    812 		/*
    813 		 * Get a pointer to the ISNS buffer in which this PDU is
    814 		 * contained, and then free it.
    815 		 */
    816 		buf_p = ((struct isns_buffer_s *)(void *)(pdu_p))-1 ;
    817 		isns_free_buffer(buf_p);
    818 	}
    819 }
    820 
    821 
    822 /*
    823  * isns_send_pdu - initiates the send PDU task
    824  */
    825 int
    826 isns_send_pdu(ISNS_TRANS trans, struct isns_pdu_s *pdu_p,
    827     const struct timespec *timeout_p)
    828 {
    829 	struct isns_trans_s *trans_p;
    830 	struct isns_task_s* task_p;
    831 	int rval;
    832 
    833 	if (trans == ISNS_INVALID_TRANS) {
    834 		DBG("isns_send_pdu: error - trans=%p\n", trans);
    835 		return EINVAL;
    836 	}
    837 
    838 	if (pdu_p == NULL) {
    839 		DBG("isns_send_pdu: error - pdu_p=%p\n", pdu_p);
    840 		return EINVAL;
    841 	}
    842 
    843 	trans_p = (struct isns_trans_s *)trans;
    844 
    845 	/* Build SEND_PDU task, insert on queue and issue command. */
    846 	task_p = isns_new_task(pdu_p->cfg_p, ISNS_TASK_SEND_PDU,
    847 	    (timeout_p != NULL));
    848 	task_p->var.send_pdu.trans_p = trans_p;
    849 	task_p->var.send_pdu.pdu_p = pdu_p;
    850 
    851 	isns_taskq_insert_tail(pdu_p->cfg_p, task_p);
    852 
    853 	isns_issue_cmd(pdu_p->cfg_p, ISNS_CMD_PROCESS_TASKQ);
    854 
    855 	if (timeout_p == NULL)
    856 		rval = 0;
    857 	else {
    858 		rval = isns_wait_task(task_p, timeout_p);
    859 		if (rval == ETIMEDOUT) {
    860 			DBG("isns_send_pdu: "
    861 			     "timeout on isns_wait_task() trans_id=%d\n",
    862 			     trans_p->id);
    863 
    864 			isns_issue_cmd_with_data(task_p->cfg_p,
    865 			    ISNS_CMD_ABORT_TRANS, (void *)&trans_p->id,
    866 			    (int)sizeof(trans_p->id));
    867 		}
    868 	}
    869 
    870 	return rval;
    871 }
    872 
    873 
    874 /*
    875  * isns_init_pdu - initialize ISNS buffer to be a PDU
    876  */
    877 static struct isns_pdu_s *
    878 isns_init_pdu(struct isns_buffer_s *buf_p, struct isns_config_s *cfg_p,
    879     uint16_t trans_id, uint16_t func_id, uint16_t flags)
    880 {
    881 	struct isns_pdu_s *pdu_p;
    882 
    883 	/* The config and buffer pointers must be valid here. */
    884 	assert(cfg_p != NULL);
    885 	assert(buf_p != NULL);
    886 
    887 	/* The PDU starts at offset 0 for the ISNS buffer data. */
    888 	pdu_p = isns_buffer_data(buf_p, 0);
    889 	buf_p->cur_len = sizeof(struct isns_pdu_s);
    890 
    891 	/* Assign PDU members. */
    892 	pdu_p->cfg_p = cfg_p;
    893 	pdu_p->hdr.isnsp_version = ISNSP_VERSION;
    894 	pdu_p->hdr.func_id = func_id;
    895 	pdu_p->hdr.payload_len = 0;
    896 	pdu_p->hdr.flags = flags;
    897 	pdu_p->hdr.trans_id = trans_id;
    898 	pdu_p->hdr.seq_id = 0;
    899 	pdu_p->byteorder_host = 1;
    900 	pdu_p->next = NULL;
    901 
    902 	/*
    903 	 * The PDU payload buffer starts after the PDU struct portion in the
    904 	 * ISNS buffer passed in to this function.  So, assign the payload_p
    905 	 * pointer accordingly, and then init the buffer with the proper length
    906 	 * and ISNS_BUF_STATIC type.
    907 	 */
    908 	pdu_p->payload_p = (struct isns_buffer_s *)
    909 	    isns_buffer_data(buf_p, buf_p->cur_len);
    910 	ISNS_INIT_BUFFER(pdu_p->payload_p, (unsigned)(buf_p->alloc_len -
    911 	    sizeof(struct isns_pdu_s) - sizeof(struct isns_buffer_s)),
    912 	    ISNS_BUF_STATIC);
    913 
    914 	DBG("isns_init_pdu: %p\n", pdu_p);
    915 
    916 	return pdu_p;
    917 }
    918 
    919 
    920 /*
    921  * isns_add_pdu_payload_data - add data to PDU payload
    922  */
    923 static int
    924 isns_add_pdu_payload_data(struct isns_trans_s *trans_p, const void *data_p,
    925     int len)
    926 {
    927 	struct isns_pdu_s *pdu_p, *new_pdu_p;
    928 	struct isns_buffer_s *buf_p, *new_buf_p;
    929 	const uint8_t *src_p;
    930 	uint8_t *dst_p;
    931 	int pad_bytes;
    932 
    933 	/* Get the request PDU for this transaction. */
    934 	if ((pdu_p = isns_get_pdu_request_tail(trans_p)) == NULL) {
    935 		DBG("isns_add_pdu_payload_data: no request PDU\n");
    936 		return EINVAL;
    937 	}
    938 	/* Get the active buffer for this PDU (where data should be copied). */
    939 	buf_p = isns_get_pdu_active_buffer(pdu_p);
    940 
    941 	/* Set up source and destination pointers.  Calculate pad bytes. */
    942 	src_p = data_p;
    943 	dst_p = isns_buffer_data(buf_p, buf_p->cur_len);
    944 	pad_bytes = ISNS_PAD4_BYTES(len);
    945 
    946 	/*
    947 	 * Move data from source to PDU buffer(s), allocated new pdus and
    948 	 * buffers as necessary to accomodate the data.
    949 	 */
    950 	while (len--) {
    951 		/* If at max for one PDU payload, add a new PDU. */
    952 		if (pdu_p->hdr.payload_len == ISNS_MAX_PDU_PAYLOAD) {
    953 			new_pdu_p = isns_new_pdu(trans_p->cfg_p,
    954 			    trans_p->id, trans_p->func_id, pdu_p->hdr.flags);
    955 			if (new_pdu_p == NULL)
    956 				return ENOMEM;
    957 			isns_add_pdu_request(trans_p, new_pdu_p);
    958 			pdu_p = new_pdu_p;
    959 			buf_p = pdu_p->payload_p;
    960 			dst_p = isns_buffer_data(buf_p, 0);
    961 		}
    962 		/* If at end of current buffer, add a new buffer. */
    963 		if (buf_p->cur_len == buf_p->alloc_len) {
    964 			if (buf_p->next != NULL)
    965 				buf_p = buf_p->next;
    966 			else {
    967 				new_buf_p = isns_new_buffer(0);
    968 				if (new_buf_p == NULL)
    969 					return ENOMEM;
    970 				buf_p->next = new_buf_p;
    971 				buf_p = new_buf_p;
    972 			}
    973 			dst_p = isns_buffer_data(buf_p, 0);
    974 		}
    975 		pdu_p->hdr.payload_len++;
    976 		buf_p->cur_len++;
    977 		*dst_p++ = *src_p++;
    978 	}
    979 
    980 	/*
    981 	 * Since the buffer alloc length is always a multiple of 4, we are
    982 	 * guaranteed to have enough room for the pad bytes.
    983 	 */
    984 	if (pad_bytes > 0) {
    985 		pdu_p->hdr.payload_len += pad_bytes;
    986 		buf_p->cur_len += pad_bytes;
    987 		while (pad_bytes--)
    988 			*dst_p++ = 0;
    989 	}
    990 
    991 	return 0;
    992 }
    993 
    994 
    995 /*
    996  * isns_get_tlv_info_advance - advances pdu/buffer pointers if at end of
    997  *			       current buffer.
    998  */
    999 static void
   1000 isns_get_tlv_info_advance(struct isns_get_tlv_info_s *info_p)
   1001 {
   1002 	if ((info_p->buf_p != NULL) &&
   1003 	    (info_p->buf_ofs == (int)info_p->buf_p->cur_len)) {
   1004 		info_p->buf_p = info_p->buf_p->next;
   1005 		info_p->buf_ofs = 0;
   1006 	}
   1007 
   1008 	if ((info_p->buf_p == NULL) && (info_p->pdu_p->next != NULL)) {
   1009 		info_p->pdu_p = info_p->pdu_p->next;
   1010 		info_p->buf_p = isns_get_pdu_head_buffer(info_p->pdu_p);
   1011 		info_p->buf_ofs = 0;
   1012 	}
   1013 }
   1014 
   1015 
   1016 /*
   1017  * isns_get_tlv_uint32 - retrieve host-ordered uint32_t from PDU buffer at
   1018  *			 starting offset and adjusts isns_get_tlv_info
   1019  *			 pointers accordingly.
   1020  */
   1021 static int
   1022 isns_get_tlv_uint32(struct isns_get_tlv_info_s *info_p, uint32_t *uint32_p)
   1023 {
   1024 	/* Advance to next buffer/pdu (if necessary). */
   1025 	isns_get_tlv_info_advance(info_p);
   1026 
   1027 	if ((info_p->buf_p == NULL) ||
   1028 	    ((info_p->buf_ofs + 4) > (int)info_p->buf_p->cur_len)) {
   1029 		DBG("isns_get_tlv_uint32: end of buffer reached\n");
   1030 		return EFAULT;
   1031 	}
   1032 
   1033 	*uint32_p = ntohl(*(uint32_t *)isns_buffer_data(info_p->buf_p,
   1034 	    info_p->buf_ofs));
   1035 	info_p->buf_ofs += 4;
   1036 
   1037 	return 0;
   1038 }
   1039 
   1040 
   1041 /*
   1042  * isns_get_tlv_data - retrieves data from PDU buffer at starting offset
   1043  *		       for TLV data contained in specified isns_get_tlv_info.
   1044  */
   1045 static int
   1046 isns_get_tlv_data(struct isns_get_tlv_info_s *info_p, int data_len,
   1047     void **data_pp)
   1048 {
   1049 	struct isns_buffer_s *extra_buf_p;
   1050 	struct isns_get_tlv_info_s gti;
   1051 	uint8_t *data_p, *extra_data_p;
   1052 	int bytes_remaining, cbytes;
   1053 
   1054 	/* First, NULL return data pointer. */
   1055 	*data_pp = NULL;
   1056 
   1057 	/* Advance to next buffer/pdu (if necessary). */
   1058 	isns_get_tlv_info_advance(info_p);
   1059 
   1060 	/* Make sure we have a current get tlv info buffer. */
   1061 	if (info_p->buf_p == NULL) {
   1062 		DBG("isns_get_tlv_data: no next buffer\n");
   1063 		return EFAULT;
   1064 	}
   1065 
   1066 	/* Get pointer into buffer where desired TLV resides. */
   1067 	data_p = isns_buffer_data(info_p->buf_p, info_p->buf_ofs);
   1068 
   1069 	/* TLV data completely resides in current buffer. */
   1070 	if ((info_p->buf_ofs + data_len) <= (int)info_p->buf_p->cur_len) {
   1071 		info_p->buf_ofs += data_len;
   1072 		*data_pp = data_p;
   1073 		return 0;
   1074 	}
   1075 
   1076 	/*
   1077 	 * TLV data extends into next buffer so an "extra" buffer is allocated
   1078 	 * that is large enough to hold the entire data value.  The extra buffer
   1079 	 * is added to the transaction's extra buffer list so we can free it
   1080 	 * when the transaction is freed.
   1081 	 */
   1082 
   1083 	if ((extra_buf_p = isns_new_buffer(data_len)) == NULL) {
   1084 		DBG("isns_get_tlv_data: error on isns_new_buffer()\n");
   1085 		return ENOMEM;
   1086 	}
   1087 	if (info_p->extra_buf_list == NULL)
   1088 		info_p->extra_buf_list = extra_buf_p;
   1089 	else {
   1090 		extra_buf_p->next = info_p->extra_buf_list;
   1091 		info_p->extra_buf_list = extra_buf_p;
   1092 	}
   1093 
   1094 	/* Setup to copy data bytes out to extra buffer. */
   1095 	gti = *info_p;
   1096 	extra_data_p = isns_buffer_data(extra_buf_p, 0);
   1097 	bytes_remaining = data_len;
   1098 
   1099 	while (bytes_remaining > 0) {
   1100 		/*
   1101 		 * Advance to next buffer/pdu (if necessary), using local copy
   1102 		 * of the get_tlv_info structure.
   1103 		 */
   1104 		isns_get_tlv_info_advance(&gti);
   1105 		if (gti.buf_p == NULL) {
   1106 			DBG("isns_get_tlv_data: no next buffer\n");
   1107 			return EFAULT;
   1108 		}
   1109 
   1110 		data_p = isns_buffer_data(gti.buf_p, gti.buf_ofs);
   1111 
   1112 		cbytes = MIN(bytes_remaining, ((int)gti.buf_p->cur_len - gti.buf_ofs));
   1113 		bytes_remaining -= cbytes;
   1114 		gti.buf_ofs += cbytes;
   1115 		while (cbytes--)
   1116 			*extra_data_p++ = *data_p++;
   1117 	}
   1118 
   1119 	/* Update isns_get_tlv_info with our local copy. */
   1120 	*info_p = gti;
   1121 
   1122 	/* Assign return data pointer. */
   1123 	*data_pp = isns_buffer_data(extra_buf_p, 0);
   1124 
   1125 	return 0;
   1126 }
   1127 
   1128 
   1129 /*
   1130  * isns_get_pdu_response_status - returns status in PDU response
   1131  *
   1132  * Returns: 0 - success
   1133  *	    EPERM - operation not permitted, trans not complete
   1134  *	    EINVAL - invalid trans PDU response/payload
   1135  */
   1136 int
   1137 isns_get_pdu_response_status(struct isns_trans_s *trans_p, uint32_t *status_p)
   1138 {
   1139 	struct isns_pdu_s *pdu_p;
   1140 
   1141 	if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0)
   1142 		return EPERM;
   1143 
   1144 	pdu_p = isns_get_pdu_response(trans_p);
   1145 	if ((pdu_p == NULL)
   1146 	    || (pdu_p->payload_p == NULL)
   1147 	    || (pdu_p->payload_p->cur_len < 4))
   1148 	    	return EINVAL;
   1149 
   1150 	*status_p = htonl(*(uint32_t *)isns_buffer_data(pdu_p->payload_p, 0));
   1151 
   1152 	return 0;
   1153 }
   1154 
   1155 
   1156 /*
   1157  * isns_add_pdu_list - adds pdu to specified pdu list
   1158  */
   1159 static void
   1160 isns_add_pdu_list(struct isns_pdu_s **list_pp, struct isns_pdu_s *pdu_p)
   1161 {
   1162 	struct isns_pdu_s *p, *p_prev;
   1163 
   1164 
   1165 	if (*list_pp == NULL) {
   1166 		*list_pp = pdu_p;
   1167 		pdu_p->next = NULL;
   1168 		return;
   1169 	}
   1170 
   1171 	p = *list_pp;
   1172 	while (p != NULL) {
   1173 		if (pdu_p->hdr.seq_id < p->hdr.seq_id) {
   1174 			if (p == *list_pp) {
   1175 				*list_pp = pdu_p;
   1176 				pdu_p->next = p;
   1177 			} else {
   1178 				p_prev = *list_pp;
   1179 				while (p_prev->next != p)
   1180 					p_prev = p_prev->next;
   1181 				p_prev->next = pdu_p;
   1182 				pdu_p->next = p;
   1183 			}
   1184 
   1185 			return;
   1186 		}
   1187 		p = p->next;
   1188 	}
   1189 
   1190 	/* pdu_p->hdr.seq_id > hdr.seq_id of all list elements */
   1191 	p = *list_pp;
   1192 	while (p->next != NULL)
   1193 		p = p->next;
   1194 	p->next = pdu_p;
   1195 	pdu_p->next = NULL;
   1196 }
   1197 
   1198 
   1199 /*
   1200  * isns_get_pdu_head_buffer - returns PDU payload head buffer
   1201  */
   1202 static struct isns_buffer_s *
   1203 isns_get_pdu_head_buffer(struct isns_pdu_s *pdu_p)
   1204 {
   1205 	return pdu_p->payload_p;
   1206 }
   1207 
   1208 
   1209 #if 0
   1210 /*
   1211  * isns_get_pdu_tail_buffer - returns PDU payload tail buffer
   1212  */
   1213 static struct isns_buffer_s *
   1214 isns_get_pdu_tail_buffer(struct isns_pdu_s *pdu_p)
   1215 {
   1216 	struct isns_buffer_s *buf_p;
   1217 
   1218 	buf_p = pdu_p->payload_p;
   1219 	if (buf_p != NULL)
   1220 		while (buf_p->next != NULL) buf_p = buf_p->next;
   1221 
   1222 	return buf_p;
   1223 }
   1224 #endif
   1225 
   1226 
   1227 /*
   1228  * isns_get_pdu_active_buffer - returns PDU payload "active buffer where the
   1229  *				next TLV/data should be written
   1230  */
   1231 static struct isns_buffer_s *
   1232 isns_get_pdu_active_buffer(struct isns_pdu_s *pdu_p)
   1233 {
   1234 	struct isns_buffer_s *buf_p;
   1235 
   1236 	buf_p = pdu_p->payload_p;
   1237 	while ((buf_p->next != NULL) && (buf_p->cur_len == buf_p->alloc_len)) {
   1238 		buf_p = buf_p->next;
   1239 	}
   1240 
   1241 	return buf_p;
   1242 }
   1243 
   1244 
   1245 /*
   1246  * isns_get_next_trans_id - returns next ISNS transaction ID to use
   1247  */
   1248 static uint32_t
   1249 isns_get_next_trans_id(void)
   1250 {
   1251 	static int	trans_id = 1;
   1252 
   1253 	return trans_id++;
   1254 }
   1255 
   1256 
   1257 #ifdef ISNS_DEBUG
   1258 /*
   1259  * isns_dump_pdu - dumps PDU contents
   1260  */
   1261 void
   1262 isns_dump_pdu(struct isns_pdu_s *pdu_p)
   1263 {
   1264 	int n, pos;
   1265 	struct isns_buffer_s *buf_p;
   1266 	uint8_t *p;
   1267 	char text[17];
   1268 
   1269 	if (pdu_p == NULL) {
   1270 		DBG("isns_dump_pdu: pdu_p is NULL\n");
   1271 		return;
   1272 	}
   1273 
   1274 	DBG("pdu header:\n");
   1275 	if (pdu_p->byteorder_host) {
   1276 	    DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, "
   1277 	        "seq=%d\n",
   1278 		pdu_p->hdr.isnsp_version,
   1279 		pdu_p->hdr.func_id & ~0x8000,
   1280 		(pdu_p->hdr.func_id & 0x8000 ? "rsp" : "req"),
   1281 		pdu_p->hdr.payload_len,
   1282 		pdu_p->hdr.flags,
   1283 		pdu_p->hdr.trans_id,
   1284 		pdu_p->hdr.seq_id);
   1285 	} else {
   1286 	    DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, "
   1287 	        "seq=%d\n",
   1288 		isns_ntohs(pdu_p->hdr.isnsp_version),
   1289 		isns_ntohs(pdu_p->hdr.func_id) & ~0x8000,
   1290 		(pdu_p->hdr.func_id & 0x0080 ? "rsp" : "req"),
   1291 		isns_ntohs(pdu_p->hdr.payload_len),
   1292 		isns_ntohs(pdu_p->hdr.flags),
   1293 		isns_ntohs(pdu_p->hdr.trans_id),
   1294 		isns_ntohs(pdu_p->hdr.seq_id));
   1295 	}
   1296 
   1297 	DBG("pdu buffers:\n");
   1298 	buf_p = pdu_p->payload_p;
   1299 	while (buf_p != NULL) {
   1300 		DBG("[%p]: alloc_len=%d, cur_len=%d\n",
   1301 		    buf_p, buf_p->alloc_len, buf_p->cur_len);
   1302 		buf_p = buf_p->next;
   1303 	}
   1304 
   1305 	DBG("pdu payload:\n");
   1306 	buf_p = pdu_p->payload_p;
   1307 	if (buf_p == NULL) {
   1308 		DBG("<none>\n");
   1309 		return;
   1310 	}
   1311 
   1312 	pos = 0;
   1313 	memset(text, 0, 17);
   1314 	while (buf_p != NULL) {
   1315 		p = isns_buffer_data(buf_p, 0);
   1316 		for (n = 0; n < buf_p->cur_len; n++) {
   1317 			DBG("%02X ", *p);
   1318 			text[pos] = (isprint(*p) ? *p : '.');
   1319 			pos++;
   1320  			p++;
   1321 
   1322 			if ((pos % 16) == 0) {
   1323 				DBG("   %s\n", text);
   1324 				memset(text, 0, 17);
   1325 				pos = 0;
   1326 			}
   1327 		}
   1328 		buf_p = buf_p->next;
   1329 	}
   1330 
   1331 	if ((pos % 16) != 0)
   1332 		DBG("%*c   %s\n", (16 - (pos % 16)) * 3, ' ', text);
   1333 }
   1334 #endif /* ISNS_DEBUG */
   1335