to make sure we don't exceed the number of credits we have in this case.

Signed-off-by: Pavel Shilovsky <[email protected]>
---
 fs/cifs/cifsglob.h  |    6 ++++-
 fs/cifs/cifssmb.c   |    6 ++--
 fs/cifs/connect.c   |    1 +
 fs/cifs/transport.c |   57 +++++++++++++++++++++++++++++++++-----------------
 4 files changed, 46 insertions(+), 24 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 35d3df4..fa1a9f9 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -257,6 +257,7 @@ struct TCP_Server_Info {
        bool tcp_nodelay;
        int credits;  /* send no more requests at once */
        unsigned int in_flight;  /* number of requests on the wire to server */
+       unsigned int block_locks;  /* number of oustanding blocking locks */
        spinlock_t req_lock;  /* protect the two values above */
        struct mutex srv_mutex;
        struct task_struct *tsk;
@@ -330,11 +331,14 @@ get_credits(struct TCP_Server_Info *server)
 }
 
 static inline void
-add_credits(struct TCP_Server_Info *server, const unsigned int add)
+add_credits(struct TCP_Server_Info *server, const unsigned int add, int optype)
 {
        spin_lock(&server->req_lock);
        server->credits += add;
        server->in_flight--;
+       /* blocking lock case */
+       if (optype == 1)
+               server->block_locks--;
        spin_unlock(&server->req_lock);
 }
 
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index c255c25..b1040c7 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -716,7 +716,7 @@ cifs_echo_callback(struct mid_q_entry *mid)
        struct TCP_Server_Info *server = mid->callback_data;
 
        DeleteMidQEntry(mid);
-       add_credits(server, 1);
+       add_credits(server, 1, 0);
        wake_up(&server->request_q);
 }
 
@@ -1669,7 +1669,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
 
        queue_work(system_nrt_wq, &rdata->work);
        DeleteMidQEntry(mid);
-       add_credits(server, 1);
+       add_credits(server, 1, 0);
        wake_up(&server->request_q);
 }
 
@@ -2110,7 +2110,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
 
        queue_work(system_nrt_wq, &wdata->work);
        DeleteMidQEntry(mid);
-       add_credits(tcon->ses->server, 1);
+       add_credits(tcon->ses->server, 1, 0);
        wake_up(&tcon->ses->server->request_q);
 }
 
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 673813f..708be6c 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1903,6 +1903,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay;
        tcp_ses->in_flight = 0;
        tcp_ses->credits = cifs_max_pending;
+       tcp_ses->block_locks = 0;
        init_waitqueue_head(&tcp_ses->response_q);
        init_waitqueue_head(&tcp_ses->request_q);
        INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 44ac0aa..5fab0f1 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -267,6 +267,11 @@ wait_for_free_request(struct TCP_Server_Info *server, 
const int long_op)
                return 0;
        }
 
+       /* we can't leave the client without available credits */
+       if (long_op == CIFS_BLOCKING_OP &&
+           (server->block_locks >= (server->in_flight + server->credits) / 2))
+               return -ENOLCK;
+
        while (1) {
                if (server->credits <= 0) {
                        spin_unlock(&server->req_lock);
@@ -279,15 +284,10 @@ wait_for_free_request(struct TCP_Server_Info *server, 
const int long_op)
                                spin_unlock(&server->req_lock);
                                return -ENOENT;
                        }
-
-                       /* can not count locking commands against total
-                          as they are allowed to block on server */
-
-                       /* update # of requests on the wire to server */
-                       if (long_op != CIFS_BLOCKING_OP) {
-                               server->credits--;
-                               server->in_flight++;
-                       }
+                       server->credits--;
+                       server->in_flight++;
+                       if (long_op == CIFS_BLOCKING_OP)
+                               server->block_locks++;
                        spin_unlock(&server->req_lock);
                        break;
                }
@@ -362,7 +362,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec 
*iov,
        mid = AllocMidQEntry(hdr, server);
        if (mid == NULL) {
                mutex_unlock(&server->srv_mutex);
-               add_credits(server, 1);
+               add_credits(server, 1, 0);
                wake_up(&server->request_q);
                return -ENOMEM;
        }
@@ -395,7 +395,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec 
*iov,
        return rc;
 out_err:
        delete_mid(mid);
-       add_credits(server, 1);
+       add_credits(server, 1, 0);
        wake_up(&server->request_q);
        return rc;
 }
@@ -567,7 +567,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
                mutex_unlock(&ses->server->srv_mutex);
                cifs_small_buf_release(in_buf);
                /* Update # of requests on wire to server */
-               add_credits(ses->server, 1);
+               add_credits(ses->server, 1, 0);
                wake_up(&ses->server->request_q);
                return rc;
        }
@@ -604,7 +604,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
                        cifs_small_buf_release(in_buf);
-                       add_credits(ses->server, 1);
+                       add_credits(ses->server, 1, 0);
                        wake_up(&ses->server->request_q);
                        return rc;
                }
@@ -615,7 +615,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
 
        rc = cifs_sync_mid_result(midQ, ses->server);
        if (rc != 0) {
-               add_credits(ses->server, 1);
+               add_credits(ses->server, 1, 0);
                wake_up(&ses->server->request_q);
                return rc;
        }
@@ -640,7 +640,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
                midQ->resp_buf = NULL;
 out:
        delete_mid(midQ);
-       add_credits(ses->server, 1);
+       add_credits(ses->server, 1, 0);
        wake_up(&ses->server->request_q);
 
        return rc;
@@ -691,7 +691,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        if (rc) {
                mutex_unlock(&ses->server->srv_mutex);
                /* Update # of requests on wire to server */
-               add_credits(ses->server, 1);
+               add_credits(ses->server, 1, 0);
                wake_up(&ses->server->request_q);
                return rc;
        }
@@ -724,7 +724,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
                        /* no longer considered to be "in-flight" */
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
-                       add_credits(ses->server, 1);
+                       add_credits(ses->server, 1, 0);
                        wake_up(&ses->server->request_q);
                        return rc;
                }
@@ -733,7 +733,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
 
        rc = cifs_sync_mid_result(midQ, ses->server);
        if (rc != 0) {
-               add_credits(ses->server, 1);
+               add_credits(ses->server, 1, 0);
                wake_up(&ses->server->request_q);
                return rc;
        }
@@ -750,7 +750,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        rc = cifs_check_receive(midQ, ses->server, 0);
 out:
        delete_mid(midQ);
-       add_credits(ses->server, 1);
+       add_credits(ses->server, 1, 0);
        wake_up(&ses->server->request_q);
 
        return rc;
@@ -829,6 +829,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
        rc = allocate_mid(ses, in_buf, &midQ);
        if (rc) {
                mutex_unlock(&ses->server->srv_mutex);
+               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+               wake_up(&ses->server->request_q);
                return rc;
        }
 
@@ -836,6 +838,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
        if (rc) {
                delete_mid(midQ);
                mutex_unlock(&ses->server->srv_mutex);
+               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+               wake_up(&ses->server->request_q);
                return rc;
        }
 
@@ -848,6 +852,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
 
        if (rc < 0) {
                delete_mid(midQ);
+               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+               wake_up(&ses->server->request_q);
                return rc;
        }
 
@@ -869,6 +875,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
                        rc = send_nt_cancel(ses->server, in_buf, midQ);
                        if (rc) {
                                delete_mid(midQ);
+                               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+                               wake_up(&ses->server->request_q);
                                return rc;
                        }
                } else {
@@ -881,6 +889,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
                           already been removed. Don't exit in this case. */
                        if (rc && rc != -ENOLCK) {
                                delete_mid(midQ);
+                               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+                               wake_up(&ses->server->request_q);
                                return rc;
                        }
                }
@@ -893,6 +903,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
                                /* no longer considered to be "in-flight" */
                                midQ->callback = DeleteMidQEntry;
                                spin_unlock(&GlobalMid_Lock);
+                               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+                               wake_up(&ses->server->request_q);
                                return rc;
                        }
                        spin_unlock(&GlobalMid_Lock);
@@ -903,8 +915,11 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
        }
 
        rc = cifs_sync_mid_result(midQ, ses->server);
-       if (rc != 0)
+       if (rc != 0) {
+               add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+               wake_up(&ses->server->request_q);
                return rc;
+       }
 
        /* rcvd frame is ok */
        if (out_buf == NULL || midQ->midState != MID_RESPONSE_RECEIVED) {
@@ -918,6 +933,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct 
cifs_tcon *tcon,
        rc = cifs_check_receive(midQ, ses->server, 0);
 out:
        delete_mid(midQ);
+       add_credits(ses->server, 1, CIFS_BLOCKING_OP);
+       wake_up(&ses->server->request_q);
        if (rstart && rc == -EACCES)
                return -ERESTARTSYS;
        return rc;
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to