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>
This commit is contained in:
parent
f047390a09
commit
42873b0a28
|
@ -255,6 +255,7 @@ cifs_alloc_inode(struct super_block *sb)
|
||||||
cifs_inode->server_eof = 0;
|
cifs_inode->server_eof = 0;
|
||||||
cifs_inode->uniqueid = 0;
|
cifs_inode->uniqueid = 0;
|
||||||
cifs_inode->createtime = 0;
|
cifs_inode->createtime = 0;
|
||||||
|
cifs_inode->epoch = 0;
|
||||||
#ifdef CONFIG_CIFS_SMB2
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
|
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -373,11 +373,12 @@ struct smb_version_operations {
|
||||||
/* if we can do cache read operations */
|
/* if we can do cache read operations */
|
||||||
bool (*is_read_op)(__u32);
|
bool (*is_read_op)(__u32);
|
||||||
/* set oplock level for the inode */
|
/* set oplock level for the inode */
|
||||||
void (*set_oplock_level)(struct cifsInodeInfo *, __u32);
|
void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int,
|
||||||
|
bool *);
|
||||||
/* create lease context buffer for CREATE request */
|
/* create lease context buffer for CREATE request */
|
||||||
char * (*create_lease_buf)(u8 *, u8);
|
char * (*create_lease_buf)(u8 *, u8);
|
||||||
/* parse lease context buffer and return oplock info */
|
/* parse lease context buffer and return oplock/epoch info */
|
||||||
__u8 (*parse_lease_buf)(void *);
|
__u8 (*parse_lease_buf)(void *, unsigned int *);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
|
@ -940,6 +941,8 @@ struct cifs_fid {
|
||||||
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
|
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
|
||||||
#endif
|
#endif
|
||||||
struct cifs_pending_open *pending_open;
|
struct cifs_pending_open *pending_open;
|
||||||
|
unsigned int epoch;
|
||||||
|
bool purge_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cifs_fid_locks {
|
struct cifs_fid_locks {
|
||||||
|
@ -1039,7 +1042,10 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||||
|
|
||||||
#define CIFS_CACHE_READ_FLG 1
|
#define CIFS_CACHE_READ_FLG 1
|
||||||
#define CIFS_CACHE_HANDLE_FLG 2
|
#define CIFS_CACHE_HANDLE_FLG 2
|
||||||
|
#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG)
|
||||||
#define CIFS_CACHE_WRITE_FLG 4
|
#define CIFS_CACHE_WRITE_FLG 4
|
||||||
|
#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
|
||||||
|
#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)
|
||||||
|
|
||||||
#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG)
|
#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG)
|
||||||
#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
|
#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
|
||||||
|
@ -1057,6 +1063,7 @@ struct cifsInodeInfo {
|
||||||
struct list_head openFileList;
|
struct list_head openFileList;
|
||||||
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
|
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
|
||||||
unsigned int oplock; /* oplock/lease level we have */
|
unsigned int oplock; /* oplock/lease level we have */
|
||||||
|
unsigned int epoch; /* used to track lease state changes */
|
||||||
bool delete_pending; /* DELETE_ON_CLOSE is set */
|
bool delete_pending; /* DELETE_ON_CLOSE is set */
|
||||||
bool invalid_mapping; /* pagecache is invalid */
|
bool invalid_mapping; /* pagecache is invalid */
|
||||||
unsigned long time; /* jiffies of last update of inode */
|
unsigned long time; /* jiffies of last update of inode */
|
||||||
|
|
|
@ -323,6 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||||
oplock = fid->pending_open->oplock;
|
oplock = fid->pending_open->oplock;
|
||||||
list_del(&fid->pending_open->olist);
|
list_del(&fid->pending_open->olist);
|
||||||
|
|
||||||
|
fid->purge_cache = false;
|
||||||
server->ops->set_fid(cfile, fid, oplock);
|
server->ops->set_fid(cfile, fid, oplock);
|
||||||
|
|
||||||
list_add(&cfile->tlist, &tcon->openFileList);
|
list_add(&cfile->tlist, &tcon->openFileList);
|
||||||
|
@ -333,6 +334,9 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||||
list_add_tail(&cfile->flist, &cinode->openFileList);
|
list_add_tail(&cfile->flist, &cinode->openFileList);
|
||||||
spin_unlock(&cifs_file_list_lock);
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
|
||||||
|
if (fid->purge_cache)
|
||||||
|
cifs_invalidate_mapping(inode);
|
||||||
|
|
||||||
file->private_data = cfile;
|
file->private_data = cfile;
|
||||||
return cfile;
|
return cfile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,6 +420,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
||||||
__u8 lease_state;
|
__u8 lease_state;
|
||||||
struct list_head *tmp;
|
struct list_head *tmp;
|
||||||
struct cifsFileInfo *cfile;
|
struct cifsFileInfo *cfile;
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
struct cifs_pending_open *open;
|
struct cifs_pending_open *open;
|
||||||
struct cifsInodeInfo *cinode;
|
struct cifsInodeInfo *cinode;
|
||||||
int ack_req = le32_to_cpu(rsp->Flags &
|
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,
|
||||||
cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
|
cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
|
||||||
le32_to_cpu(rsp->NewLeaseState));
|
le32_to_cpu(rsp->NewLeaseState));
|
||||||
|
|
||||||
tcon->ses->server->ops->set_oplock_level(cinode, lease_state);
|
server->ops->set_oplock_level(cinode, lease_state, 0, NULL);
|
||||||
|
|
||||||
if (ack_req)
|
if (ack_req)
|
||||||
cfile->oplock_break_cancelled = false;
|
cfile->oplock_break_cancelled = false;
|
||||||
|
@ -575,7 +576,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
||||||
cfile->oplock_break_cancelled = false;
|
cfile->oplock_break_cancelled = false;
|
||||||
|
|
||||||
server->ops->set_oplock_level(cinode,
|
server->ops->set_oplock_level(cinode,
|
||||||
rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0);
|
rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0,
|
||||||
|
0, NULL);
|
||||||
|
|
||||||
queue_work(cifsiod_wq, &cfile->oplock_break);
|
queue_work(cifsiod_wq, &cfile->oplock_break);
|
||||||
|
|
||||||
|
|
|
@ -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.persistent_fid = fid->persistent_fid;
|
||||||
cfile->fid.volatile_fid = fid->volatile_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);
|
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
|
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;
|
oplock &= 0xFF;
|
||||||
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
||||||
return;
|
return;
|
||||||
if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
|
if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
|
||||||
cinode->oplock = CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG |
|
cinode->oplock = CIFS_CACHE_RHW_FLG;
|
||||||
CIFS_CACHE_HANDLE_FLG;
|
|
||||||
cifs_dbg(FYI, "Batch Oplock granted on inode %p\n",
|
cifs_dbg(FYI, "Batch Oplock granted on inode %p\n",
|
||||||
&cinode->vfs_inode);
|
&cinode->vfs_inode);
|
||||||
} else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
|
} 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",
|
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
|
||||||
&cinode->vfs_inode);
|
&cinode->vfs_inode);
|
||||||
} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
|
} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
|
||||||
|
@ -674,7 +675,8 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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};
|
char message[5] = {0};
|
||||||
|
|
||||||
|
@ -701,6 +703,41 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
|
||||||
&cinode->vfs_inode);
|
&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
|
static bool
|
||||||
smb2_is_read_op(__u32 oplock)
|
smb2_is_read_op(__u32 oplock)
|
||||||
{
|
{
|
||||||
|
@ -780,20 +817,22 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock)
|
||||||
}
|
}
|
||||||
|
|
||||||
static __u8
|
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;
|
struct create_lease *lc = (struct create_lease *)buf;
|
||||||
|
|
||||||
|
*epoch = 0; /* not used */
|
||||||
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
|
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
|
||||||
return SMB2_OPLOCK_LEVEL_NOCHANGE;
|
return SMB2_OPLOCK_LEVEL_NOCHANGE;
|
||||||
return le32_to_cpu(lc->lcontext.LeaseState);
|
return le32_to_cpu(lc->lcontext.LeaseState);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __u8
|
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;
|
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)
|
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
|
||||||
return SMB2_OPLOCK_LEVEL_NOCHANGE;
|
return SMB2_OPLOCK_LEVEL_NOCHANGE;
|
||||||
return le32_to_cpu(lc->lcontext.LeaseState);
|
return le32_to_cpu(lc->lcontext.LeaseState);
|
||||||
|
@ -1009,7 +1048,7 @@ struct smb_version_operations smb30_operations = {
|
||||||
.generate_signingkey = generate_smb3signingkey,
|
.generate_signingkey = generate_smb3signingkey,
|
||||||
.calc_signature = smb3_calc_signature,
|
.calc_signature = smb3_calc_signature,
|
||||||
.is_read_op = smb21_is_read_op,
|
.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,
|
.create_lease_buf = smb3_create_lease_buf,
|
||||||
.parse_lease_buf = smb3_parse_lease_buf,
|
.parse_lease_buf = smb3_parse_lease_buf,
|
||||||
};
|
};
|
||||||
|
|
|
@ -903,7 +903,8 @@ create_reconnect_durable_buf(struct cifs_fid *fid)
|
||||||
}
|
}
|
||||||
|
|
||||||
static __u8
|
static __u8
|
||||||
parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp)
|
parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp,
|
||||||
|
unsigned int *epoch)
|
||||||
{
|
{
|
||||||
char *data_offset;
|
char *data_offset;
|
||||||
struct create_context *cc;
|
struct create_context *cc;
|
||||||
|
@ -920,7 +921,7 @@ parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp)
|
||||||
next = le32_to_cpu(cc->Next);
|
next = le32_to_cpu(cc->Next);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return server->ops->parse_lease_buf(cc);
|
return server->ops->parse_lease_buf(cc, epoch);
|
||||||
} while (next != 0);
|
} while (next != 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1102,7 +1103,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
|
if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
|
||||||
*oplock = parse_lease_state(server, rsp);
|
*oplock = parse_lease_state(server, rsp, &oparms->fid->epoch);
|
||||||
else
|
else
|
||||||
*oplock = rsp->OplockLevel;
|
*oplock = rsp->OplockLevel;
|
||||||
creat_exit:
|
creat_exit:
|
||||||
|
|
Loading…
Reference in New Issue