From 2d86dbc97094ea4cfc2204fdefd7d07685496189 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 6 Feb 2012 15:59:18 +0400 Subject: 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 Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/misc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'fs/cifs/misc.c') 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) return false; } + +void +cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add) +{ + spin_lock(&server->req_lock); + server->credits += add; + server->in_flight--; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); +} + +void +cifs_set_credits(struct TCP_Server_Info *server, const int val) +{ + spin_lock(&server->req_lock); + server->credits = val; + server->oplocks = val > 1 ? enable_oplocks : false; + spin_unlock(&server->req_lock); +} -- cgit v1.2.3 From 792af7b05b8a78def080ec757a4d4420b9fd0cc2 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:02 -0400 Subject: CIFS: Separate protocol-specific code from transport routines that lets us use this functions for SMB2. Signed-off-by: Pavel Shilovsky --- fs/cifs/misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs/cifs/misc.c') diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index c273c12de98e..e88601fc6f22 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -604,16 +604,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) } void -dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) +dump_smb(void *buf, int smb_buf_length) { int i, j; char debug_line[17]; - unsigned char *buffer; + unsigned char *buffer = buf; if (traceSMB == 0) return; - buffer = (unsigned char *) smb_buf; for (i = 0, j = 0; i < smb_buf_length; i++, j++) { if (i % 8 == 0) { /* have reached the beginning of line */ -- cgit v1.2.3 From d4e4854fd1c85ac8ba4d6de39703e07704754b85 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:02 -0400 Subject: CIFS: Separate protocol-specific code from demultiplex code Signed-off-by: Pavel Shilovsky --- fs/cifs/misc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/cifs/misc.c') diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index e88601fc6f22..dc61dff2c42a 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -420,8 +420,10 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid) } int -checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read) +checkSMB(char *buf, unsigned int total_read) { + struct smb_hdr *smb = (struct smb_hdr *)buf; + __u16 mid = smb->Mid; __u32 rfclen = be32_to_cpu(smb->smb_buf_length); __u32 clc_len; /* calculated length */ cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", @@ -502,8 +504,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read) } bool -is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) +is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) { + struct smb_hdr *buf = (struct smb_hdr *)buffer; struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct list_head *tmp, *tmp1, *tmp2; struct cifs_ses *ses; -- cgit v1.2.3 From 243d04b6e6de7fd08578fffd28b890c0200a2ca5 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:03 -0400 Subject: CIFS: Expand CurrentMid field While in CIFS/SMB we have 16 bit mid, in SMB2 it is 64 bit. Convert the existing field to 64 bit and mask off higher bits for CIFS/SMB. Signed-off-by: Pavel Shilovsky --- fs/cifs/misc.c | 84 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 38 deletions(-) (limited to 'fs/cifs/misc.c') diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index dc61dff2c42a..d2ccce89062f 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -213,54 +213,61 @@ cifs_small_buf_release(void *buf_to_free) } /* - Find a free multiplex id (SMB mid). Otherwise there could be - mid collisions which might cause problems, demultiplexing the - wrong response to this request. Multiplex ids could collide if - one of a series requests takes much longer than the others, or - if a very large number of long lived requests (byte range - locks or FindNotify requests) are pending. No more than - 64K-1 requests can be outstanding at one time. If no - mids are available, return zero. A future optimization - could make the combination of mids and uid the key we use - to demultiplex on (rather than mid alone). - In addition to the above check, the cifs demultiplex - code already used the command code as a secondary - check of the frame and if signing is negotiated the - response would be discarded if the mid were the same - but the signature was wrong. Since the mid is not put in the - pending queue until later (when it is about to be dispatched) - we do have to limit the number of outstanding requests - to somewhat less than 64K-1 although it is hard to imagine - so many threads being in the vfs at one time. -*/ -__u16 GetNextMid(struct TCP_Server_Info *server) + * Find a free multiplex id (SMB mid). Otherwise there could be + * mid collisions which might cause problems, demultiplexing the + * wrong response to this request. Multiplex ids could collide if + * one of a series requests takes much longer than the others, or + * if a very large number of long lived requests (byte range + * locks or FindNotify requests) are pending. No more than + * 64K-1 requests can be outstanding at one time. If no + * mids are available, return zero. A future optimization + * could make the combination of mids and uid the key we use + * to demultiplex on (rather than mid alone). + * In addition to the above check, the cifs demultiplex + * code already used the command code as a secondary + * check of the frame and if signing is negotiated the + * response would be discarded if the mid were the same + * but the signature was wrong. Since the mid is not put in the + * pending queue until later (when it is about to be dispatched) + * we do have to limit the number of outstanding requests + * to somewhat less than 64K-1 although it is hard to imagine + * so many threads being in the vfs at one time. + */ +__u64 GetNextMid(struct TCP_Server_Info *server) { - __u16 mid = 0; - __u16 last_mid; + __u64 mid = 0; + __u16 last_mid, cur_mid; bool collision; spin_lock(&GlobalMid_Lock); - last_mid = server->CurrentMid; /* we do not want to loop forever */ - server->CurrentMid++; - /* This nested loop looks more expensive than it is. - In practice the list of pending requests is short, - fewer than 50, and the mids are likely to be unique - on the first pass through the loop unless some request - takes longer than the 64 thousand requests before it - (and it would also have to have been a request that - did not time out) */ - while (server->CurrentMid != last_mid) { + + /* mid is 16 bit only for CIFS/SMB */ + cur_mid = (__u16)((server->CurrentMid) & 0xffff); + /* we do not want to loop forever */ + last_mid = cur_mid; + cur_mid++; + + /* + * This nested loop looks more expensive than it is. + * In practice the list of pending requests is short, + * fewer than 50, and the mids are likely to be unique + * on the first pass through the loop unless some request + * takes longer than the 64 thousand requests before it + * (and it would also have to have been a request that + * did not time out). + */ + while (cur_mid != last_mid) { struct mid_q_entry *mid_entry; unsigned int num_mids; collision = false; - if (server->CurrentMid == 0) - server->CurrentMid++; + if (cur_mid == 0) + cur_mid++; num_mids = 0; list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { ++num_mids; - if (mid_entry->mid == server->CurrentMid && + if (mid_entry->mid == cur_mid && mid_entry->midState == MID_REQUEST_SUBMITTED) { /* This mid is in use, try a different one */ collision = true; @@ -282,10 +289,11 @@ __u16 GetNextMid(struct TCP_Server_Info *server) server->tcpStatus = CifsNeedReconnect; if (!collision) { - mid = server->CurrentMid; + mid = (__u64)cur_mid; + server->CurrentMid = mid; break; } - server->CurrentMid++; + cur_mid++; } spin_unlock(&GlobalMid_Lock); return mid; -- cgit v1.2.3 From 7c9421e1a9ce8d17816f480c3a5b4f2609442cd5 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 23 Mar 2012 14:28:03 -0400 Subject: CIFS: Change mid_q_entry structure fields to be protocol-unspecific and big enough to keep both CIFS and SMB2 values. Signed-off-by: Pavel Shilovsky --- fs/cifs/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/cifs/misc.c') diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index d2ccce89062f..425e4f2a155c 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -268,7 +268,7 @@ __u64 GetNextMid(struct TCP_Server_Info *server) list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { ++num_mids; if (mid_entry->mid == cur_mid && - mid_entry->midState == MID_REQUEST_SUBMITTED) { + mid_entry->mid_state == MID_REQUEST_SUBMITTED) { /* This mid is in use, try a different one */ collision = true; break; -- cgit v1.2.3 From da472fc847e9d8c9da69b09ce0ab975b24f9b894 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 23 Mar 2012 14:40:53 -0400 Subject: cifs: add new cifsiod_wq workqueue ...and convert existing cifs users of system_nrt_wq to use that instead. Also, make it freezable, and set WQ_MEM_RECLAIM since we use it to deal with write reply handling. Signed-off-by: Jeff Layton Acked-by: Shirish Pargaonkar --- fs/cifs/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/cifs/misc.c') diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 425e4f2a155c..c29d1aa2c54f 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -595,7 +595,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) cifs_set_oplock_level(pCifsInode, pSMB->OplockLevel ? OPLOCK_READ : 0); - queue_work(system_nrt_wq, + queue_work(cifsiod_wq, &netfile->oplock_break); netfile->oplock_break_cancelled = false; -- cgit v1.2.3