📄 iscsi.c
字号:
}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); 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 */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 0; } 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_INIT2("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_INIT2("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->ip_length == 4) { memcpy(&addr.sin_addr.s_addr, session->ip_address, MIN(sizeof(addr.sin_addr.s_addr), session->ip_length)); } else { /* FIXME: IPv6 */ printk("iSCSI: unable to handle IPv6 address, length %u, addr %u.%u.%u.%u\n", session->ip_length, session->ip_address[0], session->ip_address[1], session->ip_address[2], session->ip_address[3]); 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; } else { /* close the socket */ sock_release(socket); session->socket = NULL; } wmb(); set_fs(oldfs); return ret;}void iscsi_disconnect(iscsi_session_t *session) { if (session->socket) {#if TCP_ABORT_ON_DROP if (test_and_clear_bit(SESSION_DROPPED, &session->control_bits) && !test_bit(SESSION_LOGGED_OUT, &session->control_bits)) { /* setting linger on and lingertime to 0 before closing * the socket will trigger a TCP abort (abort all sends * and receives, possibly send RST, connection to CLOSED), * which is probably what we want if we're dropping and * restarting a session. A TCP Abort will discard TCP * data, which is probably a bunch of commands and data * we'll resend on a new session anyway. This frees up * skbuffs, and makes the VM livelock less likely. When * we relogin again to the target with the same ISID, the * target will kill off the old connections on it's side, * so the FIN handshake should be unnecessary, and there * are cases where network failures may prevent the FIN * handshake from completing, so the connection wouldn't * get cleaned up unless the TCP stack has timeouts for * some of the TCP states. */ struct linger ling; mm_segment_t oldfs; memset(&ling, 0, sizeof(ling)); ling.l_onoff = 1; ling.l_linger = 0; /* we could adjust the socket linger values directly, but using the sockopt call * is less likely to break if someone overhauls the socket structure. */ oldfs = get_fs(); set_fs(get_ds()); if (sock_setsockopt(session->socket, IPPROTO_TCP, SO_LINGER, (char *)&ling, sizeof(ling)) < 0) { printk("iSCSI: session %p couldn't set lingertime to zero after session drop\n", session); } else { DEBUG_INIT1("iSCSI: session %p set lingertime to zero because of session drop\n", session); } set_fs(oldfs); }#endif /* close the socket, triggering either a TCP close or a TCP abort */ sock_release(session->socket); session->socket = NULL; wmb(); }}int iscsi_send_pdu(iscsi_session_t *session, struct IscsiHdr *pdu, char *data, int timeout){ struct msghdr msg; struct iovec iov[3]; char padding[4]; int pad = 0; int rc; int pdu_length = 0; int data_length; if (pdu == NULL) { printk("iSCSI: session %p, pdu NULL, can't send PDU header\n", session); return 0; } memset(iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = 1; /* pdu header */ iov[0].iov_base = pdu; iov[0].iov_len = sizeof(*pdu); pdu_length = sizeof(*pdu); /* pdu data */ data_length = ntoh24(pdu->dlength); if (data) { iov[msg.msg_iovlen].iov_base = data; iov[msg.msg_iovlen].iov_len = data_length; msg.msg_iovlen++; pdu_length += ntoh24(pdu->dlength); } else if (data_length) { printk("iSCSI: session %p pdu %p with dlength %d, but data NULL\n", session, pdu, data_length); return 0; } /* add any padding needed */ if (pdu_length % PAD_WORD_LEN) { memset(padding, 0x0, sizeof(padding)); pad = PAD_WORD_LEN - (pdu_length % PAD_WORD_LEN); } if (pad) { iov[msg.msg_iovlen].iov_base = padding; iov[msg.msg_iovlen].iov_len = pad; msg.msg_iovlen++; pdu_length += pad; } /* set a timer, though we shouldn't really need one */ if (timeout) { session->login_phase_timer = jiffies + (timeout * HZ); wmb(); } if (LOG_ENABLED(ISCSI_LOG_LOGIN)) { char *text = data; char *end = text + ntoh24(pdu->dlength); int show_text = 0; if ((pdu->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN_CMD) { struct IscsiLoginHdr *login_pdu = (struct IscsiLoginHdr *)pdu; /* show the login phases and tbit */ printk("iSCSI: session %p sending login pdu with current phase %d, next %d, tbit %d, dlength %d at %lu, timeout at %lu (%d seconds)\n", session, login_pdu->curr, login_pdu->next, login_pdu->tbit, ntoh24(pdu->dlength), jiffies, session->login_phase_timer, session->login_timeout); show_text = 1; } else if ((pdu->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_TEXT_CMD) { printk("iSCSI: session %p sending text pdu, dlength %d at %lu, timeout at %lu (%d seconds)\n", session, ntoh24(pdu->dlength), jiffies, session->login_phase_timer, session->login_timeout); show_text = 1; } else { printk("iSCSI: session %p sending pdu with opcode 0x%x, dlength %d at %lu, timeout at %lu (%d seconds)\n", session, pdu->opcode, ntoh24(pdu->dlength), jiffies, session->login_phase_timer, session->login_timeout); } /* show all the text that we're sending */ while (show_text && (text < end)) { printk("iSCSI: session %p login text: %s\n", session, text); text += strlen(text); while ((text < end) && (*text == '\0')) text++; } } rc = iscsi_sendmsg(session, &msg, pdu_length); /* clear the timer */ session->login_phase_timer = 0; wmb(); if (rc != pdu_length) { char *error; if ((rc < 0) && (error = iscsi_strerror(-rc)) && (error[0] != '\0')) printk("iSCSI: session %p failed to send login PDU, rc %d, %s\n", session, rc, iscsi_strerror(-rc)); else printk("iSCSI: session %p failed to send login PDU, rc %d\n", session, rc); return 0; } DEBUG_FLOW5("iSCSI: session %p sent login pdu %p at %lu, length %d, dlength %d\n", session, pdu, jiffies, pdu_length, ntoh24(pdu->dlength)); return 1;}/* try to read an entire login PDU into the buffer, timing out after timeout seconds */int iscsi_recv_pdu(iscsi_session_t *session, struct IscsiHdr *header, int max_header_length, char *data, int max_data_length, int timeout){ struct msghdr msg; struct iovec iov[2]; char padding[PAD_WORD_LEN]; int rc = 0; int data_length; int ret = 0; if (header == NULL) { printk("iSCSI: session %p, can't receive PDU header into NULL\n", session); return 0; } if (max_header_length < sizeof(*header)) { printk("iSCSI: session %p, can't receive %d PDU header bytes into %d byte buffer\n", session, sizeof(*header), max_header_length); return 0; } /* set the timer to implement the timeout requested */ if (timeout) session->login_phase_timer = jiffies + (timeout * HZ); else session->login_phase_timer = 0; mb(); if (LOG_ENABLED(ISCSI_LOG_LOGIN)) { printk("iSCSI: session %p trying to recv login pdu at %lu, timeout at %lu (%d seconds)\n", session, jiffies, session->login_phase_timer, timeout); } /* read the PDU header */ memset(iov, 0, sizeof(iov)); iov[0].iov_base = (void *)header; iov[0].iov_len = sizeof(*header); memset(&msg, 0, sizeof(struct msghdr)); msg.msg_iov = iov; msg.msg_iovlen = 1; rc = iscsi_recvmsg(session, &msg, sizeof(*header)); /* FIXME: check for additional header segments */ if (signal_pending(current)) { printk("iSCSI: session %p recv_login_pdu timed out at %lu\n", session, jiffies); goto done; } if (rc != sizeof(*header)) { if (rc < 0) { char *error = iscsi_strerror(-rc); if (error && error[0] != '\0') { printk("iSCSI: session %p recv_login_pdu failed to recv %d login PDU bytes, rc %d, %s\n", session, iov[0].iov_len, rc, iscsi_strerror(-rc)); } else { printk("iSCSI: session %p recv_login_pdu failed to recv %d login PDU bytes, rc %d\n", session, iov[0].iov_len, rc); } } else if (rc == 0) { printk("iSCSI: session %p recv_lo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -