config: tegra3: enable /dev mount with ACL
[linux-2.6.git] / drivers / scsi / iscsi_tcp.c
index fec47de..7724414 100644 (file)
@@ -107,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)) {
@@ -123,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.
@@ -141,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)
@@ -157,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);
@@ -178,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);
 }
@@ -295,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)
@@ -592,61 +616,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 +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;
@@ -670,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;
@@ -752,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
@@ -797,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) {
@@ -804,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;
@@ -825,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))
@@ -929,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,