diff options
Diffstat (limited to 'drivers/scsi/iscsi_tcp.c')
| -rw-r--r-- | drivers/scsi/iscsi_tcp.c | 330 |
1 files changed, 191 insertions, 139 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index fec47de7253..a669f2d11c3 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -35,6 +35,7 @@ #include <linux/delay.h> #include <linux/kfifo.h> #include <linux/scatterlist.h> +#include <linux/module.h> #include <net/tcp.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -54,7 +55,7 @@ static struct scsi_transport_template *iscsi_sw_tcp_scsi_transport; static struct scsi_host_template iscsi_sw_tcp_sht; static struct iscsi_transport iscsi_sw_tcp_transport; -static unsigned int iscsi_max_lun = 512; +static unsigned int iscsi_max_lun = ~0; module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); static int iscsi_sw_tcp_dbg; @@ -107,12 +108,15 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, * If the socket is in CLOSE or CLOSE_WAIT we should * not close the connection if there is still some * data pending. + * + * Must be called with sk_callback_lock. */ static inline int iscsi_sw_sk_state_check(struct sock *sk) { - struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data; + struct iscsi_conn *conn = sk->sk_user_data; if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) && + (conn->session->state != ISCSI_STATE_LOGGING_OUT) && !atomic_read(&sk->sk_rmem_alloc)) { ISCSI_SW_TCP_DBG(conn, "TCP_CLOSE|TCP_CLOSE_WAIT\n"); iscsi_conn_failure(conn, ISCSI_ERR_TCP_CONN_CLOSE); @@ -121,13 +125,19 @@ static inline int iscsi_sw_sk_state_check(struct sock *sk) return 0; } -static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag) +static void iscsi_sw_tcp_data_ready(struct sock *sk) { - struct iscsi_conn *conn = sk->sk_user_data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + struct iscsi_conn *conn; + struct iscsi_tcp_conn *tcp_conn; read_descriptor_t rd_desc; read_lock(&sk->sk_callback_lock); + conn = sk->sk_user_data; + if (!conn) { + read_unlock(&sk->sk_callback_lock); + return; + } + tcp_conn = conn->dd_data; /* * Use rd_desc to pass 'conn' to iscsi_tcp_recv. @@ -141,11 +151,10 @@ static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag) iscsi_sw_sk_state_check(sk); - read_unlock(&sk->sk_callback_lock); - /* If we had to (atomically) map a highmem page, * unmap it now. */ iscsi_tcp_segment_unmap(&tcp_conn->in.segment); + read_unlock(&sk->sk_callback_lock); } static void iscsi_sw_tcp_state_change(struct sock *sk) @@ -157,8 +166,11 @@ static void iscsi_sw_tcp_state_change(struct sock *sk) void (*old_state_change)(struct sock *); read_lock(&sk->sk_callback_lock); - - conn = (struct iscsi_conn*)sk->sk_user_data; + conn = sk->sk_user_data; + if (!conn) { + read_unlock(&sk->sk_callback_lock); + return; + } session = conn->session; iscsi_sw_sk_state_check(sk); @@ -178,11 +190,25 @@ static void iscsi_sw_tcp_state_change(struct sock *sk) **/ static void iscsi_sw_tcp_write_space(struct sock *sk) { - struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; + struct iscsi_conn *conn; + struct iscsi_tcp_conn *tcp_conn; + struct iscsi_sw_tcp_conn *tcp_sw_conn; + void (*old_write_space)(struct sock *); + + read_lock_bh(&sk->sk_callback_lock); + conn = sk->sk_user_data; + if (!conn) { + read_unlock_bh(&sk->sk_callback_lock); + return; + } + + tcp_conn = conn->dd_data; + tcp_sw_conn = tcp_conn->dd_data; + old_write_space = tcp_sw_conn->old_write_space; + read_unlock_bh(&sk->sk_callback_lock); + + old_write_space(sk); - tcp_sw_conn->old_write_space(sk); ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n"); iscsi_conn_queue_work(conn); } @@ -218,7 +244,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn) sk->sk_data_ready = tcp_sw_conn->old_data_ready; sk->sk_state_change = tcp_sw_conn->old_state_change; sk->sk_write_space = tcp_sw_conn->old_write_space; - sk->sk_no_check = 0; + sk->sk_no_check_tx = 0; write_unlock_bh(&sk->sk_callback_lock); } @@ -295,7 +321,7 @@ static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn) rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment); /* * We may not have been able to send data because the conn - * is getting stopped. libiscsi will know so propogate err + * is getting stopped. libiscsi will know so propagate err * for it to do the right thing. */ if (rc == -EAGAIN) @@ -345,17 +371,24 @@ static inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn) static int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task) { struct iscsi_conn *conn = task->conn; - int rc; + unsigned long pflags = current->flags; + int rc = 0; + + current->flags |= PF_MEMALLOC; while (iscsi_sw_tcp_xmit_qlen(conn)) { rc = iscsi_sw_tcp_xmit(conn); - if (rc == 0) - return -EAGAIN; + if (rc == 0) { + rc = -EAGAIN; + break; + } if (rc < 0) - return rc; + break; + rc = 0; } - return 0; + tsk_restore_flags(current, pflags, PF_MEMALLOC); + return rc; } /* @@ -560,9 +593,9 @@ static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn) iscsi_sw_tcp_conn_restore_callbacks(conn); sock_put(sock->sk); - spin_lock_bh(&session->lock); + spin_lock_bh(&session->frwd_lock); tcp_sw_conn->sock = NULL; - spin_unlock_bh(&session->lock); + spin_unlock_bh(&session->frwd_lock); sockfd_put(sock); } @@ -592,61 +625,17 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) /* userspace may have goofed up and not bound us */ if (!sock) return; - /* - * Make sure our recv side is stopped. - * Older tools called conn stop before ep_disconnect - * so IO could still be coming in. - */ - write_lock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock); - set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); - write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock); sock->sk->sk_err = EIO; wake_up_interruptible(sk_sleep(sock->sk)); - iscsi_conn_stop(cls_conn, flag); - iscsi_sw_tcp_release_conn(conn); -} - -static int iscsi_sw_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock, - char *buf, int *port, - int (*getname)(struct socket *, - struct sockaddr *, - int *addrlen)) -{ - struct sockaddr_storage *addr; - struct sockaddr_in6 *sin6; - struct sockaddr_in *sin; - int rc = 0, len; + /* stop xmit side */ + iscsi_suspend_tx(conn); - addr = kmalloc(sizeof(*addr), GFP_KERNEL); - if (!addr) - return -ENOMEM; - - if (getname(sock, (struct sockaddr *) addr, &len)) { - rc = -ENODEV; - goto free_addr; - } + /* stop recv side and release socket */ + iscsi_sw_tcp_release_conn(conn); - switch (addr->ss_family) { - case AF_INET: - sin = (struct sockaddr_in *)addr; - spin_lock_bh(&conn->session->lock); - sprintf(buf, "%pI4", &sin->sin_addr.s_addr); - *port = be16_to_cpu(sin->sin_port); - spin_unlock_bh(&conn->session->lock); - break; - case AF_INET6: - sin6 = (struct sockaddr_in6 *)addr; - spin_lock_bh(&conn->session->lock); - sprintf(buf, "%pI6", &sin6->sin6_addr); - *port = be16_to_cpu(sin6->sin6_port); - spin_unlock_bh(&conn->session->lock); - break; - } -free_addr: - kfree(addr); - return rc; + iscsi_conn_stop(cls_conn, flag); } static int @@ -654,8 +643,7 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, int is_leading) { - struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); - struct iscsi_host *ihost = shost_priv(shost); + struct iscsi_session *session = cls_session->dd_data; struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; @@ -670,33 +658,22 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, "sockfd_lookup failed %d\n", err); return -EEXIST; } - /* - * copy these values now because if we drop the session - * userspace may still want to query the values since we will - * be using them for the reconnect - */ - err = iscsi_sw_tcp_get_addr(conn, sock, conn->portal_address, - &conn->portal_port, kernel_getpeername); - if (err) - goto free_socket; - - err = iscsi_sw_tcp_get_addr(conn, sock, ihost->local_address, - &ihost->local_port, kernel_getsockname); - if (err) - goto free_socket; err = iscsi_conn_bind(cls_session, cls_conn, is_leading); if (err) goto free_socket; + spin_lock_bh(&session->frwd_lock); /* bind iSCSI connection and socket */ tcp_sw_conn->sock = sock; + spin_unlock_bh(&session->frwd_lock); /* setup Socket parameters */ sk = sock->sk; - sk->sk_reuse = 1; + sk->sk_reuse = SK_CAN_REUSE; sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */ sk->sk_allocation = GFP_ATOMIC; + sk_set_memalloc(sk); iscsi_sw_tcp_conn_set_callbacks(conn); tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage; @@ -716,10 +693,8 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; - int value; switch(param) { case ISCSI_PARAM_HDRDGST_EN: @@ -731,16 +706,7 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage; break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &value); - if (value <= 0 || !is_power_of_2(value)) - return -EINVAL; - if (session->max_r2t == value) - break; - iscsi_tcp_r2tpool_free(session); - iscsi_set_param(cls_conn, param, buf, buflen); - if (iscsi_tcp_r2tpool_alloc(session)) - return -ENOMEM; - break; + return iscsi_tcp_set_max_r2t(conn, buf); default: return iscsi_set_param(cls_conn, param, buf, buflen); } @@ -752,24 +718,77 @@ static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf) { struct iscsi_conn *conn = cls_conn->dd_data; - int len; + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; + struct sockaddr_in6 addr; + int rc, len; switch(param) { case ISCSI_PARAM_CONN_PORT: - spin_lock_bh(&conn->session->lock); - len = sprintf(buf, "%hu\n", conn->portal_port); - spin_unlock_bh(&conn->session->lock); - break; case ISCSI_PARAM_CONN_ADDRESS: - spin_lock_bh(&conn->session->lock); - len = sprintf(buf, "%s\n", conn->portal_address); - spin_unlock_bh(&conn->session->lock); - break; + spin_lock_bh(&conn->session->frwd_lock); + if (!tcp_sw_conn || !tcp_sw_conn->sock) { + spin_unlock_bh(&conn->session->frwd_lock); + return -ENOTCONN; + } + rc = kernel_getpeername(tcp_sw_conn->sock, + (struct sockaddr *)&addr, &len); + spin_unlock_bh(&conn->session->frwd_lock); + if (rc) + return rc; + + return iscsi_conn_get_addr_param((struct sockaddr_storage *) + &addr, param, buf); default: return iscsi_conn_get_param(cls_conn, param, buf); } - return len; + return 0; +} + +static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost, + enum iscsi_host_param param, char *buf) +{ + struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost); + struct iscsi_session *session = tcp_sw_host->session; + struct iscsi_conn *conn; + struct iscsi_tcp_conn *tcp_conn; + struct iscsi_sw_tcp_conn *tcp_sw_conn; + struct sockaddr_in6 addr; + int rc, len; + + switch (param) { + case ISCSI_HOST_PARAM_IPADDRESS: + if (!session) + return -ENOTCONN; + + spin_lock_bh(&session->frwd_lock); + conn = session->leadconn; + if (!conn) { + spin_unlock_bh(&session->frwd_lock); + return -ENOTCONN; + } + tcp_conn = conn->dd_data; + + tcp_sw_conn = tcp_conn->dd_data; + if (!tcp_sw_conn->sock) { + spin_unlock_bh(&session->frwd_lock); + return -ENOTCONN; + } + + rc = kernel_getsockname(tcp_sw_conn->sock, + (struct sockaddr *)&addr, &len); + spin_unlock_bh(&session->frwd_lock); + if (rc) + return rc; + + return iscsi_conn_get_addr_param((struct sockaddr_storage *) + &addr, param, buf); + default: + return iscsi_host_get_param(shost, param, buf); + } + + return 0; } static void @@ -797,6 +816,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, { struct iscsi_cls_session *cls_session; struct iscsi_session *session; + struct iscsi_sw_tcp_host *tcp_sw_host; struct Scsi_Host *shost; if (ep) { @@ -804,7 +824,8 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, return NULL; } - shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 0, 1); + shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, + sizeof(struct iscsi_sw_tcp_host), 1); if (!shost) return NULL; shost->transportt = iscsi_sw_tcp_scsi_transport; @@ -825,6 +846,8 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, if (!cls_session) goto remove_host; session = cls_session->dd_data; + tcp_sw_host = iscsi_host_priv(shost); + tcp_sw_host->session = session; shost->can_queue = session->scsi_cmds_max; if (iscsi_tcp_r2tpool_alloc(session)) @@ -851,6 +874,61 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) iscsi_host_free(shost); } +static umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param) +{ + switch (param_type) { + case ISCSI_HOST_PARAM: + switch (param) { + case ISCSI_HOST_PARAM_NETDEV_NAME: + case ISCSI_HOST_PARAM_HWADDRESS: + case ISCSI_HOST_PARAM_IPADDRESS: + case ISCSI_HOST_PARAM_INITIATOR_NAME: + return S_IRUGO; + default: + return 0; + } + case ISCSI_PARAM: + switch (param) { + case ISCSI_PARAM_MAX_RECV_DLENGTH: + case ISCSI_PARAM_MAX_XMIT_DLENGTH: + case ISCSI_PARAM_HDRDGST_EN: + case ISCSI_PARAM_DATADGST_EN: + case ISCSI_PARAM_CONN_ADDRESS: + case ISCSI_PARAM_CONN_PORT: + case ISCSI_PARAM_EXP_STATSN: + case ISCSI_PARAM_PERSISTENT_ADDRESS: + case ISCSI_PARAM_PERSISTENT_PORT: + case ISCSI_PARAM_PING_TMO: + case ISCSI_PARAM_RECV_TMO: + case ISCSI_PARAM_INITIAL_R2T_EN: + case ISCSI_PARAM_MAX_R2T: + case ISCSI_PARAM_IMM_DATA_EN: + case ISCSI_PARAM_FIRST_BURST: + case ISCSI_PARAM_MAX_BURST: + case ISCSI_PARAM_PDU_INORDER_EN: + case ISCSI_PARAM_DATASEQ_INORDER_EN: + case ISCSI_PARAM_ERL: + case ISCSI_PARAM_TARGET_NAME: + case ISCSI_PARAM_TPGT: + case ISCSI_PARAM_USERNAME: + case ISCSI_PARAM_PASSWORD: + case ISCSI_PARAM_USERNAME_IN: + case ISCSI_PARAM_PASSWORD_IN: + case ISCSI_PARAM_FAST_ABORT: + case ISCSI_PARAM_ABORT_TMO: + case ISCSI_PARAM_LU_RESET_TMO: + case ISCSI_PARAM_TGT_RESET_TMO: + case ISCSI_PARAM_IFACE_NAME: + case ISCSI_PARAM_INITIATOR_NAME: + return S_IRUGO; + default: + return 0; + } + } + + return 0; +} + static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev) { set_bit(QUEUE_FLAG_BIDI, &sdev->request_queue->queue_flags); @@ -889,33 +967,6 @@ static struct iscsi_transport iscsi_sw_tcp_transport = { .name = "tcp", .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST | CAP_DATADGST, - .param_mask = ISCSI_MAX_RECV_DLENGTH | - ISCSI_MAX_XMIT_DLENGTH | - ISCSI_HDRDGST_EN | - ISCSI_DATADGST_EN | - ISCSI_INITIAL_R2T_EN | - ISCSI_MAX_R2T | - ISCSI_IMM_DATA_EN | - ISCSI_FIRST_BURST | - ISCSI_MAX_BURST | - ISCSI_PDU_INORDER_EN | - ISCSI_DATASEQ_INORDER_EN | - ISCSI_ERL | - ISCSI_CONN_PORT | - ISCSI_CONN_ADDRESS | - ISCSI_EXP_STATSN | - ISCSI_PERSISTENT_PORT | - ISCSI_PERSISTENT_ADDRESS | - ISCSI_TARGET_NAME | ISCSI_TPGT | - ISCSI_USERNAME | ISCSI_PASSWORD | - ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | - ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | - ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | - ISCSI_PING_TMO | ISCSI_RECV_TMO | - ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, - .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | - ISCSI_HOST_INITIATOR_NAME | - ISCSI_HOST_NETDEV_NAME, /* session management */ .create_session = iscsi_sw_tcp_session_create, .destroy_session = iscsi_sw_tcp_session_destroy, @@ -923,13 +974,14 @@ static struct iscsi_transport iscsi_sw_tcp_transport = { .create_conn = iscsi_sw_tcp_conn_create, .bind_conn = iscsi_sw_tcp_conn_bind, .destroy_conn = iscsi_sw_tcp_conn_destroy, + .attr_is_visible = iscsi_sw_tcp_attr_is_visible, .set_param = iscsi_sw_tcp_conn_set_param, .get_conn_param = iscsi_sw_tcp_conn_get_param, .get_session_param = iscsi_session_get_param, .start_conn = iscsi_conn_start, .stop_conn = iscsi_sw_tcp_conn_stop, /* iscsi host params */ - .get_host_param = iscsi_host_get_param, + .get_host_param = iscsi_sw_tcp_host_get_param, .set_host_param = iscsi_host_set_param, /* IO */ .send_pdu = iscsi_conn_send_pdu, |
