diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-18 19:20:33 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:30 -0400 |
commit | 2e44b2887882134abf353b28867b82645e9f0856 (patch) | |
tree | 963236eb542e26b046960f72f9ec47ae8e339a76 /fs | |
parent | d324f08d6a87149597817f4496ef0f7ac185e8da (diff) |
CIFS: Process oplocks for SMB2
Signed-off-by: Pavel Shilovsky <piastryyy@gmail.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
-rw-r--r-- | fs/cifs/connect.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2file.c | 24 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 3 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 34 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 10 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 3 |
7 files changed, 68 insertions, 12 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9adf211ca95a..3eb59ed6904a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -328,6 +328,8 @@ struct smb_version_operations { | |||
328 | struct cifs_fid *); | 328 | struct cifs_fid *); |
329 | /* calculate a size of SMB message */ | 329 | /* calculate a size of SMB message */ |
330 | unsigned int (*calc_smb_size)(void *); | 330 | unsigned int (*calc_smb_size)(void *); |
331 | /* check for STATUS_PENDING and process it in a positive case */ | ||
332 | bool (*is_status_pending)(char *, struct TCP_Server_Info *, int); | ||
331 | }; | 333 | }; |
332 | 334 | ||
333 | struct smb_version_values { | 335 | struct smb_version_values { |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c31b30b572e0..549409b1c776 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -819,6 +819,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) | |||
819 | cifs_dump_mem("Bad SMB: ", buf, | 819 | cifs_dump_mem("Bad SMB: ", buf, |
820 | min_t(unsigned int, server->total_read, 48)); | 820 | min_t(unsigned int, server->total_read, 48)); |
821 | 821 | ||
822 | if (server->ops->is_status_pending && | ||
823 | server->ops->is_status_pending(buf, server, length)) | ||
824 | return -1; | ||
825 | |||
822 | if (!mid) | 826 | if (!mid) |
823 | return length; | 827 | return length; |
824 | 828 | ||
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index a7618dfb7712..5ff25e025215 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c | |||
@@ -34,6 +34,26 @@ | |||
34 | #include "fscache.h" | 34 | #include "fscache.h" |
35 | #include "smb2proto.h" | 35 | #include "smb2proto.h" |
36 | 36 | ||
37 | void | ||
38 | smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | ||
39 | { | ||
40 | oplock &= 0xFF; | ||
41 | if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { | ||
42 | cinode->clientCanCacheAll = true; | ||
43 | cinode->clientCanCacheRead = true; | ||
44 | cFYI(1, "Exclusive Oplock granted on inode %p", | ||
45 | &cinode->vfs_inode); | ||
46 | } else if (oplock == SMB2_OPLOCK_LEVEL_II) { | ||
47 | cinode->clientCanCacheAll = false; | ||
48 | cinode->clientCanCacheRead = true; | ||
49 | cFYI(1, "Level II Oplock granted on inode %p", | ||
50 | &cinode->vfs_inode); | ||
51 | } else { | ||
52 | cinode->clientCanCacheAll = false; | ||
53 | cinode->clientCanCacheRead = false; | ||
54 | } | ||
55 | } | ||
56 | |||
37 | int | 57 | int |
38 | smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, | 58 | smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, |
39 | int disposition, int desired_access, int create_options, | 59 | int disposition, int desired_access, int create_options, |
@@ -58,10 +78,11 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, | |||
58 | } | 78 | } |
59 | 79 | ||
60 | desired_access |= FILE_READ_ATTRIBUTES; | 80 | desired_access |= FILE_READ_ATTRIBUTES; |
81 | *oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; | ||
61 | 82 | ||
62 | rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid, | 83 | rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid, |
63 | &fid->volatile_fid, desired_access, disposition, | 84 | &fid->volatile_fid, desired_access, disposition, |
64 | 0, 0, smb2_data); | 85 | 0, 0, (__u8 *)oplock, smb2_data); |
65 | if (rc) | 86 | if (rc) |
66 | goto out; | 87 | goto out; |
67 | 88 | ||
@@ -79,7 +100,6 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, | |||
79 | } | 100 | } |
80 | 101 | ||
81 | out: | 102 | out: |
82 | *oplock = 0; | ||
83 | kfree(smb2_data); | 103 | kfree(smb2_data); |
84 | kfree(smb2_path); | 104 | kfree(smb2_path); |
85 | return rc; | 105 | return rc; |
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 1bd6b0f0c10e..706482452df4 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c | |||
@@ -47,6 +47,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, | |||
47 | int rc, tmprc = 0; | 47 | int rc, tmprc = 0; |
48 | u64 persistent_fid, volatile_fid; | 48 | u64 persistent_fid, volatile_fid; |
49 | __le16 *utf16_path; | 49 | __le16 *utf16_path; |
50 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | ||
50 | 51 | ||
51 | utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); | 52 | utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); |
52 | if (!utf16_path) | 53 | if (!utf16_path) |
@@ -54,7 +55,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, | |||
54 | 55 | ||
55 | rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, | 56 | rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, |
56 | desired_access, create_disposition, file_attributes, | 57 | desired_access, create_disposition, file_attributes, |
57 | create_options, NULL); | 58 | create_options, &oplock, NULL); |
58 | if (rc) { | 59 | if (rc) { |
59 | kfree(utf16_path); | 60 | kfree(utf16_path); |
60 | return rc; | 61 | return rc; |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0fa086c1b5dc..2d88c90ded0c 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include "smb2proto.h" | 23 | #include "smb2proto.h" |
24 | #include "cifsproto.h" | 24 | #include "cifsproto.h" |
25 | #include "cifs_debug.h" | 25 | #include "cifs_debug.h" |
26 | #include "smb2status.h" | ||
26 | 27 | ||
27 | static int | 28 | static int |
28 | change_conf(struct TCP_Server_Info *server) | 29 | change_conf(struct TCP_Server_Info *server) |
@@ -207,13 +208,14 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, | |||
207 | int rc; | 208 | int rc; |
208 | __u64 persistent_fid, volatile_fid; | 209 | __u64 persistent_fid, volatile_fid; |
209 | __le16 *utf16_path; | 210 | __le16 *utf16_path; |
211 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | ||
210 | 212 | ||
211 | utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); | 213 | utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); |
212 | if (!utf16_path) | 214 | if (!utf16_path) |
213 | return -ENOMEM; | 215 | return -ENOMEM; |
214 | 216 | ||
215 | rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, | 217 | rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, |
216 | FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, NULL); | 218 | FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL); |
217 | if (rc) { | 219 | if (rc) { |
218 | kfree(utf16_path); | 220 | kfree(utf16_path); |
219 | return rc; | 221 | return rc; |
@@ -358,10 +360,10 @@ smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) | |||
358 | static void | 360 | static void |
359 | smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) | 361 | smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) |
360 | { | 362 | { |
361 | /* struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); */ | 363 | struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); |
362 | cfile->fid.persistent_fid = fid->persistent_fid; | 364 | cfile->fid.persistent_fid = fid->persistent_fid; |
363 | cfile->fid.volatile_fid = fid->volatile_fid; | 365 | cfile->fid.volatile_fid = fid->volatile_fid; |
364 | /* cifs_set_oplock_level(cinode, oplock); */ | 366 | smb2_set_oplock_level(cinode, oplock); |
365 | /* cinode->can_cache_brlcks = cinode->clientCanCacheAll; */ | 367 | /* cinode->can_cache_brlcks = cinode->clientCanCacheAll; */ |
366 | } | 368 | } |
367 | 369 | ||
@@ -432,6 +434,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, | |||
432 | { | 434 | { |
433 | __le16 *utf16_path; | 435 | __le16 *utf16_path; |
434 | int rc; | 436 | int rc; |
437 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | ||
435 | __u64 persistent_fid, volatile_fid; | 438 | __u64 persistent_fid, volatile_fid; |
436 | 439 | ||
437 | utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); | 440 | utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); |
@@ -440,7 +443,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, | |||
440 | 443 | ||
441 | rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, | 444 | rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, |
442 | FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0, | 445 | FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0, |
443 | NULL); | 446 | &oplock, NULL); |
444 | kfree(utf16_path); | 447 | kfree(utf16_path); |
445 | if (rc) { | 448 | if (rc) { |
446 | cERROR(1, "open dir failed"); | 449 | cERROR(1, "open dir failed"); |
@@ -477,6 +480,28 @@ smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, | |||
477 | return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); | 480 | return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); |
478 | } | 481 | } |
479 | 482 | ||
483 | /* | ||
484 | * If we negotiate SMB2 protocol and get STATUS_PENDING - update | ||
485 | * the number of credits and return true. Otherwise - return false. | ||
486 | */ | ||
487 | static bool | ||
488 | smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length) | ||
489 | { | ||
490 | struct smb2_hdr *hdr = (struct smb2_hdr *)buf; | ||
491 | |||
492 | if (le32_to_cpu(hdr->Status) != STATUS_PENDING) | ||
493 | return false; | ||
494 | |||
495 | if (!length) { | ||
496 | spin_lock(&server->req_lock); | ||
497 | server->credits += le16_to_cpu(hdr->CreditRequest); | ||
498 | spin_unlock(&server->req_lock); | ||
499 | wake_up(&server->request_q); | ||
500 | } | ||
501 | |||
502 | return true; | ||
503 | } | ||
504 | |||
480 | struct smb_version_operations smb21_operations = { | 505 | struct smb_version_operations smb21_operations = { |
481 | .setup_request = smb2_setup_request, | 506 | .setup_request = smb2_setup_request, |
482 | .setup_async_request = smb2_setup_async_request, | 507 | .setup_async_request = smb2_setup_async_request, |
@@ -530,6 +555,7 @@ struct smb_version_operations smb21_operations = { | |||
530 | .query_dir_next = smb2_query_dir_next, | 555 | .query_dir_next = smb2_query_dir_next, |
531 | .close_dir = smb2_close_dir, | 556 | .close_dir = smb2_close_dir, |
532 | .calc_smb_size = smb2_calc_size, | 557 | .calc_smb_size = smb2_calc_size, |
558 | .is_status_pending = smb2_is_status_pending, | ||
533 | }; | 559 | }; |
534 | 560 | ||
535 | struct smb_version_values smb21_values = { | 561 | struct smb_version_values smb21_values = { |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 21b3a652e192..e97c256c8a42 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -872,7 +872,7 @@ int | |||
872 | SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | 872 | SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, |
873 | u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, | 873 | u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, |
874 | __u32 create_disposition, __u32 file_attributes, __u32 create_options, | 874 | __u32 create_disposition, __u32 file_attributes, __u32 create_options, |
875 | struct smb2_file_all_info *buf) | 875 | __u8 *oplock, struct smb2_file_all_info *buf) |
876 | { | 876 | { |
877 | struct smb2_create_req *req; | 877 | struct smb2_create_req *req; |
878 | struct smb2_create_rsp *rsp; | 878 | struct smb2_create_rsp *rsp; |
@@ -895,9 +895,9 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
895 | if (rc) | 895 | if (rc) |
896 | return rc; | 896 | return rc; |
897 | 897 | ||
898 | /* if (server->oplocks) | 898 | if (server->oplocks) |
899 | req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_BATCH; | 899 | req->RequestedOplockLevel = *oplock; |
900 | else */ | 900 | else |
901 | req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; | 901 | req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; |
902 | req->ImpersonationLevel = IL_IMPERSONATION; | 902 | req->ImpersonationLevel = IL_IMPERSONATION; |
903 | req->DesiredAccess = cpu_to_le32(desired_access); | 903 | req->DesiredAccess = cpu_to_le32(desired_access); |
@@ -954,6 +954,8 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, | |||
954 | buf->NumberOfLinks = cpu_to_le32(1); | 954 | buf->NumberOfLinks = cpu_to_le32(1); |
955 | buf->DeletePending = 0; | 955 | buf->DeletePending = 0; |
956 | } | 956 | } |
957 | |||
958 | *oplock = rsp->OplockLevel; | ||
957 | creat_exit: | 959 | creat_exit: |
958 | free_rsp_buf(resp_buftype, rsp); | 960 | free_rsp_buf(resp_buftype, rsp); |
959 | return rc; | 961 | return rc; |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 0d29db222a5a..d18339fcc8a2 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -82,6 +82,7 @@ extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, | |||
82 | int desired_access, int create_options, | 82 | int desired_access, int create_options, |
83 | struct cifs_fid *fid, __u32 *oplock, | 83 | struct cifs_fid *fid, __u32 *oplock, |
84 | FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb); | 84 | FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb); |
85 | extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); | ||
85 | 86 | ||
86 | /* | 87 | /* |
87 | * SMB2 Worker functions - most of protocol specific implementation details | 88 | * SMB2 Worker functions - most of protocol specific implementation details |
@@ -99,7 +100,7 @@ extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, | |||
99 | __le16 *path, u64 *persistent_fid, u64 *volatile_fid, | 100 | __le16 *path, u64 *persistent_fid, u64 *volatile_fid, |
100 | __u32 desired_access, __u32 create_disposition, | 101 | __u32 desired_access, __u32 create_disposition, |
101 | __u32 file_attributes, __u32 create_options, | 102 | __u32 file_attributes, __u32 create_options, |
102 | struct smb2_file_all_info *buf); | 103 | __u8 *oplock, struct smb2_file_all_info *buf); |
103 | extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, | 104 | extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, |
104 | u64 persistent_file_id, u64 volatile_file_id); | 105 | u64 persistent_file_id, u64 volatile_file_id); |
105 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, | 106 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, |