diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2012-02-06 06:59:18 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2012-03-21 12:35:03 -0400 |
commit | 2d86dbc97094ea4cfc2204fdefd7d07685496189 (patch) | |
tree | 9aee614e155fd837c78ded2cd083dead1a9d4a3f /fs/cifs | |
parent | fc40f9cf828908e91d9af820e9300a9d42fbbd72 (diff) |
CIFS: Introduce credit-based flow control
and send no more than credits value requests at once. For SMB/CIFS
it's trivial: increment this value by receiving any message and
decrement by sending one.
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsglob.h | 11 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 13 | ||||
-rw-r--r-- | fs/cifs/connect.c | 14 | ||||
-rw-r--r-- | fs/cifs/misc.c | 19 | ||||
-rw-r--r-- | fs/cifs/transport.c | 44 |
6 files changed, 60 insertions, 44 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index fb78bc903887..d55de9684df9 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -250,8 +250,9 @@ struct TCP_Server_Info { | |||
250 | bool noblocksnd; /* use blocking sendmsg */ | 250 | bool noblocksnd; /* use blocking sendmsg */ |
251 | bool noautotune; /* do not autotune send buf sizes */ | 251 | bool noautotune; /* do not autotune send buf sizes */ |
252 | bool tcp_nodelay; | 252 | bool tcp_nodelay; |
253 | int credits; /* send no more requests at once */ | ||
253 | unsigned int in_flight; /* number of requests on the wire to server */ | 254 | unsigned int in_flight; /* number of requests on the wire to server */ |
254 | spinlock_t req_lock; /* protect the value above */ | 255 | spinlock_t req_lock; /* protect the two values above */ |
255 | struct mutex srv_mutex; | 256 | struct mutex srv_mutex; |
256 | struct task_struct *tsk; | 257 | struct task_struct *tsk; |
257 | char server_GUID[16]; | 258 | char server_GUID[16]; |
@@ -314,12 +315,14 @@ in_flight(struct TCP_Server_Info *server) | |||
314 | return num; | 315 | return num; |
315 | } | 316 | } |
316 | 317 | ||
317 | static inline void | 318 | static inline bool |
318 | dec_in_flight(struct TCP_Server_Info *server) | 319 | has_credits(struct TCP_Server_Info *server) |
319 | { | 320 | { |
321 | int num; | ||
320 | spin_lock(&server->req_lock); | 322 | spin_lock(&server->req_lock); |
321 | server->in_flight--; | 323 | num = server->credits; |
322 | spin_unlock(&server->req_lock); | 324 | spin_unlock(&server->req_lock); |
325 | return num > 0; | ||
323 | } | 326 | } |
324 | 327 | ||
325 | /* | 328 | /* |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 6f4e243e0f62..47a769e535b1 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -88,6 +88,9 @@ extern int SendReceiveBlockingLock(const unsigned int xid, | |||
88 | struct smb_hdr *in_buf , | 88 | struct smb_hdr *in_buf , |
89 | struct smb_hdr *out_buf, | 89 | struct smb_hdr *out_buf, |
90 | int *bytes_returned); | 90 | int *bytes_returned); |
91 | extern void cifs_add_credits(struct TCP_Server_Info *server, | ||
92 | const unsigned int add); | ||
93 | extern void cifs_set_credits(struct TCP_Server_Info *server, const int val); | ||
91 | extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); | 94 | extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); |
92 | extern bool is_valid_oplock_break(struct smb_hdr *smb, | 95 | extern bool is_valid_oplock_break(struct smb_hdr *smb, |
93 | struct TCP_Server_Info *); | 96 | struct TCP_Server_Info *); |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index d7cbcfa21a0c..70aac35c398f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -461,7 +461,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses) | |||
461 | server->maxReq = min_t(unsigned int, | 461 | server->maxReq = min_t(unsigned int, |
462 | le16_to_cpu(rsp->MaxMpxCount), | 462 | le16_to_cpu(rsp->MaxMpxCount), |
463 | cifs_max_pending); | 463 | cifs_max_pending); |
464 | server->oplocks = server->maxReq > 1 ? enable_oplocks : false; | 464 | cifs_set_credits(server, server->maxReq); |
465 | server->maxBuf = le16_to_cpu(rsp->MaxBufSize); | 465 | server->maxBuf = le16_to_cpu(rsp->MaxBufSize); |
466 | server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); | 466 | server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); |
467 | /* even though we do not use raw we might as well set this | 467 | /* even though we do not use raw we might as well set this |
@@ -569,7 +569,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses) | |||
569 | little endian */ | 569 | little endian */ |
570 | server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), | 570 | server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), |
571 | cifs_max_pending); | 571 | cifs_max_pending); |
572 | server->oplocks = server->maxReq > 1 ? enable_oplocks : false; | 572 | cifs_set_credits(server, server->maxReq); |
573 | /* probably no need to store and check maxvcs */ | 573 | /* probably no need to store and check maxvcs */ |
574 | server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); | 574 | server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); |
575 | server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); | 575 | server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); |
@@ -721,8 +721,7 @@ cifs_echo_callback(struct mid_q_entry *mid) | |||
721 | struct TCP_Server_Info *server = mid->callback_data; | 721 | struct TCP_Server_Info *server = mid->callback_data; |
722 | 722 | ||
723 | DeleteMidQEntry(mid); | 723 | DeleteMidQEntry(mid); |
724 | dec_in_flight(server); | 724 | cifs_add_credits(server, 1); |
725 | wake_up(&server->request_q); | ||
726 | } | 725 | } |
727 | 726 | ||
728 | int | 727 | int |
@@ -1674,8 +1673,7 @@ cifs_readv_callback(struct mid_q_entry *mid) | |||
1674 | 1673 | ||
1675 | queue_work(system_nrt_wq, &rdata->work); | 1674 | queue_work(system_nrt_wq, &rdata->work); |
1676 | DeleteMidQEntry(mid); | 1675 | DeleteMidQEntry(mid); |
1677 | dec_in_flight(server); | 1676 | cifs_add_credits(server, 1); |
1678 | wake_up(&server->request_q); | ||
1679 | } | 1677 | } |
1680 | 1678 | ||
1681 | /* cifs_async_readv - send an async write, and set up mid to handle result */ | 1679 | /* cifs_async_readv - send an async write, and set up mid to handle result */ |
@@ -2115,8 +2113,7 @@ cifs_writev_callback(struct mid_q_entry *mid) | |||
2115 | 2113 | ||
2116 | queue_work(system_nrt_wq, &wdata->work); | 2114 | queue_work(system_nrt_wq, &wdata->work); |
2117 | DeleteMidQEntry(mid); | 2115 | DeleteMidQEntry(mid); |
2118 | dec_in_flight(tcon->ses->server); | 2116 | cifs_add_credits(tcon->ses->server, 1); |
2119 | wake_up(&tcon->ses->server->request_q); | ||
2120 | } | 2117 | } |
2121 | 2118 | ||
2122 | /* cifs_async_writev - send an async write, and set up mid to handle result */ | 2119 | /* cifs_async_writev - send an async write, and set up mid to handle result */ |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ed91abcce8a9..1d489010615b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -642,14 +642,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) | |||
642 | spin_unlock(&GlobalMid_Lock); | 642 | spin_unlock(&GlobalMid_Lock); |
643 | wake_up_all(&server->response_q); | 643 | wake_up_all(&server->response_q); |
644 | 644 | ||
645 | /* Check if we have blocked requests that need to free. */ | 645 | /* check if we have blocked requests that need to free */ |
646 | spin_lock(&server->req_lock); | 646 | spin_lock(&server->req_lock); |
647 | if (server->in_flight >= server->maxReq) | 647 | if (server->credits <= 0) |
648 | server->in_flight = server->maxReq - 1; | 648 | server->credits = 1; |
649 | /* | ||
650 | * We do not want to set the max_pending too low or we could end up | ||
651 | * with the counter going negative. | ||
652 | */ | ||
653 | spin_unlock(&server->req_lock); | 649 | spin_unlock(&server->req_lock); |
654 | /* | 650 | /* |
655 | * Although there should not be any requests blocked on this queue it | 651 | * Although there should not be any requests blocked on this queue it |
@@ -1906,7 +1902,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) | |||
1906 | tcp_ses->noautotune = volume_info->noautotune; | 1902 | tcp_ses->noautotune = volume_info->noautotune; |
1907 | tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; | 1903 | tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; |
1908 | tcp_ses->in_flight = 0; | 1904 | tcp_ses->in_flight = 0; |
1909 | tcp_ses->maxReq = 1; /* enough to send negotiate request */ | 1905 | tcp_ses->credits = 1; |
1910 | init_waitqueue_head(&tcp_ses->response_q); | 1906 | init_waitqueue_head(&tcp_ses->response_q); |
1911 | init_waitqueue_head(&tcp_ses->request_q); | 1907 | init_waitqueue_head(&tcp_ses->request_q); |
1912 | INIT_LIST_HEAD(&tcp_ses->pending_mid_q); | 1908 | INIT_LIST_HEAD(&tcp_ses->pending_mid_q); |
@@ -3757,9 +3753,11 @@ int cifs_negotiate_protocol(unsigned int xid, struct cifs_ses *ses) | |||
3757 | if (server->maxBuf != 0) | 3753 | if (server->maxBuf != 0) |
3758 | return 0; | 3754 | return 0; |
3759 | 3755 | ||
3756 | cifs_set_credits(server, 1); | ||
3760 | rc = CIFSSMBNegotiate(xid, ses); | 3757 | rc = CIFSSMBNegotiate(xid, ses); |
3761 | if (rc == -EAGAIN) { | 3758 | if (rc == -EAGAIN) { |
3762 | /* retry only once on 1st time connection */ | 3759 | /* retry only once on 1st time connection */ |
3760 | cifs_set_credits(server, 1); | ||
3763 | rc = CIFSSMBNegotiate(xid, ses); | 3761 | rc = CIFSSMBNegotiate(xid, ses); |
3764 | if (rc == -EAGAIN) | 3762 | if (rc == -EAGAIN) |
3765 | rc = -EHOSTDOWN; | 3763 | rc = -EHOSTDOWN; |
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 703ef5c6fdb1..c273c12de98e 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -690,3 +690,22 @@ backup_cred(struct cifs_sb_info *cifs_sb) | |||
690 | 690 | ||
691 | return false; | 691 | return false; |
692 | } | 692 | } |
693 | |||
694 | void | ||
695 | cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add) | ||
696 | { | ||
697 | spin_lock(&server->req_lock); | ||
698 | server->credits += add; | ||
699 | server->in_flight--; | ||
700 | spin_unlock(&server->req_lock); | ||
701 | wake_up(&server->request_q); | ||
702 | } | ||
703 | |||
704 | void | ||
705 | cifs_set_credits(struct TCP_Server_Info *server, const int val) | ||
706 | { | ||
707 | spin_lock(&server->req_lock); | ||
708 | server->credits = val; | ||
709 | server->oplocks = val > 1 ? enable_oplocks : false; | ||
710 | spin_unlock(&server->req_lock); | ||
711 | } | ||
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index e2673aa34381..e5202ddef2fb 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -262,16 +262,16 @@ wait_for_free_request(struct TCP_Server_Info *server, const int long_op) | |||
262 | if (long_op == CIFS_ASYNC_OP) { | 262 | if (long_op == CIFS_ASYNC_OP) { |
263 | /* oplock breaks must not be held up */ | 263 | /* oplock breaks must not be held up */ |
264 | server->in_flight++; | 264 | server->in_flight++; |
265 | server->credits--; | ||
265 | spin_unlock(&server->req_lock); | 266 | spin_unlock(&server->req_lock); |
266 | return 0; | 267 | return 0; |
267 | } | 268 | } |
268 | 269 | ||
269 | while (1) { | 270 | while (1) { |
270 | if (server->in_flight >= server->maxReq) { | 271 | if (server->credits <= 0) { |
271 | spin_unlock(&server->req_lock); | 272 | spin_unlock(&server->req_lock); |
272 | cifs_num_waiters_inc(server); | 273 | cifs_num_waiters_inc(server); |
273 | wait_event(server->request_q, | 274 | wait_event(server->request_q, has_credits(server)); |
274 | in_flight(server) < server->maxReq); | ||
275 | cifs_num_waiters_dec(server); | 275 | cifs_num_waiters_dec(server); |
276 | spin_lock(&server->req_lock); | 276 | spin_lock(&server->req_lock); |
277 | } else { | 277 | } else { |
@@ -280,12 +280,16 @@ wait_for_free_request(struct TCP_Server_Info *server, const int long_op) | |||
280 | return -ENOENT; | 280 | return -ENOENT; |
281 | } | 281 | } |
282 | 282 | ||
283 | /* can not count locking commands against total | 283 | /* |
284 | as they are allowed to block on server */ | 284 | * Can not count locking commands against total |
285 | * as they are allowed to block on server. | ||
286 | */ | ||
285 | 287 | ||
286 | /* update # of requests on the wire to server */ | 288 | /* update # of requests on the wire to server */ |
287 | if (long_op != CIFS_BLOCKING_OP) | 289 | if (long_op != CIFS_BLOCKING_OP) { |
290 | server->credits--; | ||
288 | server->in_flight++; | 291 | server->in_flight++; |
292 | } | ||
289 | spin_unlock(&server->req_lock); | 293 | spin_unlock(&server->req_lock); |
290 | break; | 294 | break; |
291 | } | 295 | } |
@@ -360,7 +364,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, | |||
360 | mid = AllocMidQEntry(hdr, server); | 364 | mid = AllocMidQEntry(hdr, server); |
361 | if (mid == NULL) { | 365 | if (mid == NULL) { |
362 | mutex_unlock(&server->srv_mutex); | 366 | mutex_unlock(&server->srv_mutex); |
363 | dec_in_flight(server); | 367 | cifs_add_credits(server, 1); |
364 | wake_up(&server->request_q); | 368 | wake_up(&server->request_q); |
365 | return -ENOMEM; | 369 | return -ENOMEM; |
366 | } | 370 | } |
@@ -393,7 +397,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, | |||
393 | return rc; | 397 | return rc; |
394 | out_err: | 398 | out_err: |
395 | delete_mid(mid); | 399 | delete_mid(mid); |
396 | dec_in_flight(server); | 400 | cifs_add_credits(server, 1); |
397 | wake_up(&server->request_q); | 401 | wake_up(&server->request_q); |
398 | return rc; | 402 | return rc; |
399 | } | 403 | } |
@@ -565,8 +569,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
565 | mutex_unlock(&ses->server->srv_mutex); | 569 | mutex_unlock(&ses->server->srv_mutex); |
566 | cifs_small_buf_release(in_buf); | 570 | cifs_small_buf_release(in_buf); |
567 | /* Update # of requests on wire to server */ | 571 | /* Update # of requests on wire to server */ |
568 | dec_in_flight(ses->server); | 572 | cifs_add_credits(ses->server, 1); |
569 | wake_up(&ses->server->request_q); | ||
570 | return rc; | 573 | return rc; |
571 | } | 574 | } |
572 | rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); | 575 | rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); |
@@ -602,8 +605,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
602 | midQ->callback = DeleteMidQEntry; | 605 | midQ->callback = DeleteMidQEntry; |
603 | spin_unlock(&GlobalMid_Lock); | 606 | spin_unlock(&GlobalMid_Lock); |
604 | cifs_small_buf_release(in_buf); | 607 | cifs_small_buf_release(in_buf); |
605 | dec_in_flight(ses->server); | 608 | cifs_add_credits(ses->server, 1); |
606 | wake_up(&ses->server->request_q); | ||
607 | return rc; | 609 | return rc; |
608 | } | 610 | } |
609 | spin_unlock(&GlobalMid_Lock); | 611 | spin_unlock(&GlobalMid_Lock); |
@@ -613,8 +615,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
613 | 615 | ||
614 | rc = cifs_sync_mid_result(midQ, ses->server); | 616 | rc = cifs_sync_mid_result(midQ, ses->server); |
615 | if (rc != 0) { | 617 | if (rc != 0) { |
616 | dec_in_flight(ses->server); | 618 | cifs_add_credits(ses->server, 1); |
617 | wake_up(&ses->server->request_q); | ||
618 | return rc; | 619 | return rc; |
619 | } | 620 | } |
620 | 621 | ||
@@ -638,8 +639,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
638 | midQ->resp_buf = NULL; | 639 | midQ->resp_buf = NULL; |
639 | out: | 640 | out: |
640 | delete_mid(midQ); | 641 | delete_mid(midQ); |
641 | dec_in_flight(ses->server); | 642 | cifs_add_credits(ses->server, 1); |
642 | wake_up(&ses->server->request_q); | ||
643 | 643 | ||
644 | return rc; | 644 | return rc; |
645 | } | 645 | } |
@@ -689,8 +689,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, | |||
689 | if (rc) { | 689 | if (rc) { |
690 | mutex_unlock(&ses->server->srv_mutex); | 690 | mutex_unlock(&ses->server->srv_mutex); |
691 | /* Update # of requests on wire to server */ | 691 | /* Update # of requests on wire to server */ |
692 | dec_in_flight(ses->server); | 692 | cifs_add_credits(ses->server, 1); |
693 | wake_up(&ses->server->request_q); | ||
694 | return rc; | 693 | return rc; |
695 | } | 694 | } |
696 | 695 | ||
@@ -722,8 +721,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, | |||
722 | /* no longer considered to be "in-flight" */ | 721 | /* no longer considered to be "in-flight" */ |
723 | midQ->callback = DeleteMidQEntry; | 722 | midQ->callback = DeleteMidQEntry; |
724 | spin_unlock(&GlobalMid_Lock); | 723 | spin_unlock(&GlobalMid_Lock); |
725 | dec_in_flight(ses->server); | 724 | cifs_add_credits(ses->server, 1); |
726 | wake_up(&ses->server->request_q); | ||
727 | return rc; | 725 | return rc; |
728 | } | 726 | } |
729 | spin_unlock(&GlobalMid_Lock); | 727 | spin_unlock(&GlobalMid_Lock); |
@@ -731,8 +729,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, | |||
731 | 729 | ||
732 | rc = cifs_sync_mid_result(midQ, ses->server); | 730 | rc = cifs_sync_mid_result(midQ, ses->server); |
733 | if (rc != 0) { | 731 | if (rc != 0) { |
734 | dec_in_flight(ses->server); | 732 | cifs_add_credits(ses->server, 1); |
735 | wake_up(&ses->server->request_q); | ||
736 | return rc; | 733 | return rc; |
737 | } | 734 | } |
738 | 735 | ||
@@ -748,8 +745,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, | |||
748 | rc = cifs_check_receive(midQ, ses->server, 0); | 745 | rc = cifs_check_receive(midQ, ses->server, 0); |
749 | out: | 746 | out: |
750 | delete_mid(midQ); | 747 | delete_mid(midQ); |
751 | dec_in_flight(ses->server); | 748 | cifs_add_credits(ses->server, 1); |
752 | wake_up(&ses->server->request_q); | ||
753 | 749 | ||
754 | return rc; | 750 | return rc; |
755 | } | 751 | } |