📄 iscsi_main.c
字号:
* but check pointers to make sure we don't crash the kernel * if the function is called for a task not in the session. */ if (task->order_next && task->order_prev) { task->order_next->order_prev = task->order_prev; task->order_prev->order_next = task->order_next; } else { DEBUG_ERR4("iSCSI: failed to remove itt %u, task %p from session %p to %s\n", task->itt, task, session, session->log_name); return 0; } } task->order_prev = NULL; task->order_next = NULL; atomic_dec(&session->num_active_tasks); return 1;}/* * move all tasks in the session for the specified LUN into the collection. */static void move_session_tasks_for_lun(iscsi_task_collection_t *collection, iscsi_session_t *session, int lun){ iscsi_task_t *search = session->arrival_order.head; while (search) { iscsi_task_t *next = search->order_next; if (search->scsi_cmnd && search->scsi_cmnd->lun == lun) { remove_session_task(session, search); add_task(collection, search); } search = next; }}/* * remove cmnds for the specified LUN that are in the session's cmnd queue, * or the forced abort queue, and return a list of them. */static Scsi_Cmnd *remove_session_cmnds_for_lun(iscsi_session_t *session, int lun){ Scsi_Cmnd *cmnd = NULL; Scsi_Cmnd *prior = NULL; Scsi_Cmnd *head = NULL, *tail = NULL; /* handle any commands we hid from the tx thread */ while (session->ignored_cmnd_head && (session->ignored_cmnd_head->lun == lun)) { /* move the head */ cmnd = session->ignored_cmnd_head; session->ignored_cmnd_head = (Scsi_Cmnd *)cmnd->host_scribble; if (session->ignored_cmnd_head == NULL) session->ignored_cmnd_tail = NULL; atomic_dec(&session->num_ignored_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } /* we're either out of cmnds, or the head is for a different LUN */ prior = session->ignored_cmnd_head; while (prior && (cmnd = (Scsi_Cmnd *)prior->host_scribble)) { if (cmnd->lun == lun) { /* splice out cmnd and move it */ prior->host_scribble = cmnd->host_scribble; if (session->ignored_cmnd_tail == cmnd) session->ignored_cmnd_tail = prior; atomic_dec(&session->num_ignored_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } else { prior = cmnd; } } /* handle cmnds queued for the tx thread to send */ while (session->scsi_cmnd_head && (session->scsi_cmnd_head->lun == lun)) { /* move the head */ cmnd = session->scsi_cmnd_head; session->scsi_cmnd_head = (Scsi_Cmnd *)cmnd->host_scribble; if (session->scsi_cmnd_head == NULL) session->scsi_cmnd_tail = NULL; atomic_dec(&session->num_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } /* we're either out of cmnds, or the head is for a different LUN */ prior = session->scsi_cmnd_head; while (prior && (cmnd = (Scsi_Cmnd *)prior->host_scribble)) { if (cmnd->lun == lun) { /* splice out cmnd and move it */ prior->host_scribble = cmnd->host_scribble; if (session->scsi_cmnd_tail == cmnd) session->scsi_cmnd_tail = prior; atomic_dec(&session->num_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } else { prior = cmnd; } } return head;}/* decode common network errno values into more useful strings. * strerror would be nice right about now. */static char *iscsi_strerror(int errno){ switch (errno) { case EIO: return "I/O error"; case EINTR: return "Interrupted system call"; case ENXIO: return "No such device or address"; case EFAULT: return "Bad address"; case EBUSY: return "Device or resource busy"; case EINVAL: return "Invalid argument"; case EPIPE: return "Broken pipe"; case ENONET: return "Machine is not on the network"; case ECOMM: return "Communication error on send"; case EPROTO: return "Protocol error"; case ENOTUNIQ: return "Name not unique on network"; case ENOTSOCK: return "Socket operation on non-socket"; case ENETDOWN: return "Network is down"; case ENETUNREACH: return "Network is unreachable"; case ENETRESET: return "Network dropped connection because of reset"; case ECONNABORTED: return "Software caused connection abort"; case ECONNRESET: return "Connection reset by peer"; case ESHUTDOWN: return "Cannot send after shutdown"; case ETIMEDOUT: return "Connection timed out"; case ECONNREFUSED: return "Connection refused"; case EHOSTDOWN: return "Host is down"; case EHOSTUNREACH: return "No route to host"; default: return ""; }}static int iscsi_recvmsg( iscsi_session_t *session, struct msghdr *msg, int len ){ int rc = 0; mm_segment_t oldfs; if (session->socket) { oldfs = get_fs(); set_fs( get_ds() ); /* Try to avoid memory allocation deadlocks by using GFP_ATOMIC. */ session->socket->sk->allocation = GFP_ATOMIC; rc = sock_recvmsg( session->socket, msg, len, MSG_WAITALL); if (rc > 0) { session->last_rx = jiffies; mb(); } } set_fs( oldfs ); return rc;}static int iscsi_sendmsg( iscsi_session_t *session, struct msghdr *msg, int len ){ int rc = 0; mm_segment_t oldfs; if (session->socket) { oldfs = get_fs(); set_fs( get_ds() ); /* Try to avoid resource acquisition deadlocks by using GFP_ATOMIC. */ session->socket->sk->allocation = GFP_ATOMIC; /* FIXME: ought to loop handling short writes, unless a signal occurs */ rc = sock_sendmsg(session->socket, msg, len); set_fs( oldfs ); } return rc;}/* create and connect a new socket for this session */static int iscsi_connect(iscsi_session_t *session) { mm_segment_t oldfs; struct socket *socket; struct sockaddr_in addr; int window_size; int arg = 1, arglen = 0; int rc = 0, ret = 0; if (session->socket) { printk("iSCSI: session %p already has socket %p\n", session, session->socket); return 1; } oldfs = get_fs(); set_fs( get_ds() ); if (sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &socket) < 0) { printk("iSCSI: failed to create socket\n"); set_fs(oldfs); return 0; } /* no delay in sending */ if (socket->ops->setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&arg, sizeof(arg)) < 0) { printk("iSCSI: failed to setsockopt TCP_NODELAY\n"); goto done; } /* try to ensure a reasonably sized TCP window */ arglen = sizeof(window_size); if (sock_getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&window_size, &arglen) >= 0) { DEBUG_FLOW1("iSCSI: TCP recv window size %u\n", window_size); if (session->tcp_window_size && (window_size < session->tcp_window_size)) { window_size = session->tcp_window_size; if (sock_setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&window_size, sizeof(window_size)) < 0) { printk("iSCSI: failed to set TCP recv window size to %u\n", window_size); } else if (sock_getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&window_size, &arglen) >= 0) { DEBUG_FLOW2("iSCSI: set TCP recv window size to %u, actually got %u\n", session->tcp_window_size, window_size); } } } else { printk("iSCSI: getsockopt RCVBUF %p failed\n", socket); } if (sock_getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&window_size, &arglen) >= 0) { DEBUG_FLOW1("iSCSI: TCP send window size %u\n", window_size); if (session->tcp_window_size && (window_size < session->tcp_window_size)) { window_size = session->tcp_window_size; if (sock_setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&window_size, sizeof(window_size)) < 0) { printk("iSCSI: failed to set TCP send window size to %u\n", window_size); } else if (sock_getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&window_size, &arglen) >= 0) { DEBUG_FLOW2("iSCSI: set TCP send window size to %u, actually got %u\n", session->tcp_window_size, window_size); } } } else { printk("iSCSI: getsockopt SNDBUF %p failed\n", socket); } /* connect to the target */ addr.sin_family = AF_INET; addr.sin_port = htons(session->port); if (session->address_length == 4) { memcpy(&addr.sin_addr.s_addr, session->ip_address, MIN(sizeof(addr.sin_addr.s_addr), session->address_length)); } else { /* FIXME: IPv6 */ printk("iSCSI: unable to handle IPv6 address\n"); goto done; } rc = socket->ops->connect(socket, (struct sockaddr *)&addr, sizeof(addr), 0); if (signal_pending(current)) goto done; if (rc < 0) { char *error = iscsi_strerror(-rc); if (error && error[0] != '\0') { printk("iSCSI: session %p to %s failed to connect, rc %d, %s\n", session, session->log_name, rc, error); } else { printk("iSCSI: session %p to %s failed to connect, rc %d\n", session, session->log_name, rc); } } else { if (LOG_ENABLED(ISCSI_LOG_LOGIN)) printk("iSCSI: session %p to %s connected at %lu\n", session, session->log_name, jiffies); ret = 1; } done: if (ret) { /* save the socket pointer for later */ session->socket = socket; mb(); } else { /* close the socket */ sock_release(socket); } set_fs(oldfs); return ret;}static void iscsi_disconnect(iscsi_session_t *session) { if (session->socket) { sock_release(session->socket); session->socket = NULL; mb(); }}int iscsi_send_login_pdu(iscsi_session_t *session, struct IscsiLoginHdr *pdu, int max_pdu_length){ struct msghdr msg; struct iovec iov; int rc; int pdu_length = sizeof(*pdu) + ntoh24(pdu->dlength); /* add any padding needed */ if (pdu_length % PAD_WORD_LEN) { int pad = 0; char *data = ((char *)pdu) + pdu_length; pad = PAD_WORD_LEN - (pdu_length % PAD_WORD_LEN); if (pdu_length + pad > max_pdu_length) { printk("iSCSI: session %p failing to send login pdu %p, no room for padding\n", pdu, session); return 0; } DEBUG_FLOW3("iSCSI: session %p adding %d pad bytes on login pdu %p\n", session, pad, pdu); for (; pad; pad--) { *data++ = 0; pdu_length++; } } memset(&iov, 0, sizeof(iov)); iov.iov_base = pdu; iov.iov_len = pdu_length; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -