/* * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. * By downloading, copying, installing or using the software you agree * to this license. If you do not agree to this license, do not * download, install, copy or use the software. * * Intel License Agreement * * Copyright (c) 2000, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * -Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * -Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * -The name of Intel Corporation may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_PTHREAD_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_POLL_H #include #endif #include #include #ifdef HAVE_STRING_H #include #endif #include #include "compat.h" #define EXTERN #include "iscsiutil.h" /* * Memory Allocation */ void * iscsi_malloc_atomic(unsigned n) { void *ptr; ptr = malloc(n); iscsi_trace(TRACE_MEM, "iscsi_malloc_atomic(%u) = %p\n", n, ptr); return ptr; } void * iscsi_malloc(unsigned n) { void *ptr; ptr = malloc(n); iscsi_trace(TRACE_MEM, "iscsi_malloc(%u) = %p\n", n, ptr); return ptr; } void iscsi_free_atomic(void *ptr) { iscsi_trace(TRACE_MEM, "iscsi_free_atomic(%p)\n", ptr); (void) free(ptr); } void iscsi_free(void *ptr) { iscsi_trace(TRACE_MEM, "iscsi_free(%p)\n", ptr); (void) free(ptr); } /* debugging levels */ void set_debug(const char *level) { if (strcmp(level, "net") == 0) { iscsi_debug_level |= TRACE_NET_ALL; } else if (strcmp(level, "iscsi") == 0) { iscsi_debug_level |= TRACE_ISCSI_ALL; } else if (strcmp(level, "scsi") == 0) { iscsi_debug_level |= TRACE_SCSI_ALL; } else if (strcmp(level, "osd") == 0) { iscsi_debug_level |= TRACE_OSD; } else if (strcmp(level, "all") == 0) { iscsi_debug_level |= TRACE_ALL; } } /* * Threading Routines */ int iscsi_thread_create(iscsi_thread_t * thread, void *(*proc) (void *), void *arg) { if (pthread_create(&thread->pthread, NULL, proc, arg) != 0) { iscsi_err(__FILE__, __LINE__, "pthread_create() failed\n"); return -1; } if (pthread_detach(thread->pthread) != 0) { iscsi_err(__FILE__, __LINE__, "pthread_detach() failed\n"); return -1; } return 0; } /* * Queuing Functions */ int iscsi_queue_init(iscsi_queue_t * q, int depth) { q->head = q->tail = q->count = 0; q->depth = depth; q->elem = iscsi_malloc_atomic((unsigned)(depth * sizeof(void *))); if (q->elem == NULL) { iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); return -1; } iscsi_spin_init(&q->lock); return 0; } void iscsi_queue_destroy(iscsi_queue_t * q) { iscsi_free_atomic(q->elem); } int iscsi_queue_full(iscsi_queue_t * q) { return (q->count == q->depth); } int iscsi_queue_depth(iscsi_queue_t * q) { return q->count; } int iscsi_queue_insert(iscsi_queue_t * q, void *ptr) { uint32_t flags; iscsi_spin_lock_irqsave(&q->lock, &flags); if (iscsi_queue_full(q)) { iscsi_err(__FILE__, __LINE__, "QUEUE FULL\n"); iscsi_spin_unlock_irqrestore(&q->lock, &flags); return -1; } q->elem[q->tail] = ptr; q->tail++; if (q->tail == q->depth) { q->tail = 0; } q->count++; iscsi_spin_unlock_irqrestore(&q->lock, &flags); return 0; } void * iscsi_queue_remove(iscsi_queue_t *q) { uint32_t flags = 0; void *ptr; iscsi_spin_lock_irqsave(&q->lock, &flags); if (!iscsi_queue_depth(q)) { iscsi_trace(TRACE_QUEUE, "QUEUE EMPTY\n"); iscsi_spin_unlock_irqrestore(&q->lock, &flags); return NULL; } q->count--; ptr = q->elem[q->head]; q->head++; if (q->head == q->depth) { q->head = 0; } iscsi_spin_unlock_irqrestore(&q->lock, &flags); return ptr; } void iscsi_trace(const int trace, const char *fmt, ...) { #ifdef CONFIG_ISCSI_DEBUG va_list vp; char buf[8192]; if (iscsi_debug_level & trace) { va_start(vp, fmt); (void) vsnprintf(buf, sizeof(buf), fmt, vp); printf("pid %d: %s", (int) getpid(), buf); va_end(vp); } #endif } void iscsi_warn(const char *f, const int line, const char *fmt, ...) { #ifdef CONFIG_ISCSI_DEBUG va_list vp; char buf[8192]; if (iscsi_debug_level & TRACE_WARN) { va_start(vp, fmt); (void) vsnprintf(buf, sizeof(buf), fmt, vp); printf("pid %d:%s:%d: ***WARNING*** %s", (int) getpid(), f, line, buf); va_end(vp); } #endif } void iscsi_err(const char *f, const int line, const char *fmt, ...) { #ifdef CONFIG_ISCSI_DEBUG va_list vp; char buf[8192]; va_start(vp, fmt); (void) vsnprintf(buf, sizeof(buf), fmt, vp); va_end(vp); printf("pid %d:%s:%d: ***ERROR*** %s", (int) getpid(), f, line, buf); # ifdef HAVE_SYSLOG syslog(LOG_ERR, "pid %d:%s:%d: ***ERROR*** %s", getpid(), f, line, buf); # endif /* HAVE_SYSLOG */ #endif } void iscsi_print_buffer(const char *buf, const size_t len) { #ifdef CONFIG_ISCSI_DEBUG size_t i; if (iscsi_debug_level & TRACE_NET_BUFF) { for (i=0 ; i < len; i++) { if (i % 4 == 0) { if (i) { printf("\n"); } printf("%4zu:", i); } printf("%2x ", (uint8_t) (buf)[i]); } if ((len + 1) % 32) { printf("\n"); } } #endif } /* * Hashing Functions */ #include "initiator.h" int hash_init(hash_t * h, int n) { int i; iscsi_spin_init(&h->lock); h->n = n; h->insertions = 0; h->collisions = 0; h->bucket = iscsi_malloc_atomic(n * sizeof(initiator_cmd_t *)); if (h->bucket == NULL) { iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); return -1; } for (i = 0; i < n; i++) h->bucket[i] = NULL; return 0; } int hash_insert(hash_t * h, initiator_cmd_t * cmd, unsigned key) { int i; iscsi_spin_lock(&h->lock); cmd->hash_next = NULL; cmd->key = key; i = key % (h->n); if (h->bucket[i] == NULL) { iscsi_trace(TRACE_HASH, "inserting key %u (val 0x%p) into bucket[%d]\n", key, cmd, i); h->bucket[i] = cmd; } else { cmd->hash_next = h->bucket[i]; h->bucket[i] = cmd; h->collisions++; iscsi_trace(TRACE_HASH, "inserting key %u (val 0x%p) into bucket[%d] " "(collision)\n", key, cmd, i); } h->insertions++; iscsi_spin_unlock(&h->lock); return 0; } struct initiator_cmd_t * hash_remove(hash_t * h, unsigned key) { initiator_cmd_t *prev; initiator_cmd_t *curr; int i; iscsi_spin_lock(&h->lock); i = key % (h->n); if (h->bucket[i] == NULL) { iscsi_err(__FILE__, __LINE__, "bucket emtpy\n"); curr = NULL; } else { prev = NULL; curr = h->bucket[i]; while ((curr->key != key) && (curr->hash_next != NULL)) { prev = curr; curr = curr->hash_next; } if (curr->key != key) { iscsi_err(__FILE__, __LINE__, "key %u (%#x) not found in bucket[%d]\n", key, key, i); curr = NULL; } else { if (prev == NULL) { h->bucket[i] = h->bucket[i]->hash_next; iscsi_trace(TRACE_HASH, "removed key %u (val 0x%p) from head " "of bucket\n", key, curr); } else { prev->hash_next = curr->hash_next; if (prev->hash_next == NULL) { iscsi_trace(TRACE_HASH, "removed key %u (val 0x%p) " "from end of bucket\n", key, curr); } else { iscsi_trace(TRACE_HASH, "removed key %u (val 0x%p) " "from middle of bucket\n", key, curr); } } } } iscsi_spin_unlock(&h->lock); return curr; } int hash_destroy(hash_t * h) { iscsi_free_atomic(h->bucket); return 0; } /* * Socket Functions */ int modify_iov(struct iovec ** iov_ptr, int *iovc, uint32_t offset, uint32_t length) { size_t len; int disp = offset; int i; struct iovec *iov = *iov_ptr; char *basep; /* Given , find beginning iovec and modify its base * and length */ len = 0; for (i = 0; i < *iovc; i++) { len += iov[i].iov_len; if (len > offset) { iscsi_trace(TRACE_NET_IOV, "found offset %u in iov[%d]\n", offset, i); break; } disp -= iov[i].iov_len; } if (i == *iovc) { iscsi_err(__FILE__, __LINE__, "sum of iov lens (%zu) < offset (%u)\n", len, offset); return -1; } iov[i].iov_len -= disp; basep = iov[i].iov_base; basep += disp; iov[i].iov_base = basep; *iovc -= i; *iov_ptr = &(iov[i]); iov = *iov_ptr; /* * Given , find ending iovec and modify its length (base does * not change) */ len = 0; /* we should re-use len and i here... */ for (i = 0; i < *iovc; i++) { len += iov[i].iov_len; if (len >= length) { iscsi_trace(TRACE_NET_IOV, "length %u ends in iovec[%d]\n", length, i); break; } } if (i == *iovc) { iscsi_err(__FILE__, __LINE__, "sum of iovec lens (%zu) < length (%u)\n", len, length); for (i = 0; i < *iovc; i++) { iscsi_err(__FILE__, __LINE__, "iov[%d].iov_base = %p (len %u)\n", i, iov[i].iov_base, (unsigned)iov[i].iov_len); } return -1; } iov[i].iov_len -= (len - length); *iovc = i + 1; #ifdef CONFIG_ISCSI_DEBUG iscsi_trace(TRACE_NET_IOV, "new iov:\n"); len = 0; for (i = 0; i < *iovc; i++) { iscsi_trace(TRACE_NET_IOV, "iov[%d].iov_base = %p (len %u)\n", i, iov[i].iov_base, (unsigned)iov[i].iov_len); len += iov[i].iov_len; } iscsi_trace(TRACE_NET_IOV, "new iov length: %zu bytes\n", len); #endif return 0; } int iscsi_sock_setsockopt(int * sock, int level, int optname, void *optval, unsigned optlen) { int rc; if ((rc = setsockopt(*sock, level, optname, optval, optlen)) != 0) { iscsi_err(__FILE__, __LINE__, "sock->ops->setsockopt() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_getsockopt(int * sock, int level, int optname, void *optval, unsigned *optlen) { int rc; if ((rc = getsockopt(*sock, level, optname, optval, optlen)) != 0) { iscsi_err(__FILE__, __LINE__, "sock->ops->getsockopt() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_create(int * sock) { int rc; if ((*sock = rc = socket(AF_INET, SOCK_STREAM, 0)) < 0) { iscsi_err(__FILE__, __LINE__, "socket() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_bind(int sock, int port) { struct sockaddr_in laddr; int rc; (void) memset(&laddr, 0x0, sizeof(laddr)); laddr.sin_family = AF_INET; laddr.sin_addr.s_addr = INADDR_ANY; laddr.sin_port = ISCSI_HTONS(port); rc = bind(sock, (struct sockaddr *) (void *) &laddr, sizeof(laddr)); if (rc < 0) { iscsi_err(__FILE__, __LINE__, "bind() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_listen(int sock) { int rc; if ((rc = listen(sock, 32)) < 0) { iscsi_err(__FILE__, __LINE__, "listen() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } #ifndef ISCSI_MAXSOCK #define ISCSI_MAXSOCK 8 #endif int iscsi_socks_establish(int *sockv, int *famv, int *sockc, char *family, int port) { struct addrinfo hints; struct addrinfo *res; struct addrinfo *res0; const char *cause = NULL; char portnum[31]; int one = 1; int error; (void) memset(&hints, 0x0, sizeof(hints)); hints.ai_family = (strcmp(family, "unspec") == 0) ? PF_UNSPEC : (strcmp(family, "4") == 0) ? AF_INET : AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; #ifdef AI_NUMERICSERV hints.ai_flags |= AI_NUMERICSERV; #endif (void) snprintf(portnum, sizeof(portnum), "%d", port); if ((error = getaddrinfo(NULL, portnum, &hints, &res0)) != 0) { hints.ai_flags = AI_PASSIVE; if ((error = getaddrinfo(NULL, "iscsi-target", &hints, &res0)) != 0 || (error = getaddrinfo(NULL, "iscsi", &hints, &res0)) != 0) { iscsi_err(__FILE__, __LINE__, "getaddrinfo: %s", gai_strerror(error)); return 0; } } *sockc = 0; for (res = res0; res && *sockc < ISCSI_MAXSOCK; res = res->ai_next) { sockv[*sockc] = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockv[*sockc] < 0) { cause = "socket"; continue; } famv[*sockc] = res->ai_family; if (!iscsi_sock_setsockopt(&sockv[*sockc], SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_setsockopt() failed\n"); continue; } if (!iscsi_sock_setsockopt(&sockv[*sockc], SOL_TCP, TCP_NODELAY, &one, sizeof(one))) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_setsockopt() failed\n"); continue; } if (bind(sockv[*sockc], res->ai_addr, res->ai_addrlen) < 0) { cause = "bind"; close(sockv[*sockc]); continue; } (void) listen(sockv[*sockc], 32); *sockc += 1; } if (*sockc == 0) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_establish: no sockets found: %s", cause); freeaddrinfo(res0); return 0; } freeaddrinfo(res0); return 1; } /* return the address family for the socket */ const char * iscsi_address_family(int fam) { switch(fam) { case 4: return "IPv4"; case 6: return "IPv6"; default: return "[unknown type]"; } } /* wait for a connection to come in on a socket */ /* ARGSUSED2 */ int iscsi_waitfor_connection(int *sockv, int sockc, const char *cf, int *sock) { #ifdef HAVE_POLL struct pollfd socks[ISCSI_MAXSOCK]; int i; for (;;) { for (i = 0 ; i < sockc ; i++) { socks[i].fd = sockv[i]; socks[i].events = POLLIN; socks[i].revents = 0; } switch(poll(socks, (unsigned)sockc, INFTIM)) { case -1: /* interrupted system call */ continue; case 0: /* timeout */ continue; default: for (i = 0 ; i < sockc ; i++) { if (socks[i].revents & POLLIN) { iscsi_trace(TRACE_NET_DEBUG, "connection %d selected\n", sockv[i]); *sock = sockv[i]; return i; } } } } #else fd_set infds; int i; for (;;) { FD_ZERO(&infds); for (i = 0 ; i < sockc ; i++) { FD_SET(sockv[i], &infds); } iscsi_trace(TRACE_NET_DEBUG, "waiting for connection\n"); switch (select(32, &infds, NULL, NULL, NULL)) { case -1: /* interrupted system call */ continue; case 0: /* timeout */ continue; default: for (i = 0 ; i < sockc ; i++) { if (FD_ISSET(sockv[i], &infds)) { iscsi_trace(TRACE_NET_DEBUG, "connection %d selected\n", sockv[i]); *sock = sockv[i]; return i; } } } } #endif } int iscsi_sock_accept(int sock, int *conn) { struct sockaddr_in peer; socklen_t peerlen; peerlen = sizeof(peer); (void) memset(&peer, 0, sizeof(peer)); *conn = accept(sock, (struct sockaddr *)(void *)&peer, &peerlen); if (*conn < 0) { iscsi_trace(TRACE_NET_DEBUG, "accept() failed: rc %d errno %d\n", *conn, errno); return 0; } return 1; } int iscsi_sock_getsockname(int sock, struct sockaddr * name, unsigned *namelen) { if (getsockname(sock, name, namelen) != 0) { iscsi_err(__FILE__, __LINE__, "getsockame() failed (errno %d)\n", errno); return 0; } return 1; } int iscsi_sock_getpeername(int sock, struct sockaddr * name, unsigned *namelen) { if (getpeername(sock, name, namelen) != 0) { iscsi_err(__FILE__, __LINE__, "getpeername() failed (errno %d)\n", errno); return 0; } return 1; } int iscsi_sock_shutdown(int sock, int how) { int rc; if ((rc = shutdown(sock, how)) != 0) { iscsi_trace(TRACE_NET_DEBUG, "shutdown() failed: rc %d, errno %d\n", rc, errno); } return 0; } int iscsi_sock_close(int sock) { int rc; if ((rc = close(sock)) != 0) { iscsi_err(__FILE__, __LINE__, "close() failed: rc %d errno %d\n", rc, errno); return -1; } return 0; } int iscsi_sock_connect(int sock, char *hostname, int port) { struct addrinfo hints; struct addrinfo *res; char portstr[32]; int rc = 0; int i; (void) memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; (void) snprintf(portstr, sizeof(portstr), "%d", port); for (i = 0; i < ISCSI_SOCK_CONNECT_TIMEOUT; i++) { /* Attempt connection */ #ifdef AI_NUMERICSERV hints.ai_flags = AI_NUMERICSERV; #endif if ((rc = getaddrinfo(hostname, portstr, &hints, &res)) != 0) { hints.ai_flags = 0; if ((rc = getaddrinfo(hostname, "iscsi-target", &hints, &res)) != 0 || (rc = getaddrinfo(hostname, "iscsi", &hints, &res)) != 0) { iscsi_err(__FILE__, __LINE__, "getaddrinfo: %s", gai_strerror(rc)); return 0; } } #if ISCSI_SOCK_CONNECT_NONBLOCK == 1 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) { iscsi_err(__FILE__, __LINE__, "fcntl O_NONBLOCK failed"); freeaddrinfo(res); return -1; } #endif rc = connect(sock, res->ai_addr, res->ai_addrlen); #if ISCSI_SOCK_CONNECT_NONBLOCK == 1 if (fcntl(sock, F_SETFL, O_SYNC) != 0) { iscsi_err(__FILE__, __LINE__, "fcntl O_SYNC failed\n"); freeaddrinfo(res); return -1; } #endif /* Check errno */ if (errno == EISCONN) { rc = 0; break; } if (errno == EAGAIN || errno == EINPROGRESS || errno == EALREADY) { if (i != ISCSI_SOCK_CONNECT_TIMEOUT - 1) { printf("***SLEEPING***\n"); sleep(1); } } else { break; } } freeaddrinfo(res); if (rc < 0) { iscsi_err(__FILE__, __LINE__, "connect() to %s:%d failed (errno %d)\n", hostname, port, errno); } return rc; } /* * NOTE: iscsi_sock_msg() alters *sg when socket sends and recvs * return having only transfered a portion of the iovec. When this * happens, the iovec is modified and resent with the appropriate * offsets. */ int iscsi_sock_msg(int sock, int xmit, unsigned len, void *data, int iovc) { struct iovec singleton; struct iovec *iov; struct iovec *iov_padding = NULL; unsigned n = 0; uint32_t remainder; uint32_t padding_len = 0; uint8_t padding[ISCSI_SOCK_MSG_BYTE_ALIGN]; size_t total_len = 0; int rc; int i; iscsi_trace(TRACE_NET_DEBUG, "%s %d bytes on sock\n", xmit ? "sending" : "receiving", len); if (iovc == 0) { iscsi_trace(TRACE_NET_DEBUG, "building singleton iovec (data %p, len %u)\n", data, len); singleton.iov_base = data; singleton.iov_len = len; iov = &singleton; iovc = 1; } else { iov = (struct iovec *) data; } /* Add padding */ if ((remainder = len % ISCSI_SOCK_MSG_BYTE_ALIGN) != 0) { iov_padding = iscsi_malloc_atomic((iovc + 1) * sizeof(struct iovec)); if (iov_padding == NULL) { iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); return -1; } memcpy(iov_padding, iov, iovc * sizeof(struct iovec)); iov_padding[iovc].iov_base = padding; padding_len = ISCSI_SOCK_MSG_BYTE_ALIGN - remainder; iov_padding[iovc].iov_len = padding_len; iov = iov_padding; iovc++; memset(padding, 0, padding_len); len += padding_len; iscsi_trace(TRACE_NET_DEBUG, "Added iovec for padding (len %u)\n", padding_len); } /* * We make copy of iovec if we're in debugging mode, as we'll * print out the iovec and the buffer contents at the end of * this subroutine and */ do { /* Check iovec */ total_len = 0; iscsi_trace(TRACE_NET_DEBUG, "%s %d buffers\n", xmit ? "gathering from" : "scattering into", iovc); for (i = 0; i < iovc; i++) { iscsi_trace(TRACE_NET_IOV, "iov[%d].iov_base = %p, len %u\n", i, iov[i].iov_base, (unsigned)iov[i].iov_len); total_len += iov[i].iov_len; } if (total_len != len - n) { iscsi_err(__FILE__, __LINE__, "iovcs sum to %zu != total len of %u\n", total_len, len - n); iscsi_err(__FILE__, __LINE__, "iov = %p\n", iov); for (i = 0; i < iovc; i++) { iscsi_err(__FILE__, __LINE__, "iov[%d].iov_base = %p, len %u\n", i, iov[i].iov_base, (unsigned)iov[i].iov_len); } return -1; } if ((rc = (xmit) ? writev(sock, iov, iovc) : readv(sock, iov, iovc)) == 0) { iscsi_trace(TRACE_NET_DEBUG, "%s() failed: rc %d errno %d\n", (xmit) ? "writev" : "readv", rc, errno); break; } else if (rc < 0) { /* Temp FIXME */ iscsi_err(__FILE__, __LINE__, "%s() failed: rc %d errno %d\n", (xmit) ? "writev" : "readv", rc, errno); break; } n += rc; if (n < len) { iscsi_trace(TRACE_NET_DEBUG, "Got partial %s: %d bytes of %u\n", (xmit) ? "send" : "recv", rc, len - n + rc); total_len = 0; for (i = 0; i < iovc; i++) { total_len += iov[i].iov_len; } iscsi_trace(TRACE_NET_IOV, "before modify_iov: %s %d buffers, " "total_len = %zu, n = %u, rc = %u\n", xmit ? "gathering from" : "scattering into", iovc, total_len, n, rc); if (modify_iov(&iov, &iovc, (unsigned) rc, len - n) != 0) { iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n"); break; } total_len = 0; for (i = 0; i < iovc; i++) { total_len += iov[i].iov_len; } iscsi_trace(TRACE_NET_IOV, "after modify_iov: %s %d buffers, " "total_len = %zu, n = %u, rc = %u\n\n", xmit ? "gathering from" : "scattering into", iovc, total_len, n, rc); } } while (n < len); if (remainder) { iscsi_free_atomic(iov_padding); } iscsi_trace(TRACE_NET_DEBUG, "successfully %s %u bytes on sock (%u bytes padding)\n", xmit ? "sent" : "received", n, padding_len); return n - padding_len; } /* * Temporary Hack: * * TCP's Nagle algorithm and delayed-ack lead to poor performance when we send * two small messages back to back (i.e., header+data). The TCP_NODELAY option * is supposed to turn off Nagle, but it doesn't seem to work on Linux 2.4. * Because of this, if our data payload is small, we'll combine the header and * data, else send as two separate messages. */ int iscsi_sock_send_header_and_data(int sock, void *header, unsigned header_len, const void *data, unsigned data_len, int iovc) { struct iovec iov[ISCSI_MAX_IOVECS]; if (data_len && data_len <= ISCSI_SOCK_HACK_CROSSOVER) { /* combine header and data into one iovec */ if (iovc >= ISCSI_MAX_IOVECS) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); return -1; } if (iovc == 0) { iov[0].iov_base = header; iov[0].iov_len = header_len; iov[1].iov_base = __UNCONST((const char *)data); iov[1].iov_len = data_len; iovc = 2; } else { iov[0].iov_base = header; iov[0].iov_len = header_len; (void) memcpy(&iov[1], data, sizeof(struct iovec) * iovc); iovc += 1; } if ((unsigned)iscsi_sock_msg(sock, Transmit, header_len + data_len, iov, iovc) != header_len + data_len) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); return -1; } } else { if ((unsigned)iscsi_sock_msg(sock, Transmit, header_len, header, 0) != header_len) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); return -1; } if (data_len != 0 && (unsigned)iscsi_sock_msg(sock, Transmit, data_len, __UNCONST((const char *) data), iovc) != data_len) { iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); return -1; } } return header_len + data_len; } /* spin lock functions */ int iscsi_spin_init(iscsi_spin_t * lock) { pthread_mutexattr_t mattr; pthread_mutexattr_init(&mattr); #ifdef PTHREAD_MUTEX_ADAPTIVE_NP pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP); #endif if (pthread_mutex_init(lock, &mattr) != 0) return -1; return 0; } int iscsi_spin_lock(iscsi_spin_t * lock) { return pthread_mutex_lock(lock); } int iscsi_spin_unlock(iscsi_spin_t * lock) { return pthread_mutex_unlock(lock); } /* ARGSUSED1 */ int iscsi_spin_lock_irqsave(iscsi_spin_t * lock, uint32_t *flags) { return pthread_mutex_lock(lock); } /* ARGSUSED1 */ int iscsi_spin_unlock_irqrestore(iscsi_spin_t * lock, uint32_t *flags) { return pthread_mutex_unlock(lock); } int iscsi_spin_destroy(iscsi_spin_t * lock) { return pthread_mutex_destroy(lock); } /* * Mutex Functions, kernel module doesn't require mutex for locking. * For thread sync, 'down' & 'up' have been wrapped into condition * varibles, which is kernel semaphores in kernel module. */ int iscsi_mutex_init(iscsi_mutex_t * m) { return (pthread_mutex_init(m, NULL) != 0) ? -1 : 0; } int iscsi_mutex_lock(iscsi_mutex_t * m) { return pthread_mutex_lock(m); } int iscsi_mutex_unlock(iscsi_mutex_t * m) { return pthread_mutex_unlock(m); } int iscsi_mutex_destroy(iscsi_mutex_t * m) { return pthread_mutex_destroy(m); } /* * Condition Functions */ int iscsi_cond_init(iscsi_cond_t * c) { return pthread_cond_init(c, NULL); } int iscsi_cond_wait(iscsi_cond_t * c, iscsi_mutex_t * m) { return pthread_cond_wait(c, m); } int iscsi_cond_signal(iscsi_cond_t * c) { return pthread_cond_signal(c); } int iscsi_cond_destroy(iscsi_cond_t * c) { return pthread_cond_destroy(c); } /* * Misc. Functions */ uint32_t iscsi_atoi(char *value) { if (value == NULL) { iscsi_err(__FILE__, __LINE__, "iscsi_atoi() called with NULL value\n"); return 0; } return atoi(value); } static const char HexString[] = "0123456789abcdef"; /* get the hex value (subscript) of the character */ static int HexStringIndex(const char *s, int c) { const char *cp; return (c == '0') ? 0 : ((cp = strchr(s, tolower(c))) == NULL) ? -1 : (int)(cp - s); } int HexDataToText(uint8_t *data, uint32_t dataLength, char *text, uint32_t textLength) { uint32_t n; if (!text || textLength == 0) { return -1; } if (!data || dataLength == 0) { *text = 0x0; return -1; } if (textLength < 3) { *text = 0x0; return -1; } *text++ = '0'; *text++ = 'x'; textLength -= 2; while (dataLength > 0) { if (textLength < 3) { *text = 0x0; return -1; } n = *data++; dataLength--; *text++ = HexString[(n >> 4) & 0xf]; *text++ = HexString[n & 0xf]; textLength -= 2; } *text = 0x0; return 0; } int HexTextToData(const char *text, uint32_t textLength, uint8_t *data, uint32_t dataLength) { int i; uint32_t n1; uint32_t n2; uint32_t len = 0; if ((text[0] == '0') && (text[1] == 'x' || text[1] == 'X')) { /* skip prefix */ text += 2; textLength -= 2; } if ((textLength % 2) == 1) { i = HexStringIndex(HexString, *text++); if (i < 0) return -1; /* error, bad character */ n2 = i; if (dataLength < 1) { return -1; /* error, too much data */ } *data++ = n2; len++; } while (*text != 0x0) { if ((i = HexStringIndex(HexString, *text++)) < 0) { /* error, bad character */ return -1; } n1 = i; if (*text == 0x0) { /* error, odd string length */ return -1; } if ((i = HexStringIndex(HexString, *text++)) < 0) { /* error, bad character */ return -1; } n2 = i; if (len >= dataLength) { /* error, too much data */ return len; } *data++ = (n1 << 4) | n2; len++; } return (len == 0) ? -1 : 0; } void GenRandomData(uint8_t *data, uint32_t length) { uint32_t n; size_t i; for (i = 0 ; i < length ; i += sizeof(n)) { n = random(); (void) memcpy(&data[i], &n, MIN(length - i, sizeof(n))); } }