diff options
| author | Pavel Shilovsky <pshilovsky@samba.org> | 2013-09-05 13:30:16 -0400 |
|---|---|---|
| committer | Steve French <smfrench@gmail.com> | 2013-09-09 23:52:18 -0400 |
| commit | 42873b0a282ac84a56e0e48c408beb62d0ad2917 (patch) | |
| tree | 14891ca4e47ebfa0a3668b68772489e6bdd805d1 /fs/cifs | |
| parent | f047390a097e907ccccf8aa894dec49890578a1a (diff) | |
CIFS: Respect epoch value from create lease context v2
that force a client to purge cache pages when a server requests it.
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
| -rw-r--r-- | fs/cifs/cifsfs.c | 1 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 13 | ||||
| -rw-r--r-- | fs/cifs/file.c | 4 | ||||
| -rw-r--r-- | fs/cifs/smb2misc.c | 6 | ||||
| -rw-r--r-- | fs/cifs/smb2ops.c | 57 | ||||
| -rw-r--r-- | fs/cifs/smb2pdu.c | 7 |
6 files changed, 71 insertions, 17 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index ab88efe014a5..a16b4e58bcc6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
| @@ -255,6 +255,7 @@ cifs_alloc_inode(struct super_block *sb) | |||
| 255 | cifs_inode->server_eof = 0; | 255 | cifs_inode->server_eof = 0; |
| 256 | cifs_inode->uniqueid = 0; | 256 | cifs_inode->uniqueid = 0; |
| 257 | cifs_inode->createtime = 0; | 257 | cifs_inode->createtime = 0; |
| 258 | cifs_inode->epoch = 0; | ||
| 258 | #ifdef CONFIG_CIFS_SMB2 | 259 | #ifdef CONFIG_CIFS_SMB2 |
| 259 | get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); | 260 | get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); |
| 260 | #endif | 261 | #endif |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 38118938f0b6..cfa14c80ef3b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
| @@ -373,11 +373,12 @@ struct smb_version_operations { | |||
| 373 | /* if we can do cache read operations */ | 373 | /* if we can do cache read operations */ |
| 374 | bool (*is_read_op)(__u32); | 374 | bool (*is_read_op)(__u32); |
| 375 | /* set oplock level for the inode */ | 375 | /* set oplock level for the inode */ |
| 376 | void (*set_oplock_level)(struct cifsInodeInfo *, __u32); | 376 | void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, |
| 377 | bool *); | ||
| 377 | /* create lease context buffer for CREATE request */ | 378 | /* create lease context buffer for CREATE request */ |
| 378 | char * (*create_lease_buf)(u8 *, u8); | 379 | char * (*create_lease_buf)(u8 *, u8); |
| 379 | /* parse lease context buffer and return oplock info */ | 380 | /* parse lease context buffer and return oplock/epoch info */ |
| 380 | __u8 (*parse_lease_buf)(void *); | 381 | __u8 (*parse_lease_buf)(void *, unsigned int *); |
| 381 | }; | 382 | }; |
| 382 | 383 | ||
| 383 | struct smb_version_values { | 384 | struct smb_version_values { |
| @@ -940,6 +941,8 @@ struct cifs_fid { | |||
| 940 | __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ | 941 | __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ |
| 941 | #endif | 942 | #endif |
| 942 | struct cifs_pending_open *pending_open; | 943 | struct cifs_pending_open *pending_open; |
| 944 | unsigned int epoch; | ||
| 945 | bool purge_cache; | ||
| 943 | }; | 946 | }; |
| 944 | 947 | ||
| 945 | struct cifs_fid_locks { | 948 | struct cifs_fid_locks { |
| @@ -1039,7 +1042,10 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file); | |||
| 1039 | 1042 | ||
| 1040 | #define CIFS_CACHE_READ_FLG 1 | 1043 | #define CIFS_CACHE_READ_FLG 1 |
| 1041 | #define CIFS_CACHE_HANDLE_FLG 2 | 1044 | #define CIFS_CACHE_HANDLE_FLG 2 |
| 1045 | #define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG) | ||
| 1042 | #define CIFS_CACHE_WRITE_FLG 4 | 1046 | #define CIFS_CACHE_WRITE_FLG 4 |
| 1047 | #define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) | ||
| 1048 | #define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) | ||
| 1043 | 1049 | ||
| 1044 | #define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG) | 1050 | #define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG) |
| 1045 | #define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) | 1051 | #define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) |
| @@ -1057,6 +1063,7 @@ struct cifsInodeInfo { | |||
| 1057 | struct list_head openFileList; | 1063 | struct list_head openFileList; |
| 1058 | __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ | 1064 | __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ |
| 1059 | unsigned int oplock; /* oplock/lease level we have */ | 1065 | unsigned int oplock; /* oplock/lease level we have */ |
| 1066 | unsigned int epoch; /* used to track lease state changes */ | ||
| 1060 | bool delete_pending; /* DELETE_ON_CLOSE is set */ | 1067 | bool delete_pending; /* DELETE_ON_CLOSE is set */ |
| 1061 | bool invalid_mapping; /* pagecache is invalid */ | 1068 | bool invalid_mapping; /* pagecache is invalid */ |
| 1062 | unsigned long time; /* jiffies of last update of inode */ | 1069 | unsigned long time; /* jiffies of last update of inode */ |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 188b2470b1fb..d044b35ce228 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
| @@ -323,6 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
| 323 | oplock = fid->pending_open->oplock; | 323 | oplock = fid->pending_open->oplock; |
| 324 | list_del(&fid->pending_open->olist); | 324 | list_del(&fid->pending_open->olist); |
| 325 | 325 | ||
| 326 | fid->purge_cache = false; | ||
| 326 | server->ops->set_fid(cfile, fid, oplock); | 327 | server->ops->set_fid(cfile, fid, oplock); |
| 327 | 328 | ||
| 328 | list_add(&cfile->tlist, &tcon->openFileList); | 329 | list_add(&cfile->tlist, &tcon->openFileList); |
| @@ -333,6 +334,9 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
| 333 | list_add_tail(&cfile->flist, &cinode->openFileList); | 334 | list_add_tail(&cfile->flist, &cinode->openFileList); |
| 334 | spin_unlock(&cifs_file_list_lock); | 335 | spin_unlock(&cifs_file_list_lock); |
| 335 | 336 | ||
| 337 | if (fid->purge_cache) | ||
| 338 | cifs_invalidate_mapping(inode); | ||
| 339 | |||
| 336 | file->private_data = cfile; | 340 | file->private_data = cfile; |
| 337 | return cfile; | 341 | return cfile; |
| 338 | } | 342 | } |
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 4aa59b34ec23..fb3966265b6e 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
| @@ -420,6 +420,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, | |||
| 420 | __u8 lease_state; | 420 | __u8 lease_state; |
| 421 | struct list_head *tmp; | 421 | struct list_head *tmp; |
| 422 | struct cifsFileInfo *cfile; | 422 | struct cifsFileInfo *cfile; |
| 423 | struct TCP_Server_Info *server = tcon->ses->server; | ||
| 423 | struct cifs_pending_open *open; | 424 | struct cifs_pending_open *open; |
| 424 | struct cifsInodeInfo *cinode; | 425 | struct cifsInodeInfo *cinode; |
| 425 | int ack_req = le32_to_cpu(rsp->Flags & | 426 | int ack_req = le32_to_cpu(rsp->Flags & |
| @@ -439,7 +440,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, | |||
| 439 | cifs_dbg(FYI, "lease key match, lease break 0x%d\n", | 440 | cifs_dbg(FYI, "lease key match, lease break 0x%d\n", |
| 440 | le32_to_cpu(rsp->NewLeaseState)); | 441 | le32_to_cpu(rsp->NewLeaseState)); |
| 441 | 442 | ||
| 442 | tcon->ses->server->ops->set_oplock_level(cinode, lease_state); | 443 | server->ops->set_oplock_level(cinode, lease_state, 0, NULL); |
| 443 | 444 | ||
| 444 | if (ack_req) | 445 | if (ack_req) |
| 445 | cfile->oplock_break_cancelled = false; | 446 | cfile->oplock_break_cancelled = false; |
| @@ -575,7 +576,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
| 575 | cfile->oplock_break_cancelled = false; | 576 | cfile->oplock_break_cancelled = false; |
| 576 | 577 | ||
| 577 | server->ops->set_oplock_level(cinode, | 578 | server->ops->set_oplock_level(cinode, |
| 578 | rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0); | 579 | rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0, |
| 580 | 0, NULL); | ||
| 579 | 581 | ||
| 580 | queue_work(cifsiod_wq, &cfile->oplock_break); | 582 | queue_work(cifsiod_wq, &cfile->oplock_break); |
| 581 | 583 | ||
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index a9256bd374f8..861b33214144 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
| @@ -381,7 +381,8 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) | |||
| 381 | 381 | ||
| 382 | cfile->fid.persistent_fid = fid->persistent_fid; | 382 | cfile->fid.persistent_fid = fid->persistent_fid; |
| 383 | cfile->fid.volatile_fid = fid->volatile_fid; | 383 | cfile->fid.volatile_fid = fid->volatile_fid; |
| 384 | server->ops->set_oplock_level(cinode, oplock); | 384 | server->ops->set_oplock_level(cinode, oplock, fid->epoch, |
| 385 | &fid->purge_cache); | ||
| 385 | cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); | 386 | cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); |
| 386 | } | 387 | } |
| 387 | 388 | ||
| @@ -651,18 +652,18 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, | |||
| 651 | } | 652 | } |
| 652 | 653 | ||
| 653 | static void | 654 | static void |
| 654 | smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | 655 | smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, |
| 656 | unsigned int epoch, bool *purge_cache) | ||
| 655 | { | 657 | { |
| 656 | oplock &= 0xFF; | 658 | oplock &= 0xFF; |
| 657 | if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) | 659 | if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) |
| 658 | return; | 660 | return; |
| 659 | if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { | 661 | if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { |
| 660 | cinode->oplock = CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG | | 662 | cinode->oplock = CIFS_CACHE_RHW_FLG; |
| 661 | CIFS_CACHE_HANDLE_FLG; | ||
| 662 | cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", | 663 | cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", |
| 663 | &cinode->vfs_inode); | 664 | &cinode->vfs_inode); |
| 664 | } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { | 665 | } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { |
| 665 | cinode->oplock = CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG; | 666 | cinode->oplock = CIFS_CACHE_RW_FLG; |
| 666 | cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", | 667 | cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", |
| 667 | &cinode->vfs_inode); | 668 | &cinode->vfs_inode); |
| 668 | } else if (oplock == SMB2_OPLOCK_LEVEL_II) { | 669 | } else if (oplock == SMB2_OPLOCK_LEVEL_II) { |
| @@ -674,7 +675,8 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | |||
| 674 | } | 675 | } |
| 675 | 676 | ||
| 676 | static void | 677 | static void |
| 677 | smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | 678 | smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, |
| 679 | unsigned int epoch, bool *purge_cache) | ||
| 678 | { | 680 | { |
| 679 | char message[5] = {0}; | 681 | char message[5] = {0}; |
| 680 | 682 | ||
| @@ -701,6 +703,41 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | |||
| 701 | &cinode->vfs_inode); | 703 | &cinode->vfs_inode); |
| 702 | } | 704 | } |
| 703 | 705 | ||
| 706 | static void | ||
| 707 | smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, | ||
| 708 | unsigned int epoch, bool *purge_cache) | ||
| 709 | { | ||
| 710 | unsigned int old_oplock = cinode->oplock; | ||
| 711 | |||
| 712 | smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); | ||
| 713 | |||
| 714 | if (purge_cache) { | ||
| 715 | *purge_cache = false; | ||
| 716 | if (old_oplock == CIFS_CACHE_READ_FLG) { | ||
| 717 | if (cinode->oplock == CIFS_CACHE_READ_FLG && | ||
| 718 | (epoch - cinode->epoch > 0)) | ||
| 719 | *purge_cache = true; | ||
| 720 | else if (cinode->oplock == CIFS_CACHE_RH_FLG && | ||
| 721 | (epoch - cinode->epoch > 1)) | ||
| 722 | *purge_cache = true; | ||
| 723 | else if (cinode->oplock == CIFS_CACHE_RHW_FLG && | ||
| 724 | (epoch - cinode->epoch > 1)) | ||
| 725 | *purge_cache = true; | ||
| 726 | else if (cinode->oplock == 0 && | ||
| 727 | (epoch - cinode->epoch > 0)) | ||
| 728 | *purge_cache = true; | ||
| 729 | } else if (old_oplock == CIFS_CACHE_RH_FLG) { | ||
| 730 | if (cinode->oplock == CIFS_CACHE_RH_FLG && | ||
| 731 | (epoch - cinode->epoch > 0)) | ||
| 732 | *purge_cache = true; | ||
| 733 | else if (cinode->oplock == CIFS_CACHE_RHW_FLG && | ||
| 734 | (epoch - cinode->epoch > 1)) | ||
| 735 | *purge_cache = true; | ||
| 736 | } | ||
| 737 | cinode->epoch = epoch; | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 704 | static bool | 741 | static bool |
| 705 | smb2_is_read_op(__u32 oplock) | 742 | smb2_is_read_op(__u32 oplock) |
| 706 | { | 743 | { |
| @@ -780,20 +817,22 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock) | |||
| 780 | } | 817 | } |
| 781 | 818 | ||
| 782 | static __u8 | 819 | static __u8 |
| 783 | smb2_parse_lease_buf(void *buf) | 820 | smb2_parse_lease_buf(void *buf, unsigned int *epoch) |
| 784 | { | 821 | { |
| 785 | struct create_lease *lc = (struct create_lease *)buf; | 822 | struct create_lease *lc = (struct create_lease *)buf; |
| 786 | 823 | ||
| 824 | *epoch = 0; /* not used */ | ||
| 787 | if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) | 825 | if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) |
| 788 | return SMB2_OPLOCK_LEVEL_NOCHANGE; | 826 | return SMB2_OPLOCK_LEVEL_NOCHANGE; |
| 789 | return le32_to_cpu(lc->lcontext.LeaseState); | 827 | return le32_to_cpu(lc->lcontext.LeaseState); |
| 790 | } | 828 | } |
| 791 | 829 | ||
| 792 | static __u8 | 830 | static __u8 |
| 793 | smb3_parse_lease_buf(void *buf) | 831 | smb3_parse_lease_buf(void *buf, unsigned int *epoch) |
| 794 | { | 832 | { |
| 795 | struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; | 833 | struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; |
| 796 | 834 | ||
| 835 | *epoch = le16_to_cpu(lc->lcontext.Epoch); | ||
| 797 | if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) | 836 | if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) |
| 798 | return SMB2_OPLOCK_LEVEL_NOCHANGE; | 837 | return SMB2_OPLOCK_LEVEL_NOCHANGE; |
| 799 | return le32_to_cpu(lc->lcontext.LeaseState); | 838 | return le32_to_cpu(lc->lcontext.LeaseState); |
| @@ -1009,7 +1048,7 @@ struct smb_version_operations smb30_operations = { | |||
| 1009 | .generate_signingkey = generate_smb3signingkey, | 1048 | .generate_signingkey = generate_smb3signingkey, |
| 1010 | .calc_signature = smb3_calc_signature, | 1049 | .calc_signature = smb3_calc_signature, |
| 1011 | .is_read_op = smb21_is_read_op, | 1050 | .is_read_op = smb21_is_read_op, |
| 1012 | .set_oplock_level = smb21_set_oplock_level, | 1051 | .set_oplock_level = smb3_set_oplock_level, |
| 1013 | .create_lease_buf = smb3_create_lease_buf, | 1052 | .create_lease_buf = smb3_create_lease_buf, |
| 1014 | .parse_lease_buf = smb3_parse_lease_buf, | 1053 | .parse_lease_buf = smb3_parse_lease_buf, |
| 1015 | }; | 1054 | }; |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 6eef8b67e709..eba0efde66d7 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
| @@ -903,7 +903,8 @@ create_reconnect_durable_buf(struct cifs_fid *fid) | |||
| 903 | } | 903 | } |
| 904 | 904 | ||
| 905 | static __u8 | 905 | static __u8 |
| 906 | parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp) | 906 | parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, |
| 907 | unsigned int *epoch) | ||
| 907 | { | 908 | { |
| 908 | char *data_offset; | 909 | char *data_offset; |
| 909 | struct create_context *cc; | 910 | struct create_context *cc; |
| @@ -920,7 +921,7 @@ parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp) | |||
| 920 | next = le32_to_cpu(cc->Next); | 921 | next = le32_to_cpu(cc->Next); |
| 921 | continue; | 922 | continue; |
| 922 | } | 923 | } |
| 923 | return server->ops->parse_lease_buf(cc); | 924 | return server->ops->parse_lease_buf(cc, epoch); |
| 924 | } while (next != 0); | 925 | } while (next != 0); |
| 925 | 926 | ||
| 926 | return 0; | 927 | return 0; |
| @@ -1102,7 +1103,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, | |||
| 1102 | } | 1103 | } |
| 1103 | 1104 | ||
| 1104 | if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) | 1105 | if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) |
| 1105 | *oplock = parse_lease_state(server, rsp); | 1106 | *oplock = parse_lease_state(server, rsp, &oparms->fid->epoch); |
| 1106 | else | 1107 | else |
| 1107 | *oplock = rsp->OplockLevel; | 1108 | *oplock = rsp->OplockLevel; |
| 1108 | creat_exit: | 1109 | creat_exit: |
