]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/cifs/transport.c
cifs: don't pop a printk when sending on a socket is interrupted
[mv-sheeva.git] / fs / cifs / transport.c
index 15059c7ef2ae076221e11ad5bf2e08e70fca15b0..b8c5e2eb43d0866d59d293afafc72873867a0e93 100644 (file)
 
 extern mempool_t *cifs_mid_poolp;
 
-static struct mid_q_entry *
+static void
+wake_up_task(struct mid_q_entry *mid)
+{
+       wake_up_process(mid->callback_data);
+}
+
+struct mid_q_entry *
 AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
 {
        struct mid_q_entry *temp;
@@ -58,7 +64,13 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
        /*      do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
                /* when mid allocated can be before when sent */
                temp->when_alloc = jiffies;
-               temp->tsk = current;
+
+               /*
+                * The default is for the mid to be synchronous, so the
+                * default callback just wakes up the current task.
+                */
+               temp->callback = wake_up_task;
+               temp->callback_data = current;
        }
 
        atomic_inc(&midCount);
@@ -66,7 +78,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
        return temp;
 }
 
-static void
+void
 DeleteMidQEntry(struct mid_q_entry *midEntry)
 {
 #ifdef CONFIG_CIFS_STATS2
@@ -224,9 +236,9 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
                server->tcpStatus = CifsNeedReconnect;
        }
 
-       if (rc < 0) {
+       if (rc < 0 && rc != -EINTR)
                cERROR(1, "Error %d sending data on socket to server", rc);
-       else
+       else
                rc = 0;
 
        /* Don't want to modify the buffer as a
@@ -318,50 +330,75 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,
        return 0;
 }
 
-static int wait_for_response(struct cifsSesInfo *ses,
-                       struct mid_q_entry *midQ,
-                       unsigned long timeout,
-                       unsigned long time_to_wait)
+static int
+wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
 {
-       unsigned long curr_timeout;
+       int error;
 
-       for (;;) {
-               curr_timeout = timeout + jiffies;
-               wait_event_timeout(ses->server->response_q,
-                       midQ->midState != MID_REQUEST_SUBMITTED, timeout);
+       error = wait_event_killable(server->response_q,
+                                   midQ->midState != MID_REQUEST_SUBMITTED);
+       if (error < 0)
+               return -ERESTARTSYS;
 
-               if (time_after(jiffies, curr_timeout) &&
-                       (midQ->midState == MID_REQUEST_SUBMITTED) &&
-                       ((ses->server->tcpStatus == CifsGood) ||
-                        (ses->server->tcpStatus == CifsNew))) {
+       return 0;
+}
 
-                       unsigned long lrt;
 
-                       /* We timed out. Is the server still
-                          sending replies ? */
-                       spin_lock(&GlobalMid_Lock);
-                       lrt = ses->server->lstrp;
-                       spin_unlock(&GlobalMid_Lock);
+/*
+ * Send a SMB request and set the callback function in the mid to handle
+ * the result. Caller is responsible for dealing with timeouts.
+ */
+int
+cifs_call_async(struct TCP_Server_Info *server, struct smb_hdr *in_buf,
+               mid_callback_t *callback, void *cbdata)
+{
+       int rc;
+       struct mid_q_entry *mid;
 
-                       /* Calculate time_to_wait past last receive time.
-                        Although we prefer not to time out if the
-                        server is still responding - we will time
-                        out if the server takes more than 15 (or 45
-                        or 180) seconds to respond to this request
-                        and has not responded to any request from
-                        other threads on the client within 10 seconds */
-                       lrt += time_to_wait;
-                       if (time_after(jiffies, lrt)) {
-                               /* No replies for time_to_wait. */
-                               cERROR(1, "server not responding");
-                               return -1;
-                       }
-               } else {
-                       return 0;
-               }
+       rc = wait_for_free_request(server, CIFS_ASYNC_OP);
+       if (rc)
+               return rc;
+
+       mutex_lock(&server->srv_mutex);
+       mid = AllocMidQEntry(in_buf, server);
+       if (mid == NULL) {
+               mutex_unlock(&server->srv_mutex);
+               return -ENOMEM;
        }
-}
 
+       /* put it on the pending_mid_q */
+       spin_lock(&GlobalMid_Lock);
+       list_add_tail(&mid->qhead, &server->pending_mid_q);
+       spin_unlock(&GlobalMid_Lock);
+
+       rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
+       if (rc) {
+               mutex_unlock(&server->srv_mutex);
+               goto out_err;
+       }
+
+       mid->callback = callback;
+       mid->callback_data = cbdata;
+       mid->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+       atomic_inc(&server->inSend);
+#endif
+       rc = smb_send(server, in_buf, in_buf->smb_buf_length);
+#ifdef CONFIG_CIFS_STATS2
+       atomic_dec(&server->inSend);
+       mid->when_sent = jiffies;
+#endif
+       mutex_unlock(&server->srv_mutex);
+       if (rc)
+               goto out_err;
+
+       return rc;
+out_err:
+       delete_mid(mid);
+       atomic_dec(&server->inFlight);
+       wake_up(&server->request_q);
+       return rc;
+}
 
 /*
  *
@@ -394,34 +431,73 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
 {
        int rc = 0;
 
+       cFYI(1, "%s: cmd=%d mid=%d state=%d", __func__, mid->command,
+               mid->mid, mid->midState);
+
        spin_lock(&GlobalMid_Lock);
+       /* ensure that it's no longer on the pending_mid_q */
+       list_del_init(&mid->qhead);
 
-       if (mid->resp_buf) {
+       switch (mid->midState) {
+       case MID_RESPONSE_RECEIVED:
                spin_unlock(&GlobalMid_Lock);
                return rc;
-       }
-
-       cERROR(1, "No response to cmd %d mid %d", mid->command, mid->mid);
-       if (mid->midState == MID_REQUEST_SUBMITTED) {
-               if (server->tcpStatus == CifsExiting)
+       case MID_REQUEST_SUBMITTED:
+               /* socket is going down, reject all calls */
+               if (server->tcpStatus == CifsExiting) {
+                       cERROR(1, "%s: canceling mid=%d cmd=0x%x state=%d",
+                              __func__, mid->mid, mid->command, mid->midState);
                        rc = -EHOSTDOWN;
-               else {
-                       server->tcpStatus = CifsNeedReconnect;
-                       mid->midState = MID_RETRY_NEEDED;
+                       break;
                }
+       case MID_RETRY_NEEDED:
+               rc = -EAGAIN;
+               break;
+       default:
+               cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__,
+                       mid->mid, mid->midState);
+               rc = -EIO;
        }
+       spin_unlock(&GlobalMid_Lock);
 
-       if (rc != -EHOSTDOWN) {
-               if (mid->midState == MID_RETRY_NEEDED) {
-                       rc = -EAGAIN;
-                       cFYI(1, "marking request for retry");
-               } else {
-                       rc = -EIO;
-               }
+       DeleteMidQEntry(mid);
+       return rc;
+}
+
+/*
+ * An NT cancel request header looks just like the original request except:
+ *
+ * The Command is SMB_COM_NT_CANCEL
+ * The WordCount is zeroed out
+ * The ByteCount is zeroed out
+ *
+ * This function mangles an existing request buffer into a
+ * SMB_COM_NT_CANCEL request and then sends it.
+ */
+static int
+send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf,
+               struct mid_q_entry *mid)
+{
+       int rc = 0;
+
+       /* -4 for RFC1001 length and +2 for BCC field */
+       in_buf->smb_buf_length = sizeof(struct smb_hdr) - 4  + 2;
+       in_buf->Command = SMB_COM_NT_CANCEL;
+       in_buf->WordCount = 0;
+       put_bcc_le(0, in_buf);
+
+       mutex_lock(&server->srv_mutex);
+       rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
+       if (rc) {
+               mutex_unlock(&server->srv_mutex);
+               return rc;
        }
-       spin_unlock(&GlobalMid_Lock);
+       rc = smb_send(server, in_buf, in_buf->smb_buf_length);
+       mutex_unlock(&server->srv_mutex);
+
+       cFYI(1, "issued NT_CANCEL for mid %u, rc = %d",
+               in_buf->Mid, rc);
 
-       delete_mid(mid);
        return rc;
 }
 
@@ -433,7 +509,6 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
        int rc = 0;
        int long_op;
        unsigned int receive_len;
-       unsigned long timeout;
        struct mid_q_entry *midQ;
        struct smb_hdr *in_buf = iov[0].iov_base;
 
@@ -495,38 +570,33 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
 #endif
 
        mutex_unlock(&ses->server->srv_mutex);
-       cifs_small_buf_release(in_buf);
 
-       if (rc < 0)
+       if (rc < 0) {
+               cifs_small_buf_release(in_buf);
                goto out;
+       }
 
-       if (long_op == CIFS_STD_OP)
-               timeout = 15 * HZ;
-       else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */
-               timeout = 180 * HZ;
-       else if (long_op == CIFS_LONG_OP)
-               timeout = 45 * HZ; /* should be greater than
-                       servers oplock break timeout (about 43 seconds) */
-       else if (long_op == CIFS_ASYNC_OP)
-               goto out;
-       else if (long_op == CIFS_BLOCKING_OP)
-               timeout = 0x7FFFFFFF; /*  large, but not so large as to wrap */
-       else {
-               cERROR(1, "unknown timeout flag %d", long_op);
-               rc = -EIO;
+       if (long_op == CIFS_ASYNC_OP) {
+               cifs_small_buf_release(in_buf);
                goto out;
        }
 
-       /* wait for 15 seconds or until woken up due to response arriving or
-          due to last connection to this server being unmounted */
-       if (signal_pending(current)) {
-               /* if signal pending do not hold up user for full smb timeout
-               but we still give response a chance to complete */
-               timeout = 2 * HZ;
+       rc = wait_for_response(ses->server, midQ);
+       if (rc != 0) {
+               send_nt_cancel(ses->server, in_buf, midQ);
+               spin_lock(&GlobalMid_Lock);
+               if (midQ->midState == MID_REQUEST_SUBMITTED) {
+                       midQ->callback = DeleteMidQEntry;
+                       spin_unlock(&GlobalMid_Lock);
+                       cifs_small_buf_release(in_buf);
+                       atomic_dec(&ses->server->inFlight);
+                       wake_up(&ses->server->request_q);
+                       return rc;
+               }
+               spin_unlock(&GlobalMid_Lock);
        }
 
-       /* No user interrupts in wait - wreaks havoc with performance */
-       wait_for_response(ses, midQ, timeout, 10 * HZ);
+       cifs_small_buf_release(in_buf);
 
        rc = sync_mid_result(midQ, ses->server);
        if (rc != 0) {
@@ -578,8 +648,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
                if (receive_len >= sizeof(struct smb_hdr) - 4
                    /* do not count RFC1001 header */  +
                    (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
-                       BCC(midQ->resp_buf) =
-                               le16_to_cpu(BCC_LE(midQ->resp_buf));
+                       put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf);
                if ((flags & CIFS_NO_RESP) == 0)
                        midQ->resp_buf = NULL;  /* mark it so buf will
                                                   not be freed by
@@ -604,7 +673,6 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
 {
        int rc = 0;
        unsigned int receive_len;
-       unsigned long timeout;
        struct mid_q_entry *midQ;
 
        if (ses == NULL) {
@@ -668,34 +736,24 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
        if (rc < 0)
                goto out;
 
-       if (long_op == CIFS_STD_OP)
-               timeout = 15 * HZ;
-       /* wait for 15 seconds or until woken up due to response arriving or
-          due to last connection to this server being unmounted */
-       else if (long_op == CIFS_ASYNC_OP)
+       if (long_op == CIFS_ASYNC_OP)
                goto out;
-       else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */
-               timeout = 180 * HZ;
-       else if (long_op == CIFS_LONG_OP)
-               timeout = 45 * HZ; /* should be greater than
-                       servers oplock break timeout (about 43 seconds) */
-       else if (long_op == CIFS_BLOCKING_OP)
-               timeout = 0x7FFFFFFF; /* large but no so large as to wrap */
-       else {
-               cERROR(1, "unknown timeout flag %d", long_op);
-               rc = -EIO;
-               goto out;
-       }
 
-       if (signal_pending(current)) {
-               /* if signal pending do not hold up user for full smb timeout
-               but we still give response a chance to complete */
-               timeout = 2 * HZ;
+       rc = wait_for_response(ses->server, midQ);
+       if (rc != 0) {
+               send_nt_cancel(ses->server, in_buf, midQ);
+               spin_lock(&GlobalMid_Lock);
+               if (midQ->midState == MID_REQUEST_SUBMITTED) {
+                       /* no longer considered to be "in-flight" */
+                       midQ->callback = DeleteMidQEntry;
+                       spin_unlock(&GlobalMid_Lock);
+                       atomic_dec(&ses->server->inFlight);
+                       wake_up(&ses->server->request_q);
+                       return rc;
+               }
+               spin_unlock(&GlobalMid_Lock);
        }
 
-       /* No user interrupts in wait - wreaks havoc with performance */
-       wait_for_response(ses, midQ, timeout, 10 * HZ);
-
        rc = sync_mid_result(midQ, ses->server);
        if (rc != 0) {
                atomic_dec(&ses->server->inFlight);
@@ -744,7 +802,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
                if (receive_len >= sizeof(struct smb_hdr) - 4
                    /* do not count RFC1001 header */  +
                    (2 * out_buf->WordCount) + 2 /* bcc */ )
-                       BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+                       put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf);
        } else {
                rc = -EIO;
                cERROR(1, "Bad MID state?");
@@ -758,29 +816,6 @@ out:
        return rc;
 }
 
-/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */
-
-static int
-send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf,
-               struct mid_q_entry *midQ)
-{
-       int rc = 0;
-       struct cifsSesInfo *ses = tcon->ses;
-       __u16 mid = in_buf->Mid;
-
-       header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0);
-       in_buf->Mid = mid;
-       mutex_lock(&ses->server->srv_mutex);
-       rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
-       if (rc) {
-               mutex_unlock(&ses->server->srv_mutex);
-               return rc;
-       }
-       rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length);
-       mutex_unlock(&ses->server->srv_mutex);
-       return rc;
-}
-
 /* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
    blocking lock to return. */
 
@@ -803,7 +838,7 @@ send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon,
        pSMB->hdr.Mid = GetNextMid(ses->server);
 
        return SendReceive(xid, ses, in_buf, out_buf,
-                       &bytes_returned, CIFS_STD_OP);
+                       &bytes_returned, 0);
 }
 
 int
@@ -895,8 +930,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
                if (in_buf->Command == SMB_COM_TRANSACTION2) {
                        /* POSIX lock. We send a NT_CANCEL SMB to cause the
                           blocking lock to return. */
-
-                       rc = send_nt_cancel(tcon, in_buf, midQ);
+                       rc = send_nt_cancel(ses->server, in_buf, midQ);
                        if (rc) {
                                delete_mid(midQ);
                                return rc;
@@ -915,11 +949,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
                        }
                }
 
-               /* Wait 5 seconds for the response. */
-               if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) {
-                       /* We got the response - restart system call. */
-                       rstart = 1;
+               rc = wait_for_response(ses->server, midQ);
+               if (rc) {
+                       send_nt_cancel(ses->server, in_buf, midQ);
+                       spin_lock(&GlobalMid_Lock);
+                       if (midQ->midState == MID_REQUEST_SUBMITTED) {
+                               /* no longer considered to be "in-flight" */
+                               midQ->callback = DeleteMidQEntry;
+                               spin_unlock(&GlobalMid_Lock);
+                               return rc;
+                       }
+                       spin_unlock(&GlobalMid_Lock);
                }
+
+               /* We got the response - restart system call. */
+               rstart = 1;
        }
 
        rc = sync_mid_result(midQ, ses->server);
@@ -970,7 +1014,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
        if (receive_len >= sizeof(struct smb_hdr) - 4
            /* do not count RFC1001 header */  +
            (2 * out_buf->WordCount) + 2 /* bcc */ )
-               BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+               put_bcc(get_bcc_le(out_buf), out_buf);
 
 out:
        delete_mid(midQ);