Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42873b0a authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French
Browse files

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: default avatarPavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent f047390a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ cifs_alloc_inode(struct super_block *sb)
	cifs_inode->server_eof = 0;
	cifs_inode->uniqueid = 0;
	cifs_inode->createtime = 0;
	cifs_inode->epoch = 0;
#ifdef CONFIG_CIFS_SMB2
	get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
#endif
+10 −3
Original line number Diff line number Diff line
@@ -373,11 +373,12 @@ struct smb_version_operations {
	/* if we can do cache read operations */
	bool (*is_read_op)(__u32);
	/* 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 */
	char * (*create_lease_buf)(u8 *, u8);
	/* parse lease context buffer and return oplock info */
	__u8 (*parse_lease_buf)(void *);
	/* parse lease context buffer and return oplock/epoch info */
	__u8 (*parse_lease_buf)(void *, unsigned int *);
};

struct smb_version_values {
@@ -940,6 +941,8 @@ struct cifs_fid {
	__u8 lease_key[SMB2_LEASE_KEY_SIZE];	/* lease key for smb2 */
#endif
	struct cifs_pending_open *pending_open;
	unsigned int epoch;
	bool purge_cache;
};

struct cifs_fid_locks {
@@ -1039,7 +1042,10 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);

#define CIFS_CACHE_READ_FLG	1
#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_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_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
@@ -1057,6 +1063,7 @@ struct cifsInodeInfo {
	struct list_head openFileList;
	__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
	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 invalid_mapping;		/* pagecache is invalid */
	unsigned long time;		/* jiffies of last update of inode */
+4 −0
Original line number Diff line number Diff line
@@ -323,6 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
		oplock = fid->pending_open->oplock;
	list_del(&fid->pending_open->olist);

	fid->purge_cache = false;
	server->ops->set_fid(cfile, fid, oplock);

	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);
	spin_unlock(&cifs_file_list_lock);

	if (fid->purge_cache)
		cifs_invalidate_mapping(inode);

	file->private_data = cfile;
	return cfile;
}
+4 −2
Original line number Diff line number Diff line
@@ -420,6 +420,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
	__u8 lease_state;
	struct list_head *tmp;
	struct cifsFileInfo *cfile;
	struct TCP_Server_Info *server = tcon->ses->server;
	struct cifs_pending_open *open;
	struct cifsInodeInfo *cinode;
	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",
			 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)
			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;

				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);

+48 −9
Original line number Diff line number Diff line
@@ -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,
};
Loading