diff options
author | Jeff Layton <jlayton@redhat.com> | 2011-05-22 07:09:13 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2011-05-23 23:11:33 -0400 |
commit | 3c1105df699188a70f5c17dc0795affea388bca7 (patch) | |
tree | bfbcfaaae2fdfd16b2fcdb57710b9affbf183c49 | |
parent | 724d9f1cfba0cb16a7151333b501e8f7885450d8 (diff) |
cifs: don't call mid_q_entry->callback under the Global_MidLock (try #5)
Minor revision to the last version of this patch -- the only difference
is the fix to the cFYI statement in cifs_reconnect.
Holding the spinlock while we call this function means that it can't
sleep, which really limits what it can do. Taking it out from under
the spinlock also means less contention for this global lock.
Change the semantics such that the Global_MidLock is not held when
the callback is called. To do this requires that we take extra care
not to have sync_mid_result remove the mid from the list when the
mid is in a state where that has already happened. This prevents
list corruption when the mid is sitting on a private list for
reconnect or when cifsd is coming down.
Reviewed-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r-- | fs/cifs/cifsglob.h | 6 | ||||
-rw-r--r-- | fs/cifs/connect.c | 30 | ||||
-rw-r--r-- | fs/cifs/transport.c | 23 |
3 files changed, 35 insertions, 24 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 76b4517e74b..fd877c110e4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -543,9 +543,8 @@ struct mid_q_entry; | |||
543 | * This is the prototype for the mid callback function. When creating one, | 543 | * This is the prototype for the mid callback function. When creating one, |
544 | * take special care to avoid deadlocks. Things to bear in mind: | 544 | * take special care to avoid deadlocks. Things to bear in mind: |
545 | * | 545 | * |
546 | * - it will be called by cifsd | 546 | * - it will be called by cifsd, with no locks held |
547 | * - the GlobalMid_Lock will be held | 547 | * - the mid will be removed from any lists |
548 | * - the mid will be removed from the pending_mid_q list | ||
549 | */ | 548 | */ |
550 | typedef void (mid_callback_t)(struct mid_q_entry *mid); | 549 | typedef void (mid_callback_t)(struct mid_q_entry *mid); |
551 | 550 | ||
@@ -656,6 +655,7 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, | |||
656 | #define MID_RESPONSE_RECEIVED 4 | 655 | #define MID_RESPONSE_RECEIVED 4 |
657 | #define MID_RETRY_NEEDED 8 /* session closed while this request out */ | 656 | #define MID_RETRY_NEEDED 8 /* session closed while this request out */ |
658 | #define MID_RESPONSE_MALFORMED 0x10 | 657 | #define MID_RESPONSE_MALFORMED 0x10 |
658 | #define MID_SHUTDOWN 0x20 | ||
659 | 659 | ||
660 | /* Types of response buffer returned from SendReceive2 */ | 660 | /* Types of response buffer returned from SendReceive2 */ |
661 | #define CIFS_NO_BUFFER 0 /* Response buffer not returned */ | 661 | #define CIFS_NO_BUFFER 0 /* Response buffer not returned */ |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 273cf42b291..6070ba69647 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -137,6 +137,7 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
137 | struct cifsSesInfo *ses; | 137 | struct cifsSesInfo *ses; |
138 | struct cifsTconInfo *tcon; | 138 | struct cifsTconInfo *tcon; |
139 | struct mid_q_entry *mid_entry; | 139 | struct mid_q_entry *mid_entry; |
140 | struct list_head retry_list; | ||
140 | 141 | ||
141 | spin_lock(&GlobalMid_Lock); | 142 | spin_lock(&GlobalMid_Lock); |
142 | if (server->tcpStatus == CifsExiting) { | 143 | if (server->tcpStatus == CifsExiting) { |
@@ -188,16 +189,23 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
188 | mutex_unlock(&server->srv_mutex); | 189 | mutex_unlock(&server->srv_mutex); |
189 | 190 | ||
190 | /* mark submitted MIDs for retry and issue callback */ | 191 | /* mark submitted MIDs for retry and issue callback */ |
191 | cFYI(1, "%s: issuing mid callbacks", __func__); | 192 | INIT_LIST_HEAD(&retry_list); |
193 | cFYI(1, "%s: moving mids to private list", __func__); | ||
192 | spin_lock(&GlobalMid_Lock); | 194 | spin_lock(&GlobalMid_Lock); |
193 | list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { | 195 | list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { |
194 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); | 196 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); |
195 | if (mid_entry->midState == MID_REQUEST_SUBMITTED) | 197 | if (mid_entry->midState == MID_REQUEST_SUBMITTED) |
196 | mid_entry->midState = MID_RETRY_NEEDED; | 198 | mid_entry->midState = MID_RETRY_NEEDED; |
199 | list_move(&mid_entry->qhead, &retry_list); | ||
200 | } | ||
201 | spin_unlock(&GlobalMid_Lock); | ||
202 | |||
203 | cFYI(1, "%s: issuing mid callbacks", __func__); | ||
204 | list_for_each_safe(tmp, tmp2, &retry_list) { | ||
205 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); | ||
197 | list_del_init(&mid_entry->qhead); | 206 | list_del_init(&mid_entry->qhead); |
198 | mid_entry->callback(mid_entry); | 207 | mid_entry->callback(mid_entry); |
199 | } | 208 | } |
200 | spin_unlock(&GlobalMid_Lock); | ||
201 | 209 | ||
202 | while (server->tcpStatus == CifsNeedReconnect) { | 210 | while (server->tcpStatus == CifsNeedReconnect) { |
203 | try_to_freeze(); | 211 | try_to_freeze(); |
@@ -671,12 +679,12 @@ multi_t2_fnd: | |||
671 | mid_entry->when_received = jiffies; | 679 | mid_entry->when_received = jiffies; |
672 | #endif | 680 | #endif |
673 | list_del_init(&mid_entry->qhead); | 681 | list_del_init(&mid_entry->qhead); |
674 | mid_entry->callback(mid_entry); | ||
675 | break; | 682 | break; |
676 | } | 683 | } |
677 | spin_unlock(&GlobalMid_Lock); | 684 | spin_unlock(&GlobalMid_Lock); |
678 | 685 | ||
679 | if (mid_entry != NULL) { | 686 | if (mid_entry != NULL) { |
687 | mid_entry->callback(mid_entry); | ||
680 | /* Was previous buf put in mpx struct for multi-rsp? */ | 688 | /* Was previous buf put in mpx struct for multi-rsp? */ |
681 | if (!isMultiRsp) { | 689 | if (!isMultiRsp) { |
682 | /* smb buffer will be freed by user thread */ | 690 | /* smb buffer will be freed by user thread */ |
@@ -740,15 +748,25 @@ multi_t2_fnd: | |||
740 | cifs_small_buf_release(smallbuf); | 748 | cifs_small_buf_release(smallbuf); |
741 | 749 | ||
742 | if (!list_empty(&server->pending_mid_q)) { | 750 | if (!list_empty(&server->pending_mid_q)) { |
751 | struct list_head dispose_list; | ||
752 | |||
753 | INIT_LIST_HEAD(&dispose_list); | ||
743 | spin_lock(&GlobalMid_Lock); | 754 | spin_lock(&GlobalMid_Lock); |
744 | list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { | 755 | list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { |
745 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); | 756 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); |
746 | cFYI(1, "Clearing Mid 0x%x - issuing callback", | 757 | cFYI(1, "Clearing mid 0x%x", mid_entry->mid); |
747 | mid_entry->mid); | 758 | mid_entry->midState = MID_SHUTDOWN; |
759 | list_move(&mid_entry->qhead, &dispose_list); | ||
760 | } | ||
761 | spin_unlock(&GlobalMid_Lock); | ||
762 | |||
763 | /* now walk dispose list and issue callbacks */ | ||
764 | list_for_each_safe(tmp, tmp2, &dispose_list) { | ||
765 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); | ||
766 | cFYI(1, "Callback mid 0x%x", mid_entry->mid); | ||
748 | list_del_init(&mid_entry->qhead); | 767 | list_del_init(&mid_entry->qhead); |
749 | mid_entry->callback(mid_entry); | 768 | mid_entry->callback(mid_entry); |
750 | } | 769 | } |
751 | spin_unlock(&GlobalMid_Lock); | ||
752 | /* 1/8th of sec is more than enough time for them to exit */ | 770 | /* 1/8th of sec is more than enough time for them to exit */ |
753 | msleep(125); | 771 | msleep(125); |
754 | } | 772 | } |
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 16bcc0725ce..d1998b6086e 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -426,7 +426,7 @@ SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses, | |||
426 | } | 426 | } |
427 | 427 | ||
428 | static int | 428 | static int |
429 | sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) | 429 | cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) |
430 | { | 430 | { |
431 | int rc = 0; | 431 | int rc = 0; |
432 | 432 | ||
@@ -434,28 +434,21 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) | |||
434 | mid->mid, mid->midState); | 434 | mid->mid, mid->midState); |
435 | 435 | ||
436 | spin_lock(&GlobalMid_Lock); | 436 | spin_lock(&GlobalMid_Lock); |
437 | /* ensure that it's no longer on the pending_mid_q */ | ||
438 | list_del_init(&mid->qhead); | ||
439 | |||
440 | switch (mid->midState) { | 437 | switch (mid->midState) { |
441 | case MID_RESPONSE_RECEIVED: | 438 | case MID_RESPONSE_RECEIVED: |
442 | spin_unlock(&GlobalMid_Lock); | 439 | spin_unlock(&GlobalMid_Lock); |
443 | return rc; | 440 | return rc; |
444 | case MID_REQUEST_SUBMITTED: | ||
445 | /* socket is going down, reject all calls */ | ||
446 | if (server->tcpStatus == CifsExiting) { | ||
447 | cERROR(1, "%s: canceling mid=%d cmd=0x%x state=%d", | ||
448 | __func__, mid->mid, mid->command, mid->midState); | ||
449 | rc = -EHOSTDOWN; | ||
450 | break; | ||
451 | } | ||
452 | case MID_RETRY_NEEDED: | 441 | case MID_RETRY_NEEDED: |
453 | rc = -EAGAIN; | 442 | rc = -EAGAIN; |
454 | break; | 443 | break; |
455 | case MID_RESPONSE_MALFORMED: | 444 | case MID_RESPONSE_MALFORMED: |
456 | rc = -EIO; | 445 | rc = -EIO; |
457 | break; | 446 | break; |
447 | case MID_SHUTDOWN: | ||
448 | rc = -EHOSTDOWN; | ||
449 | break; | ||
458 | default: | 450 | default: |
451 | list_del_init(&mid->qhead); | ||
459 | cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__, | 452 | cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__, |
460 | mid->mid, mid->midState); | 453 | mid->mid, mid->midState); |
461 | rc = -EIO; | 454 | rc = -EIO; |
@@ -618,7 +611,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, | |||
618 | 611 | ||
619 | cifs_small_buf_release(in_buf); | 612 | cifs_small_buf_release(in_buf); |
620 | 613 | ||
621 | rc = sync_mid_result(midQ, ses->server); | 614 | rc = cifs_sync_mid_result(midQ, ses->server); |
622 | if (rc != 0) { | 615 | if (rc != 0) { |
623 | atomic_dec(&ses->server->inFlight); | 616 | atomic_dec(&ses->server->inFlight); |
624 | wake_up(&ses->server->request_q); | 617 | wake_up(&ses->server->request_q); |
@@ -739,7 +732,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, | |||
739 | spin_unlock(&GlobalMid_Lock); | 732 | spin_unlock(&GlobalMid_Lock); |
740 | } | 733 | } |
741 | 734 | ||
742 | rc = sync_mid_result(midQ, ses->server); | 735 | rc = cifs_sync_mid_result(midQ, ses->server); |
743 | if (rc != 0) { | 736 | if (rc != 0) { |
744 | atomic_dec(&ses->server->inFlight); | 737 | atomic_dec(&ses->server->inFlight); |
745 | wake_up(&ses->server->request_q); | 738 | wake_up(&ses->server->request_q); |
@@ -914,7 +907,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, | |||
914 | rstart = 1; | 907 | rstart = 1; |
915 | } | 908 | } |
916 | 909 | ||
917 | rc = sync_mid_result(midQ, ses->server); | 910 | rc = cifs_sync_mid_result(midQ, ses->server); |
918 | if (rc != 0) | 911 | if (rc != 0) |
919 | return rc; | 912 | return rc; |
920 | 913 | ||