/* * 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 <sys/types.h> #include <sys/stat.h> #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif #ifdef HAVE_SYS_UIO_H #include <sys/uio.h> #endif #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif #ifdef HAVE_NETINET_TCP_H #include <netinet/tcp.h> #endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif #ifdef HAVE_CTYPE_H #include <ctype.h> #endif #ifdef HAVE_ERRNO_H #include <errno.h> #endif #ifdef HAVE_PTHREAD_H #include <pthread.h> #endif #ifdef HAVE_STDARG_H #include <stdarg.h> #endif #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif #ifdef HAVE_POLL_H #include <poll.h> #endif #include <stdio.h> #include <stdlib.h> #ifdef HAVE_STRING_H #include <string.h> #endif #include <unistd.h> #include "compat.h" #include "iscsiutil.h" /* * Memory Allocation */ void * iscsi_malloc_atomic(unsigned n) { void *ptr; ptr = malloc(n); iscsi_trace(TRACE_MEM, __FILE__, __LINE__, "iscsi_malloc_atomic(%u) = %p\n", n, ptr); return ptr; } void * iscsi_malloc(unsigned n) { void *ptr; ptr = malloc(n); iscsi_trace(TRACE_MEM, __FILE__, __LINE__, "iscsi_malloc(%u) = %p\n", n, ptr); return ptr; } void iscsi_free_atomic(void *ptr) { (void) free(ptr); iscsi_trace(TRACE_MEM, __FILE__, __LINE__, "iscsi_free_atomic(%p)\n", ptr); } void iscsi_free(void *ptr) { (void) free(ptr); iscsi_trace(TRACE_MEM, __FILE__, __LINE__, "iscsi_free(%p)\n", 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_trace_error(__FILE__, __LINE__, "pthread_create() failed\n"); return -1; } if (pthread_detach(thread->pthread) != 0) { iscsi_trace_error(__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; if ((q->elem = iscsi_malloc_atomic((unsigned)(depth * sizeof(void *)))) == NULL) { iscsi_trace_error(__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_trace_error(__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, __FILE__, __LINE__, "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 *f, const int line, 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:%d: %s", (int) ISCSI_GETPID, f, line, buf); va_end(vp); } #endif } void iscsi_trace_warning(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) ISCSI_GETPID, f, line, buf); va_end(vp); } #endif } void iscsi_trace_error(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) ISCSI_GETPID, f, line, buf); # ifdef HAVE_SYSLOG syslog(LOG_ERR, "pid %d:%s:%d: ***ERROR*** %s", ISCSI_GETPID, f, line, buf); # endif /* HAVE_SYSLOG */ #endif } void iscsi_print_buffer(const char *buf, const size_t len) { #ifdef CONFIG_ISCSI_DEBUG int i; if (iscsi_debug_level & TRACE_NET_BUFF) { for (i=0 ; i < len; i++) { if (i % 4 == 0) { if (i) { printf("\n"); } printf("%4i:", 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; if ((h->bucket = iscsi_malloc_atomic(n * sizeof(initiator_cmd_t *))) == NULL) { iscsi_trace_error(__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, __FILE__, __LINE__, "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, __FILE__, __LINE__, "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_trace_error(__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_trace_error(__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, __FILE__, __LINE__, "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, __FILE__, __LINE__, "removed key %u (val 0x%p) from end of bucket\n", key, curr); } else { iscsi_trace(TRACE_HASH, __FILE__, __LINE__, "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) { int len; int disp = offset; int i; struct iovec *iov = *iov_ptr; char *basep; /* Given <offset>, 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, __FILE__, __LINE__, "found offset %u in iov[%d]\n", offset, i); break; } disp -= iov[i].iov_len; } if (i == *iovc) { iscsi_trace_error(__FILE__, __LINE__, "sum of iov lens (%u) < 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 <length>, 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, __FILE__, __LINE__, "length %u ends in iovec[%d]\n", length, i); break; } } if (i == *iovc) { iscsi_trace_error(__FILE__, __LINE__, "sum of iovec lens (%u) < length (%u)\n", len, length); for (i = 0; i < *iovc; i++) { iscsi_trace_error(__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, __FILE__, __LINE__, "new iov:\n"); len = 0; for (i = 0; i < *iovc; i++) { iscsi_trace(TRACE_NET_IOV, __FILE__, __LINE__, "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, __FILE__, __LINE__, "new iov length: %u bytes\n", len); #endif return 0; } int iscsi_sock_setsockopt(iscsi_socket_t * sock, int level, int optname, void *optval, unsigned optlen) { int rc; if ((rc = setsockopt(*sock, level, optname, optval, optlen)) != 0) { iscsi_trace_error(__FILE__, __LINE__, "sock->ops->setsockopt() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_getsockopt(iscsi_socket_t * sock, int level, int optname, void *optval, unsigned *optlen) { int rc; if ((rc = getsockopt(*sock, level, optname, optval, optlen)) != 0) { iscsi_trace_error(__FILE__, __LINE__, "sock->ops->getsockopt() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_create(iscsi_socket_t * sock) { int rc; if ((*sock = rc = socket(AF_INET, SOCK_STREAM, 0)) < 0) { iscsi_trace_error(__FILE__, __LINE__, "socket() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_bind(iscsi_socket_t 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); if ((rc = bind(sock, (struct sockaddr *) (void *) &laddr, sizeof(laddr))) < 0) { iscsi_trace_error(__FILE__, __LINE__, "bind() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } int iscsi_sock_listen(iscsi_socket_t sock) { int rc; if ((rc = listen(sock, 32)) < 0) { iscsi_trace_error(__FILE__, __LINE__, "listen() failed: rc %d errno %d\n", rc, errno); return 0; } return 1; } #ifndef MAXSOCK #define MAXSOCK 16 #endif int iscsi_socks_establish(iscsi_socket_t *sockv, int *famv, int *sockc, int 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 = family; 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_trace_error(__FILE__, __LINE__, "getaddrinfo: %s", gai_strerror(error)); return 0; } } *sockc = 0; for (res = res0; res && *sockc < MAXSOCK; res = res->ai_next) { if ((sockv[*sockc] = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) { cause = "socket"; continue; } famv[*sockc] = res->ai_family; if (!iscsi_sock_setsockopt(&sockv[*sockc], SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_sock_setsockopt() failed\n"); continue; } if (!iscsi_sock_setsockopt(&sockv[*sockc], SOL_TCP, TCP_NODELAY, &one, sizeof(one))) { iscsi_trace_error(__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_trace_error(__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) { return (fam == AF_INET) ? "IPv4" : (fam == AF_INET6) ? "IPv6" : "[unknown type]"; } /* wait for a connection to come in on a socket */ /* ARGSUSED2 */ int iscsi_waitfor_connection(iscsi_socket_t *sockv, int sockc, const char *cf, iscsi_socket_t *sock) { #ifdef HAVE_POLL struct pollfd socks[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, __FILE__, __LINE__, "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, __FILE__, __LINE__, "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, __FILE__, __LINE__, "connection %d selected\n", sockv[i]); *sock = sockv[i]; return i; } } } } #endif } int iscsi_sock_accept(iscsi_socket_t sock, iscsi_socket_t * newsock) { struct sockaddr_in remoteAddr; socklen_t remoteAddrLen; remoteAddrLen = sizeof(remoteAddr); (void) memset(&remoteAddr, 0, sizeof(remoteAddr)); if ((*newsock = accept(sock, (struct sockaddr *) (void *)& remoteAddr, &remoteAddrLen)) < 0) { iscsi_trace(TRACE_NET_DEBUG, __FILE__, __LINE__, "accept() failed: rc %d errno %d\n", *newsock, errno); return 0; } return 1; } int iscsi_sock_getsockname(iscsi_socket_t sock, struct sockaddr * name, unsigned *namelen) { if (getsockname(sock, name, namelen) != 0) { iscsi_trace_error(__FILE__, __LINE__, "getsockame() failed (errno %d)\n", errno); return 0; } return 1; } int iscsi_sock_getpeername(iscsi_socket_t sock, struct sockaddr * name, unsigned *namelen) { if (getpeername(sock, name, namelen) != 0) { iscsi_trace_error(__FILE__, __LINE__, "getpeername() failed (errno %d)\n", errno); return 0; } return 1; } int iscsi_sock_shutdown(iscsi_socket_t sock, int how) { int rc; if ((rc = shutdown(sock, how)) != 0) { iscsi_trace(TRACE_NET_DEBUG, __FILE__, __LINE__, "shutdown() failed: rc %d, errno %d\n", rc, errno); } return 0; } int iscsi_sock_close(iscsi_socket_t sock) { int rc; if ((rc = close(sock)) != 0) { iscsi_trace_error(__FILE__, __LINE__, "close() failed: rc %d errno %d\n", rc, errno); return -1; } return 0; } int iscsi_sock_connect(iscsi_socket_t 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 = PF_UNSPEC; 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_trace_error(__FILE__, __LINE__, "getaddrinfo: %s", gai_strerror(rc)); return 0; } } #if ISCSI_SOCK_CONNECT_NONBLOCK == 1 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) { iscsi_trace_error(__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_trace_error(__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"); ISCSI_SLEEP(1); } } else { break; } } freeaddrinfo(res); if (rc < 0) { iscsi_trace_error(__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(iscsi_socket_t sock, int xmit, unsigned len, void *data, int iovc) { int i, n = 0; int rc; struct iovec *iov; struct iovec singleton; uint8_t padding[ISCSI_SOCK_MSG_BYTE_ALIGN]; struct iovec *iov_padding = NULL; uint32_t remainder; uint32_t padding_len = 0; int total_len = 0; iscsi_trace(TRACE_NET_DEBUG, __FILE__, __LINE__, "%s %d bytes on sock\n", xmit ? "sending" : "receiving", len); if (iovc == 0) { iscsi_trace(TRACE_NET_DEBUG, __FILE__, __LINE__, "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) { if ((iov_padding = iscsi_malloc_atomic((iovc + 1) * sizeof(struct iovec))) == NULL) { iscsi_trace_error(__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, __FILE__, __LINE__, "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, __FILE__, __LINE__, "%s %d buffers\n", xmit ? "gathering from" : "scattering into", iovc); for (i = 0; i < iovc; i++) { iscsi_trace(TRACE_NET_IOV, __FILE__, __LINE__, "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_trace_error(__FILE__, __LINE__, "iovcs sum to %d != total len of %d\n", total_len, len - n); iscsi_trace_error(__FILE__, __LINE__, "iov = %p\n", iov); for (i = 0; i < iovc; i++) { iscsi_trace_error(__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, __FILE__, __LINE__, "%s() failed: rc %d errno %d\n", (xmit) ? "writev" : "readv", rc, errno); break; } else if (rc < 0) { /* Temp FIXME */ iscsi_trace_error(__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, __FILE__, __LINE__, "Got partial %s: %d bytes of %d\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, __FILE__, __LINE__, "before modify_iov: %s %d buffers, total_len = %u, 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_trace_error(__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, __FILE__, __LINE__, "after modify_iov: %s %d buffers, total_len = %u, 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, __FILE__, __LINE__, "successfully %s %d bytes on sock (%d 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(iscsi_socket_t 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_trace_error(__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 (iscsi_sock_msg(sock, Transmit, header_len + data_len, iov, iovc) != header_len + data_len) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); return -1; } } else { if (iscsi_sock_msg(sock, Transmit, header_len, header, 0) != header_len) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_sock_msg() failed\n"); return -1; } if (data_len != 0 && iscsi_sock_msg(sock, Transmit, data_len, __UNCONST((const char *) data), iovc) != data_len) { iscsi_trace_error(__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_trace_error(__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) { unsigned n; uint32_t r; for ( ; length > 0 ; length--) { r = rand(); r = r ^ (r >> 8); r = r ^ (r >> 4); n = r & 0x7; r = rand(); r = r ^ (r >> 8); r = r ^ (r >> 5); n = (n << 3) | (r & 0x7); r = rand(); r = r ^ (r >> 8); r = r ^ (r >> 5); n = (n << 2) | (r & 0x3); *data++ = n; } } void cdb2lba(uint32_t *lba, uint16_t *len, uint8_t *cdb) { /* Some platforms (like strongarm) aligns on */ /* word boundaries. So HTONL and NTOHL won't */ /* work here. */ int indian = 1; if (*(char *) (void *) &indian) { /* little endian */ ((uint8_t *) (void *) lba)[0] = cdb[5]; ((uint8_t *) (void *) lba)[1] = cdb[4]; ((uint8_t *) (void *) lba)[2] = cdb[3]; ((uint8_t *) (void *) lba)[3] = cdb[2]; ((uint8_t *) (void *) len)[0] = cdb[8]; ((uint8_t *) (void *) len)[1] = cdb[7]; } else { ((uint8_t *) (void *) lba)[0] = cdb[2]; ((uint8_t *) (void *) lba)[1] = cdb[3]; ((uint8_t *) (void *) lba)[2] = cdb[4]; ((uint8_t *) (void *) lba)[3] = cdb[5]; ((uint8_t *) (void *) len)[0] = cdb[7]; ((uint8_t *) (void *) len)[1] = cdb[8]; } } void lba2cdb(uint8_t *cdb, uint32_t *lba, uint16_t *len) { /* Some platforms (like strongarm) aligns on */ /* word boundaries. So HTONL and NTOHL won't */ /* work here. */ int indian = 1; if (*(char *) (void *) &indian) { /* little endian */ cdb[2] = ((uint8_t *) (void *)lba)[3]; cdb[3] = ((uint8_t *) (void *)lba)[2]; cdb[4] = ((uint8_t *) (void *)lba)[1]; cdb[5] = ((uint8_t *) (void *)lba)[0]; cdb[7] = ((uint8_t *) (void *)len)[1]; cdb[8] = ((uint8_t *) (void *)len)[0]; } else { /* big endian */ cdb[2] = ((uint8_t *) (void *)lba)[2]; cdb[3] = ((uint8_t *) (void *)lba)[3]; cdb[4] = ((uint8_t *) (void *)lba)[0]; cdb[5] = ((uint8_t *) (void *)lba)[1]; cdb[7] = ((uint8_t *) (void *)len)[0]; cdb[8] = ((uint8_t *) (void *)len)[1]; } }