summaryrefslogtreecommitdiff
path: root/fs/cifs/smb2ops.c
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2013-09-05 21:30:16 +0400
committerSteve French <smfrench@gmail.com>2013-09-09 22:52:18 -0500
commit42873b0a282ac84a56e0e48c408beb62d0ad2917 (patch)
tree14891ca4e47ebfa0a3668b68772489e6bdd805d1 /fs/cifs/smb2ops.c
parentf047390a097e907ccccf8aa894dec49890578a1a (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/smb2ops.c')
-rw-r--r--fs/cifs/smb2ops.c57
1 files changed, 48 insertions, 9 deletions
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)
cfile->fid.persistent_fid = fid->persistent_fid;
cfile->fid.volatile_fid = fid->volatile_fid;
- server->ops->set_oplock_level(cinode, oplock);
+ server->ops->set_oplock_level(cinode, oplock, fid->epoch,
+ &fid->purge_cache);
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
}
@@ -651,18 +652,18 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
}
static void
-smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
+smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
{
oplock &= 0xFF;
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
return;
if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
- cinode->oplock = CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG |
- CIFS_CACHE_HANDLE_FLG;
+ cinode->oplock = CIFS_CACHE_RHW_FLG;
cifs_dbg(FYI, "Batch Oplock granted on inode %p\n",
&cinode->vfs_inode);
} else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
- cinode->oplock = CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG;
+ cinode->oplock = CIFS_CACHE_RW_FLG;
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
&cinode->vfs_inode);
} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
@@ -674,7 +675,8 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
}
static void
-smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
+smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
{
char message[5] = {0};
@@ -701,6 +703,41 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
&cinode->vfs_inode);
}
+static void
+smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ unsigned int old_oplock = cinode->oplock;
+
+ smb21_set_oplock_level(cinode, oplock, epoch, purge_cache);
+
+ if (purge_cache) {
+ *purge_cache = false;
+ if (old_oplock == CIFS_CACHE_READ_FLG) {
+ if (cinode->oplock == CIFS_CACHE_READ_FLG &&
+ (epoch - cinode->epoch > 0))
+ *purge_cache = true;
+ else if (cinode->oplock == CIFS_CACHE_RH_FLG &&
+ (epoch - cinode->epoch > 1))
+ *purge_cache = true;
+ else if (cinode->oplock == CIFS_CACHE_RHW_FLG &&
+ (epoch - cinode->epoch > 1))
+ *purge_cache = true;
+ else if (cinode->oplock == 0 &&
+ (epoch - cinode->epoch > 0))
+ *purge_cache = true;
+ } else if (old_oplock == CIFS_CACHE_RH_FLG) {
+ if (cinode->oplock == CIFS_CACHE_RH_FLG &&
+ (epoch - cinode->epoch > 0))
+ *purge_cache = true;
+ else if (cinode->oplock == CIFS_CACHE_RHW_FLG &&
+ (epoch - cinode->epoch > 1))
+ *purge_cache = true;
+ }
+ cinode->epoch = epoch;
+ }
+}
+
static bool
smb2_is_read_op(__u32 oplock)
{
@@ -780,20 +817,22 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock)
}
static __u8
-smb2_parse_lease_buf(void *buf)
+smb2_parse_lease_buf(void *buf, unsigned int *epoch)
{
struct create_lease *lc = (struct create_lease *)buf;
+ *epoch = 0; /* not used */
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
return SMB2_OPLOCK_LEVEL_NOCHANGE;
return le32_to_cpu(lc->lcontext.LeaseState);
}
static __u8
-smb3_parse_lease_buf(void *buf)
+smb3_parse_lease_buf(void *buf, unsigned int *epoch)
{
struct create_lease_v2 *lc = (struct create_lease_v2 *)buf;
+ *epoch = le16_to_cpu(lc->lcontext.Epoch);
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
return SMB2_OPLOCK_LEVEL_NOCHANGE;
return le32_to_cpu(lc->lcontext.LeaseState);
@@ -1009,7 +1048,7 @@ struct smb_version_operations smb30_operations = {
.generate_signingkey = generate_smb3signingkey,
.calc_signature = smb3_calc_signature,
.is_read_op = smb21_is_read_op,
- .set_oplock_level = smb21_set_oplock_level,
+ .set_oplock_level = smb3_set_oplock_level,
.create_lease_buf = smb3_create_lease_buf,
.parse_lease_buf = smb3_parse_lease_buf,
};