diff options
Diffstat (limited to 'fs/cifs/smb2ops.c')
-rw-r--r-- | fs/cifs/smb2ops.c | 115 |
1 files changed, 98 insertions, 17 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 6f96e2292856..085e91436da7 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -67,10 +67,13 @@ change_conf(struct TCP_Server_Info *server) | |||
67 | } | 67 | } |
68 | 68 | ||
69 | static void | 69 | static void |
70 | smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, | 70 | smb2_add_credits(struct TCP_Server_Info *server, |
71 | const int optype) | 71 | const struct cifs_credits *credits, const int optype) |
72 | { | 72 | { |
73 | int *val, rc = -1; | 73 | int *val, rc = -1; |
74 | unsigned int add = credits->value; | ||
75 | unsigned int instance = credits->instance; | ||
76 | bool reconnect_detected = false; | ||
74 | 77 | ||
75 | spin_lock(&server->req_lock); | 78 | spin_lock(&server->req_lock); |
76 | val = server->ops->get_credits_field(server, optype); | 79 | val = server->ops->get_credits_field(server, optype); |
@@ -79,8 +82,11 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, | |||
79 | if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) | 82 | if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) |
80 | trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, | 83 | trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, |
81 | server->hostname, *val); | 84 | server->hostname, *val); |
85 | if ((instance == 0) || (instance == server->reconnect_instance)) | ||
86 | *val += add; | ||
87 | else | ||
88 | reconnect_detected = true; | ||
82 | 89 | ||
83 | *val += add; | ||
84 | if (*val > 65000) { | 90 | if (*val > 65000) { |
85 | *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ | 91 | *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ |
86 | printk_once(KERN_WARNING "server overflowed SMB3 credits\n"); | 92 | printk_once(KERN_WARNING "server overflowed SMB3 credits\n"); |
@@ -102,7 +108,12 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, | |||
102 | spin_unlock(&server->req_lock); | 108 | spin_unlock(&server->req_lock); |
103 | wake_up(&server->request_q); | 109 | wake_up(&server->request_q); |
104 | 110 | ||
105 | if (server->tcpStatus == CifsNeedReconnect) | 111 | if (reconnect_detected) |
112 | cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", | ||
113 | add, instance); | ||
114 | |||
115 | if (server->tcpStatus == CifsNeedReconnect | ||
116 | || server->tcpStatus == CifsExiting) | ||
106 | return; | 117 | return; |
107 | 118 | ||
108 | switch (rc) { | 119 | switch (rc) { |
@@ -163,7 +174,7 @@ smb2_get_credits(struct mid_q_entry *mid) | |||
163 | 174 | ||
164 | static int | 175 | static int |
165 | smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, | 176 | smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, |
166 | unsigned int *num, unsigned int *credits) | 177 | unsigned int *num, struct cifs_credits *credits) |
167 | { | 178 | { |
168 | int rc = 0; | 179 | int rc = 0; |
169 | unsigned int scredits; | 180 | unsigned int scredits; |
@@ -189,7 +200,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, | |||
189 | /* can deadlock with reopen */ | 200 | /* can deadlock with reopen */ |
190 | if (scredits <= 8) { | 201 | if (scredits <= 8) { |
191 | *num = SMB2_MAX_BUFFER_SIZE; | 202 | *num = SMB2_MAX_BUFFER_SIZE; |
192 | *credits = 0; | 203 | credits->value = 0; |
204 | credits->instance = 0; | ||
193 | break; | 205 | break; |
194 | } | 206 | } |
195 | 207 | ||
@@ -198,8 +210,10 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, | |||
198 | *num = min_t(unsigned int, size, | 210 | *num = min_t(unsigned int, size, |
199 | scredits * SMB2_MAX_BUFFER_SIZE); | 211 | scredits * SMB2_MAX_BUFFER_SIZE); |
200 | 212 | ||
201 | *credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); | 213 | credits->value = |
202 | server->credits -= *credits; | 214 | DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); |
215 | credits->instance = server->reconnect_instance; | ||
216 | server->credits -= credits->value; | ||
203 | server->in_flight++; | 217 | server->in_flight++; |
204 | break; | 218 | break; |
205 | } | 219 | } |
@@ -208,6 +222,38 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, | |||
208 | return rc; | 222 | return rc; |
209 | } | 223 | } |
210 | 224 | ||
225 | static int | ||
226 | smb2_adjust_credits(struct TCP_Server_Info *server, | ||
227 | struct cifs_credits *credits, | ||
228 | const unsigned int payload_size) | ||
229 | { | ||
230 | int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); | ||
231 | |||
232 | if (!credits->value || credits->value == new_val) | ||
233 | return 0; | ||
234 | |||
235 | if (credits->value < new_val) { | ||
236 | WARN_ONCE(1, "request has less credits (%d) than required (%d)", | ||
237 | credits->value, new_val); | ||
238 | return -ENOTSUPP; | ||
239 | } | ||
240 | |||
241 | spin_lock(&server->req_lock); | ||
242 | |||
243 | if (server->reconnect_instance != credits->instance) { | ||
244 | spin_unlock(&server->req_lock); | ||
245 | cifs_dbg(VFS, "trying to return %d credits to old session\n", | ||
246 | credits->value - new_val); | ||
247 | return -EAGAIN; | ||
248 | } | ||
249 | |||
250 | server->credits += credits->value - new_val; | ||
251 | spin_unlock(&server->req_lock); | ||
252 | wake_up(&server->request_q); | ||
253 | credits->value = new_val; | ||
254 | return 0; | ||
255 | } | ||
256 | |||
211 | static __u64 | 257 | static __u64 |
212 | smb2_get_next_mid(struct TCP_Server_Info *server) | 258 | smb2_get_next_mid(struct TCP_Server_Info *server) |
213 | { | 259 | { |
@@ -219,6 +265,15 @@ smb2_get_next_mid(struct TCP_Server_Info *server) | |||
219 | return mid; | 265 | return mid; |
220 | } | 266 | } |
221 | 267 | ||
268 | static void | ||
269 | smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) | ||
270 | { | ||
271 | spin_lock(&GlobalMid_Lock); | ||
272 | if (server->CurrentMid >= val) | ||
273 | server->CurrentMid -= val; | ||
274 | spin_unlock(&GlobalMid_Lock); | ||
275 | } | ||
276 | |||
222 | static struct mid_q_entry * | 277 | static struct mid_q_entry * |
223 | smb2_find_mid(struct TCP_Server_Info *server, char *buf) | 278 | smb2_find_mid(struct TCP_Server_Info *server, char *buf) |
224 | { | 279 | { |
@@ -940,6 +995,16 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, | |||
940 | resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; | 995 | resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; |
941 | memset(rsp_iov, 0, sizeof(rsp_iov)); | 996 | memset(rsp_iov, 0, sizeof(rsp_iov)); |
942 | 997 | ||
998 | if (ses->server->ops->query_all_EAs) { | ||
999 | if (!ea_value) { | ||
1000 | rc = ses->server->ops->query_all_EAs(xid, tcon, path, | ||
1001 | ea_name, NULL, 0, | ||
1002 | cifs_sb); | ||
1003 | if (rc == -ENODATA) | ||
1004 | goto sea_exit; | ||
1005 | } | ||
1006 | } | ||
1007 | |||
943 | /* Open */ | 1008 | /* Open */ |
944 | memset(&open_iov, 0, sizeof(open_iov)); | 1009 | memset(&open_iov, 0, sizeof(open_iov)); |
945 | rqst[0].rq_iov = open_iov; | 1010 | rqst[0].rq_iov = open_iov; |
@@ -1753,14 +1818,14 @@ smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, | |||
1753 | * the number of credits and return true. Otherwise - return false. | 1818 | * the number of credits and return true. Otherwise - return false. |
1754 | */ | 1819 | */ |
1755 | static bool | 1820 | static bool |
1756 | smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length) | 1821 | smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) |
1757 | { | 1822 | { |
1758 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; | 1823 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; |
1759 | 1824 | ||
1760 | if (shdr->Status != STATUS_PENDING) | 1825 | if (shdr->Status != STATUS_PENDING) |
1761 | return false; | 1826 | return false; |
1762 | 1827 | ||
1763 | if (!length) { | 1828 | if (shdr->CreditRequest) { |
1764 | spin_lock(&server->req_lock); | 1829 | spin_lock(&server->req_lock); |
1765 | server->credits += le16_to_cpu(shdr->CreditRequest); | 1830 | server->credits += le16_to_cpu(shdr->CreditRequest); |
1766 | spin_unlock(&server->req_lock); | 1831 | spin_unlock(&server->req_lock); |
@@ -2595,6 +2660,15 @@ smb2_downgrade_oplock(struct TCP_Server_Info *server, | |||
2595 | } | 2660 | } |
2596 | 2661 | ||
2597 | static void | 2662 | static void |
2663 | smb21_downgrade_oplock(struct TCP_Server_Info *server, | ||
2664 | struct cifsInodeInfo *cinode, bool set_level2) | ||
2665 | { | ||
2666 | server->ops->set_oplock_level(cinode, | ||
2667 | set_level2 ? SMB2_LEASE_READ_CACHING_HE : | ||
2668 | 0, 0, NULL); | ||
2669 | } | ||
2670 | |||
2671 | static void | ||
2598 | smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, | 2672 | smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, |
2599 | unsigned int epoch, bool *purge_cache) | 2673 | unsigned int epoch, bool *purge_cache) |
2600 | { | 2674 | { |
@@ -3210,15 +3284,15 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, | |||
3210 | } | 3284 | } |
3211 | 3285 | ||
3212 | if (server->ops->is_status_pending && | 3286 | if (server->ops->is_status_pending && |
3213 | server->ops->is_status_pending(buf, server, 0)) | 3287 | server->ops->is_status_pending(buf, server)) |
3214 | return -1; | 3288 | return -1; |
3215 | 3289 | ||
3216 | /* set up first two iov to get credits */ | 3290 | /* set up first two iov to get credits */ |
3217 | rdata->iov[0].iov_base = buf; | 3291 | rdata->iov[0].iov_base = buf; |
3218 | rdata->iov[0].iov_len = 4; | 3292 | rdata->iov[0].iov_len = 0; |
3219 | rdata->iov[1].iov_base = buf + 4; | 3293 | rdata->iov[1].iov_base = buf; |
3220 | rdata->iov[1].iov_len = | 3294 | rdata->iov[1].iov_len = |
3221 | min_t(unsigned int, buf_len, server->vals->read_rsp_size) - 4; | 3295 | min_t(unsigned int, buf_len, server->vals->read_rsp_size); |
3222 | cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", | 3296 | cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", |
3223 | rdata->iov[0].iov_base, rdata->iov[0].iov_len); | 3297 | rdata->iov[0].iov_base, rdata->iov[0].iov_len); |
3224 | cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", | 3298 | cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", |
@@ -3541,6 +3615,7 @@ struct smb_version_operations smb20_operations = { | |||
3541 | .get_credits = smb2_get_credits, | 3615 | .get_credits = smb2_get_credits, |
3542 | .wait_mtu_credits = cifs_wait_mtu_credits, | 3616 | .wait_mtu_credits = cifs_wait_mtu_credits, |
3543 | .get_next_mid = smb2_get_next_mid, | 3617 | .get_next_mid = smb2_get_next_mid, |
3618 | .revert_current_mid = smb2_revert_current_mid, | ||
3544 | .read_data_offset = smb2_read_data_offset, | 3619 | .read_data_offset = smb2_read_data_offset, |
3545 | .read_data_length = smb2_read_data_length, | 3620 | .read_data_length = smb2_read_data_length, |
3546 | .map_error = map_smb2_to_linux_error, | 3621 | .map_error = map_smb2_to_linux_error, |
@@ -3635,7 +3710,9 @@ struct smb_version_operations smb21_operations = { | |||
3635 | .get_credits_field = smb2_get_credits_field, | 3710 | .get_credits_field = smb2_get_credits_field, |
3636 | .get_credits = smb2_get_credits, | 3711 | .get_credits = smb2_get_credits, |
3637 | .wait_mtu_credits = smb2_wait_mtu_credits, | 3712 | .wait_mtu_credits = smb2_wait_mtu_credits, |
3713 | .adjust_credits = smb2_adjust_credits, | ||
3638 | .get_next_mid = smb2_get_next_mid, | 3714 | .get_next_mid = smb2_get_next_mid, |
3715 | .revert_current_mid = smb2_revert_current_mid, | ||
3639 | .read_data_offset = smb2_read_data_offset, | 3716 | .read_data_offset = smb2_read_data_offset, |
3640 | .read_data_length = smb2_read_data_length, | 3717 | .read_data_length = smb2_read_data_length, |
3641 | .map_error = map_smb2_to_linux_error, | 3718 | .map_error = map_smb2_to_linux_error, |
@@ -3646,7 +3723,7 @@ struct smb_version_operations smb21_operations = { | |||
3646 | .print_stats = smb2_print_stats, | 3723 | .print_stats = smb2_print_stats, |
3647 | .is_oplock_break = smb2_is_valid_oplock_break, | 3724 | .is_oplock_break = smb2_is_valid_oplock_break, |
3648 | .handle_cancelled_mid = smb2_handle_cancelled_mid, | 3725 | .handle_cancelled_mid = smb2_handle_cancelled_mid, |
3649 | .downgrade_oplock = smb2_downgrade_oplock, | 3726 | .downgrade_oplock = smb21_downgrade_oplock, |
3650 | .need_neg = smb2_need_neg, | 3727 | .need_neg = smb2_need_neg, |
3651 | .negotiate = smb2_negotiate, | 3728 | .negotiate = smb2_negotiate, |
3652 | .negotiate_wsize = smb2_negotiate_wsize, | 3729 | .negotiate_wsize = smb2_negotiate_wsize, |
@@ -3731,7 +3808,9 @@ struct smb_version_operations smb30_operations = { | |||
3731 | .get_credits_field = smb2_get_credits_field, | 3808 | .get_credits_field = smb2_get_credits_field, |
3732 | .get_credits = smb2_get_credits, | 3809 | .get_credits = smb2_get_credits, |
3733 | .wait_mtu_credits = smb2_wait_mtu_credits, | 3810 | .wait_mtu_credits = smb2_wait_mtu_credits, |
3811 | .adjust_credits = smb2_adjust_credits, | ||
3734 | .get_next_mid = smb2_get_next_mid, | 3812 | .get_next_mid = smb2_get_next_mid, |
3813 | .revert_current_mid = smb2_revert_current_mid, | ||
3735 | .read_data_offset = smb2_read_data_offset, | 3814 | .read_data_offset = smb2_read_data_offset, |
3736 | .read_data_length = smb2_read_data_length, | 3815 | .read_data_length = smb2_read_data_length, |
3737 | .map_error = map_smb2_to_linux_error, | 3816 | .map_error = map_smb2_to_linux_error, |
@@ -3743,7 +3822,7 @@ struct smb_version_operations smb30_operations = { | |||
3743 | .dump_share_caps = smb2_dump_share_caps, | 3822 | .dump_share_caps = smb2_dump_share_caps, |
3744 | .is_oplock_break = smb2_is_valid_oplock_break, | 3823 | .is_oplock_break = smb2_is_valid_oplock_break, |
3745 | .handle_cancelled_mid = smb2_handle_cancelled_mid, | 3824 | .handle_cancelled_mid = smb2_handle_cancelled_mid, |
3746 | .downgrade_oplock = smb2_downgrade_oplock, | 3825 | .downgrade_oplock = smb21_downgrade_oplock, |
3747 | .need_neg = smb2_need_neg, | 3826 | .need_neg = smb2_need_neg, |
3748 | .negotiate = smb2_negotiate, | 3827 | .negotiate = smb2_negotiate, |
3749 | .negotiate_wsize = smb3_negotiate_wsize, | 3828 | .negotiate_wsize = smb3_negotiate_wsize, |
@@ -3836,7 +3915,9 @@ struct smb_version_operations smb311_operations = { | |||
3836 | .get_credits_field = smb2_get_credits_field, | 3915 | .get_credits_field = smb2_get_credits_field, |
3837 | .get_credits = smb2_get_credits, | 3916 | .get_credits = smb2_get_credits, |
3838 | .wait_mtu_credits = smb2_wait_mtu_credits, | 3917 | .wait_mtu_credits = smb2_wait_mtu_credits, |
3918 | .adjust_credits = smb2_adjust_credits, | ||
3839 | .get_next_mid = smb2_get_next_mid, | 3919 | .get_next_mid = smb2_get_next_mid, |
3920 | .revert_current_mid = smb2_revert_current_mid, | ||
3840 | .read_data_offset = smb2_read_data_offset, | 3921 | .read_data_offset = smb2_read_data_offset, |
3841 | .read_data_length = smb2_read_data_length, | 3922 | .read_data_length = smb2_read_data_length, |
3842 | .map_error = map_smb2_to_linux_error, | 3923 | .map_error = map_smb2_to_linux_error, |
@@ -3848,7 +3929,7 @@ struct smb_version_operations smb311_operations = { | |||
3848 | .dump_share_caps = smb2_dump_share_caps, | 3929 | .dump_share_caps = smb2_dump_share_caps, |
3849 | .is_oplock_break = smb2_is_valid_oplock_break, | 3930 | .is_oplock_break = smb2_is_valid_oplock_break, |
3850 | .handle_cancelled_mid = smb2_handle_cancelled_mid, | 3931 | .handle_cancelled_mid = smb2_handle_cancelled_mid, |
3851 | .downgrade_oplock = smb2_downgrade_oplock, | 3932 | .downgrade_oplock = smb21_downgrade_oplock, |
3852 | .need_neg = smb2_need_neg, | 3933 | .need_neg = smb2_need_neg, |
3853 | .negotiate = smb2_negotiate, | 3934 | .negotiate = smb2_negotiate, |
3854 | .negotiate_wsize = smb3_negotiate_wsize, | 3935 | .negotiate_wsize = smb3_negotiate_wsize, |