config: tegra3: enable /dev mount with ACL
[linux-2.6.git] / drivers / scsi / iscsi_tcp.c
index edc49ca..7724414 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <linux/types.h>
 #include <linux/inet.h>
+#include <linux/slab.h>
 #include <linux/file.h>
 #include <linux/blkdev.h>
 #include <linux/crypto.h>
@@ -106,10 +107,12 @@ 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) &&
            !atomic_read(&sk->sk_rmem_alloc)) {
@@ -122,11 +125,17 @@ static inline int iscsi_sw_sk_state_check(struct sock *sk)
 
 static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
 {
-       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.
@@ -140,11 +149,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)
@@ -156,8 +164,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);
@@ -177,11 +188,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);
 }
@@ -205,8 +230,10 @@ static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn)
 }
 
 static void
-iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_sw_tcp_conn *tcp_sw_conn)
+iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn)
 {
+       struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+       struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
        struct sock *sk = tcp_sw_conn->sock->sk;
 
        /* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
@@ -292,7 +319,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)
@@ -554,7 +581,7 @@ static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn)
                return;
 
        sock_hold(sock->sk);
-       iscsi_sw_tcp_conn_restore_callbacks(tcp_sw_conn);
+       iscsi_sw_tcp_conn_restore_callbacks(conn);
        sock_put(sock->sk);
 
        spin_lock_bh(&session->lock);
@@ -584,62 +611,22 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
        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;
+       struct socket *sock = tcp_sw_conn->sock;
 
        /* userspace may have goofed up and not bound us */
-       if (!tcp_sw_conn->sock)
+       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);
-
-       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;
+       sock->sk->sk_err = EIO;
+       wake_up_interruptible(sk_sleep(sock->sk));
 
-       addr = kmalloc(sizeof(*addr), GFP_KERNEL);
-       if (!addr)
-               return -ENOMEM;
+       /* stop xmit side */
+       iscsi_suspend_tx(conn);
 
-       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
@@ -647,8 +634,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;
@@ -663,27 +649,15 @@ 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->lock);
        /* bind iSCSI connection and socket */
        tcp_sw_conn->sock = sock;
+       spin_unlock_bh(&session->lock);
 
        /* setup Socket parameters */
        sk = sock->sk;
@@ -745,24 +719,74 @@ 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);
+               if (!tcp_sw_conn || !tcp_sw_conn->sock) {
+                       spin_unlock_bh(&conn->session->lock);
+                       return -ENOTCONN;
+               }
+               rc = kernel_getpeername(tcp_sw_conn->sock,
+                                       (struct sockaddr *)&addr, &len);
                spin_unlock_bh(&conn->session->lock);
-               break;
+               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:
+               spin_lock_bh(&session->lock);
+               conn = session->leadconn;
+               if (!conn) {
+                       spin_unlock_bh(&session->lock);
+                       return -ENOTCONN;
+               }
+               tcp_conn = conn->dd_data;
+
+               tcp_sw_conn = tcp_conn->dd_data;
+               if (!tcp_sw_conn->sock) {
+                       spin_unlock_bh(&session->lock);
+                       return -ENOTCONN;
+               }
+
+               rc = kernel_getsockname(tcp_sw_conn->sock,
+                                       (struct sockaddr *)&addr, &len);
+               spin_unlock_bh(&session->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
@@ -790,6 +814,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) {
@@ -797,7 +822,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;
@@ -818,6 +844,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))
@@ -868,7 +896,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = {
        .cmd_per_lun            = ISCSI_DEF_CMD_PER_LUN,
        .eh_abort_handler       = iscsi_eh_abort,
        .eh_device_reset_handler= iscsi_eh_device_reset,
-       .eh_target_reset_handler= iscsi_eh_target_reset,
+       .eh_target_reset_handler = iscsi_eh_recover_target,
        .use_clustering         = DISABLE_CLUSTERING,
        .slave_alloc            = iscsi_sw_tcp_slave_alloc,
        .slave_configure        = iscsi_sw_tcp_slave_configure,
@@ -903,7 +931,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
                                  ISCSI_USERNAME | ISCSI_PASSWORD |
                                  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
                                  ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
-                                 ISCSI_LU_RESET_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 |
@@ -922,7 +950,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
        .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,