[PATCH v2 45/53] CIFS: Process oplock/lease break for SMB2/2.1

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Pavel Shilovsky <piastryyy@xxxxxxxxx>

Signed-off-by: Pavel Shilovsky <piastryyy@xxxxxxxxx>
---
 fs/cifs/cifsglob.h  |    3 +
 fs/cifs/cifsproto.h |    4 +-
 fs/cifs/connect.c   |    2 +-
 fs/cifs/file.c      |   24 +++++--
 fs/cifs/misc.c      |   11 ++-
 fs/cifs/smb2file.c  |   39 ++++++------
 fs/cifs/smb2misc.c  |  180 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/cifs/smb2pdu.c   |   90 ++++++++++++++++++++------
 fs/cifs/smb2pdu.h   |   35 ++++++++++
 fs/cifs/smb2proto.h |   14 +++-
 10 files changed, 348 insertions(+), 54 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 3d950d9..56962a5 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -697,6 +697,9 @@ struct cifsInodeInfo {
 #endif
 };
 
+typedef int (oplock_callback_t)(struct cifs_tcon *, struct cifsInodeInfo *,
+				struct cifsFileInfo *);
+
 static inline struct cifsInodeInfo *
 CIFS_I(struct inode *inode)
 {
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 4f1045b..a61da92 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -94,7 +94,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
 			struct smb_hdr *out_buf,
 			int *bytes_returned);
 extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
-extern bool is_valid_oplock_break(struct smb_hdr *smb,
+extern bool is_valid_oplock_break(char *buffer,
 				  struct TCP_Server_Info *);
 extern bool backup_cred(struct cifs_sb_info *);
 extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
@@ -132,6 +132,8 @@ extern struct cifsFileInfo *cifs_new_fileinfo(__u16 netfid, struct file *file,
 					      __u32 oplock);
 extern struct cifsFileInfo *cifs_new_fileinfo_generic(struct file *file,
 						      struct tcon_link *tlink);
+extern void cifs_oplock_break_generic(struct work_struct *work,
+				      oplock_callback_t *oplock_cb);
 extern int cifs_posix_open(const char *full_path, struct inode **pinode,
 			   struct super_block *sb, int mode,
 			   unsigned int f_flags, __u32 *poplock, __u16 *pnetfid,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index e46c2bd..c8a94a4 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -960,7 +960,7 @@ cifs_demultiplex_thread(void *p)
 		if (mid_entry != NULL) {
 			if (!mid_entry->multiRsp || mid_entry->multiEnd)
 				mid_entry->callback(mid_entry);
-		} else if (!is_valid_oplock_break((void *)buf, server)) {
+		} else if (!is_valid_oplock_break(buf, server)) {
 			cERROR(1, "No task to wake, unknown frame received! "
 				   "NumMids %d", atomic_read(&midCount));
 			cifs_dump_mem("Received Data is: ", buf, buf_size);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 09655f7..a43856a 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -257,6 +257,7 @@ cifs_new_fileinfo(__u16 netfid, struct file *file,
 	cifs_file->netfid = netfid;
 	cifs_set_oplock_level(cinode, oplock);
 	cinode->can_cache_brlcks = cinode->clientCanCacheAll;
+	INIT_WORK(&cifs_file->oplock_break, cifs_oplock_break);
 	return cifs_file;
 }
 
@@ -280,7 +281,6 @@ cifs_new_fileinfo_generic(struct file *file, struct tcon_link *tlink)
 	pCifsFile->invalidHandle = false;
 	pCifsFile->tlink = cifs_get_tlink(tlink);
 	mutex_init(&pCifsFile->fh_mutex);
-	INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
 
 	spin_lock(&cifs_file_list_lock);
 	list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
@@ -3088,7 +3088,8 @@ int cifs_launder_page(struct page *page)
 	return rc;
 }
 
-void cifs_oplock_break(struct work_struct *work)
+void cifs_oplock_break_generic(struct work_struct *work,
+			       oplock_callback_t *oplock_cb)
 {
 	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
 						  oplock_break);
@@ -3121,14 +3122,25 @@ void cifs_oplock_break(struct work_struct *work)
 	 * disconnected since oplock already released by the server
 	 */
 	if (!cfile->oplock_break_cancelled) {
-		rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid,
-				 current->tgid, 0, 0, 0, 0,
-				 LOCKING_ANDX_OPLOCK_RELEASE, false,
-				 cinode->clientCanCacheRead ? 1 : 0);
+		rc = oplock_cb(tlink_tcon(cfile->tlink), cinode, cfile);
 		cFYI(1, "Oplock release rc = %d", rc);
 	}
 }
 
+static int
+cifs_oplock_cb(struct cifs_tcon *tcon, struct cifsInodeInfo *cinode,
+	       struct cifsFileInfo *cfile)
+{
+	return CIFSSMBLock(0, tcon, cfile->netfid, current->tgid, 0, 0, 0, 0,
+			   LOCKING_ANDX_OPLOCK_RELEASE, false,
+			   cinode->clientCanCacheRead ? 1 : 0);
+}
+
+void cifs_oplock_break(struct work_struct *work)
+{
+	cifs_oplock_break_generic(work, cifs_oplock_cb);
+}
+
 const struct address_space_operations cifs_addr_ops = {
 	.readpage = cifs_readpage,
 	.readpages = cifs_readpages,
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index d291463..88bede1 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -29,6 +29,9 @@
 #include "smberr.h"
 #include "nterr.h"
 #include "cifs_unicode.h"
+#ifdef CONFIG_CIFS_SMB2
+#include "smb2proto.h"
+#endif
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
@@ -505,8 +508,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read)
 }
 
 bool
-is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
+is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
 {
+	struct smb_hdr *buf = (struct smb_hdr *)buffer;
 	struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
 	struct list_head *tmp, *tmp1, *tmp2;
 	struct cifs_ses *ses;
@@ -516,7 +520,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 
 #ifdef CONFIG_CIFS_SMB2
 	if (srv->is_smb2)
-		return false;
+		return smb2_is_valid_oplock_break(buffer, srv);
 #endif
 
 	cFYI(1, "Checking for oplock break or dnotify response");
@@ -592,9 +596,10 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 
 				cifs_set_oplock_level(pCifsInode,
 					pSMB->OplockLevel ? OPLOCK_READ : 0);
+				netfile->oplock_break_cancelled = false;
+
 				queue_work(system_nrt_wq,
 					   &netfile->oplock_break);
-				netfile->oplock_break_cancelled = false;
 
 				spin_unlock(&cifs_file_list_lock);
 				spin_unlock(&cifs_tcp_ses_lock);
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index 58f9edb..a8cb423 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -178,25 +178,6 @@ const struct address_space_operations smb2_addr_ops_smallbuf = {
 	.launder_page = cifs_launder_page,
 };
 
-static void
-smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u8 oplock)
-{
-	if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
-		cinode->clientCanCacheAll = true;
-		cinode->clientCanCacheRead = true;
-		cFYI(1, "Exclusive Oplock granted on inode %p",
-		     &cinode->vfs_inode);
-	} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
-		cinode->clientCanCacheAll = false;
-		cinode->clientCanCacheRead = true;
-		cFYI(1, "Level II Oplock granted on inode %p",
-		    &cinode->vfs_inode);
-	} else {
-		cinode->clientCanCacheAll = false;
-		cinode->clientCanCacheRead = false;
-	}
-}
-
 struct cifsFileInfo *
 smb2_new_fileinfo(__u64 persist_fid, __u64 volatile_fid, struct file *file,
 		  struct tcon_link *tlink, __u8 oplock)
@@ -209,6 +190,7 @@ smb2_new_fileinfo(__u64 persist_fid, __u64 volatile_fid, struct file *file,
 	cifs_file->persist_fid = persist_fid;
 	cifs_file->volatile_fid = volatile_fid;
 	smb2_set_oplock_level(CIFS_I(file->f_path.dentry->d_inode), oplock);
+	INIT_WORK(&cifs_file->oplock_break, smb2_oplock_break);
 	return cifs_file;
 }
 
@@ -515,3 +497,22 @@ int smb2_strict_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 	return cifs_strict_fsync_generic(file, start, end, datasync,
 					 smb2_fsync_cb);
 }
+
+static int
+smb2_oplock_cb(struct cifs_tcon *tcon, struct cifsInodeInfo *cinode,
+	       struct cifsFileInfo *cfile)
+{
+	if (tcon->ses->server->smb2_dialect_revision == cpu_to_le16(0x0210))
+		return SMB2_lease_break(0, tlink_tcon(cfile->tlink),
+					cinode->lease_key,
+					smb2_get_lease_state(cinode));
+
+	return SMB2_oplock_break(0, tlink_tcon(cfile->tlink),
+				 cfile->persist_fid, cfile->volatile_fid,
+				 cinode->clientCanCacheRead ? 1 : 0);
+}
+
+void smb2_oplock_break(struct work_struct *work)
+{
+	cifs_oplock_break_generic(work, smb2_oplock_cb);
+}
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 6f7afdc..6b64cd8 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -151,12 +151,19 @@ checkSMB2(struct smb2_hdr *smb, __u64 mid, unsigned int length)
 
 	if (smb2_rsp_struct_sizes[command] !=
 	    le16_to_cpu(smb2->StructureSize2)) {
-		if ((smb->Status == 0) ||
-		    (le16_to_cpu(smb2->StructureSize2) != 9)) {
+		if (command != SMB2OPLOCK_BREAK && ((smb->Status == 0) ||
+		    (le16_to_cpu(smb2->StructureSize2) != 9))) {
 			/* error packets have 9 byte structure size */
 			cERROR(1, "Illegal response size %d for command %d",
 				   le16_to_cpu(smb2->StructureSize2), command);
 			return 1;
+		} else if (command == SMB2OPLOCK_BREAK && (smb->Status == 0)
+			   && (le16_to_cpu(smb2->StructureSize2) != 44)
+			   && (le16_to_cpu(smb2->StructureSize2) != 36)) {
+			/* special case for SMB2.1 lease break message */
+			cERROR(1, "Illegal response size %d for oplock break",
+				   le16_to_cpu(smb2->StructureSize2));
+			return 1;
 		}
 	}
 
@@ -171,6 +178,8 @@ checkSMB2(struct smb2_hdr *smb, __u64 mid, unsigned int length)
 	if (4 + len != clc_len) {
 		cFYI(1, "Calculated size %d length %d mismatch for mid %lld",
 			 clc_len, 4 + len, smb->MessageId);
+		if (clc_len + 20 == len && command == SMB2OPLOCK_BREAK)
+			return 0; /* Windows 7 server returns 24 bytes more */
 		if (clc_len == 4 + len + 1) /* BB FIXME (fix samba) */
 			return 0; /* BB workaround Samba 3 bug SessSetup rsp */
 		return 1;
@@ -345,3 +354,170 @@ calc_size_exit:
 	cFYI(1, "smb2 len %d", len);
 	return len;
 }
+
+__le32
+smb2_get_lease_state(struct cifsInodeInfo *cinode)
+{
+	if (cinode->clientCanCacheAll)
+		return SMB2_LEASE_WRITE_CACHING;
+	else if (cinode->clientCanCacheRead)
+		return SMB2_LEASE_READ_CACHING;
+	return 0;
+}
+
+void
+smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u8 oplock)
+{
+	if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+		cinode->clientCanCacheAll = true;
+		cinode->clientCanCacheRead = true;
+		cFYI(1, "Exclusive Oplock granted on inode %p",
+		     &cinode->vfs_inode);
+	} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
+		cinode->clientCanCacheAll = false;
+		cinode->clientCanCacheRead = true;
+		cFYI(1, "Level II Oplock granted on inode %p",
+		    &cinode->vfs_inode);
+	} else {
+		cinode->clientCanCacheAll = false;
+		cinode->clientCanCacheRead = false;
+	}
+}
+
+__u8 smb2_map_lease_to_oplock(__le32 lease_state)
+{
+	if (lease_state & SMB2_LEASE_WRITE_CACHING) {
+		if (lease_state & SMB2_LEASE_HANDLE_CACHING)
+			return SMB2_OPLOCK_LEVEL_BATCH;
+		else
+			return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+	} else if (lease_state & SMB2_LEASE_READ_CACHING)
+		return SMB2_OPLOCK_LEVEL_II;
+	return 0;
+}
+
+static bool
+smb2_is_valid_lease_break(struct smb2_hdr *buf, struct TCP_Server_Info *srv)
+{
+	struct lease_break *pSMB = (struct lease_break *)buf;
+	struct list_head *tmp, *tmp1, *tmp2;
+	struct cifs_ses *ses;
+	struct cifs_tcon *tcon;
+	struct cifsInodeInfo *cinode;
+	struct cifsFileInfo *cfile;
+
+	cFYI(1, "Checking for lease break");
+
+	/* look up tcon based on tid & uid */
+	spin_lock(&cifs_tcp_ses_lock);
+	list_for_each(tmp, &srv->smb_ses_list) {
+		ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+		list_for_each(tmp1, &ses->tcon_list) {
+			tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+
+			cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
+			spin_lock(&cifs_file_list_lock);
+			list_for_each(tmp2, &tcon->openFileList) {
+				cfile = list_entry(tmp2, struct cifsFileInfo,
+						     tlist);
+				cinode = CIFS_I(cfile->dentry->d_inode);
+
+				if (memcmp(cinode->lease_key, pSMB->LeaseKey,
+					   16))
+					continue;
+
+				cFYI(1, "lease key match, lease break 0x%d",
+				     le32_to_cpu(pSMB->NewLeaseState));
+
+				smb2_set_oplock_level(cinode,
+				 smb2_map_lease_to_oplock(pSMB->NewLeaseState));
+
+				if (pSMB->Flags &
+				    SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED)
+					cfile->oplock_break_cancelled = false;
+				else
+					cfile->oplock_break_cancelled = true;
+
+				queue_work(system_nrt_wq,
+					   &cfile->oplock_break);
+
+				spin_unlock(&cifs_file_list_lock);
+				spin_unlock(&cifs_tcp_ses_lock);
+				return true;
+			}
+			spin_unlock(&cifs_file_list_lock);
+		}
+	}
+	spin_unlock(&cifs_tcp_ses_lock);
+	cFYI(1, "Can not process oplock break for non-existent connection");
+	return false;
+}
+
+bool
+smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
+{
+	struct smb2_hdr *buf = (struct smb2_hdr *)buffer;
+	struct oplock_break *pSMB = (struct oplock_break *)buf;
+	struct list_head *tmp, *tmp1, *tmp2;
+	struct cifs_ses *ses;
+	struct cifs_tcon *tcon;
+	struct cifsInodeInfo *cinode;
+	struct cifsFileInfo *cfile;
+
+	cFYI(1, "Checking for oplock break");
+
+	if (pSMB->hdr.Command != SMB2_OPLOCK_BREAK)
+		return false;
+
+	if (le16_to_cpu(pSMB->StructureSize) !=
+				smb2_rsp_struct_sizes[SMB2OPLOCK_BREAK]) {
+		if (le16_to_cpu(pSMB->StructureSize) == 44)
+			return smb2_is_valid_lease_break(buf, srv);
+		else
+			return false;
+	}
+
+	cFYI(1, "oplock level 0x%d", pSMB->OplockLevel);
+
+	/* look up tcon based on tid & uid */
+	spin_lock(&cifs_tcp_ses_lock);
+	list_for_each(tmp, &srv->smb_ses_list) {
+		ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+		list_for_each(tmp1, &ses->tcon_list) {
+			tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+
+			cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
+			spin_lock(&cifs_file_list_lock);
+			list_for_each(tmp2, &tcon->openFileList) {
+				cfile = list_entry(tmp2, struct cifsFileInfo,
+						     tlist);
+				if (pSMB->PersistentFid != cfile->persist_fid)
+					continue;
+
+				if (pSMB->VolatileFid != cfile->volatile_fid)
+					continue;
+
+				cFYI(1, "file id match, oplock break");
+				cinode = CIFS_I(cfile->dentry->d_inode);
+
+				smb2_set_oplock_level(cinode,
+				  pSMB->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0);
+				cfile->oplock_break_cancelled = false;
+
+				queue_work(system_nrt_wq,
+					   &cfile->oplock_break);
+
+				spin_unlock(&cifs_file_list_lock);
+				spin_unlock(&cifs_tcp_ses_lock);
+				return true;
+			}
+			spin_unlock(&cifs_file_list_lock);
+			spin_unlock(&cifs_tcp_ses_lock);
+			cFYI(1, "No matching file for oplock break");
+			return true;
+		}
+	}
+	spin_unlock(&cifs_tcp_ses_lock);
+	cFYI(1, "Can not process oplock break for non-existent connection");
+	return false;
+}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index bcc3513..25d7ac2 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1088,7 +1088,6 @@ parse_lease_state(struct create_rsp *smb)
 {
 	char *data_offset;
 	struct create_lease *lc;
-	__u8 oplock = 0;
 	bool found = false;
 
 	data_offset = (char *)smb;
@@ -1111,19 +1110,9 @@ parse_lease_state(struct create_rsp *smb)
 	} while (le32_to_cpu(lc->ccontext.Next) != 0);
 
 	if (!found)
-		return oplock;
-
-	if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_WRITE_CACHING) {
-		if (le32_to_cpu(lc->lcontext.LeaseState) &
-						SMB2_LEASE_HANDLE_CACHING)
-			oplock = SMB2_OPLOCK_LEVEL_BATCH;
-		else
-			oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
-	} else if (le32_to_cpu(lc->lcontext.LeaseState) &
-						SMB2_LEASE_READ_CACHING)
-		oplock = SMB2_OPLOCK_LEVEL_II;
+		return 0;
 
-	return oplock;
+	return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
 }
 
 int SMB2_open(const int xid, struct cifs_tcon *tcon, __le16 *path,
@@ -1901,12 +1890,6 @@ qfsdev_exit:
 	return rc;
 }
 
-int SMB2_oplock_break(struct cifs_tcon *ptcon, __u64 netfid)
-{
-	/* BB FIXME BB */
-	return -EOPNOTSUPP;
-}
-
 int SMB2_query_info(const int xid, struct cifs_tcon *tcon,
 		    u64 persistent_fid, u64 volatile_fid,
 		    FILE_ALL_INFO_SMB2 *pdata)
@@ -2784,3 +2767,72 @@ int SMB2_lock(const int xid, struct cifs_tcon *tcon,
 	 */
 	return rc;
 }
+
+int
+SMB2_oplock_break(const int xid, struct cifs_tcon *tcon,
+		 const u64 persistent_fid, const u64 volatile_fid,
+		 __u8 oplock_level)
+{
+	int rc = 0, buf_type, status;
+	struct oplock_break *pSMB2 = NULL;
+	struct kvec iov[1];
+
+	cFYI(1, "SMB2_oplock_break");
+	rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &pSMB2);
+
+	if (rc)
+		return rc;
+
+	pSMB2->VolatileFid = volatile_fid;
+	pSMB2->PersistentFid = persistent_fid;
+	pSMB2->OplockLevel = oplock_level;
+
+	iov->iov_base = (char *)pSMB2;
+	iov->iov_len = get_rfc1002_length(pSMB2) + 4;
+
+	rc = smb2_sendrcv2(xid, tcon->ses, iov, 1, &buf_type, &status,
+			   CIFS_STD_OP | CIFS_LOG_ERROR);
+	/* SMB2 buffer freed by function above */
+
+	if (rc) {
+		cifs_stats_fail_inc(tcon, SMB2OPLOCK_BREAK);
+		cFYI(1, "Send error in Oplock Break = %d", rc);
+	}
+
+	return rc;
+}
+
+int
+SMB2_lease_break(const int xid, struct cifs_tcon *tcon, __u8 *lease_key,
+		 const __le32 lease_state)
+{
+	int rc = 0, buf_type, status;
+	struct lease_ack *pSMB2 = NULL;
+	struct kvec iov[1];
+
+	cFYI(1, "SMB2_lease_break");
+	rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &pSMB2);
+
+	if (rc)
+		return rc;
+
+	pSMB2->StructureSize = cpu_to_le16(36);
+	inc_rfc1001_len(&pSMB2->hdr, 12);
+
+	memcpy(pSMB2->LeaseKey, lease_key, 16);
+	pSMB2->LeaseState = lease_state;
+
+	iov->iov_base = (char *)pSMB2;
+	iov->iov_len = get_rfc1002_length(pSMB2) + 4;
+
+	rc = smb2_sendrcv2(xid, tcon->ses, iov, 1, &buf_type, &status,
+			   CIFS_STD_OP | CIFS_LOG_ERROR);
+	/* SMB2 buffer freed by function above */
+
+	if (rc) {
+		cifs_stats_fail_inc(tcon, SMB2OPLOCK_BREAK);
+		cFYI(1, "Send error in Oplock Break = %d", rc);
+	}
+
+	return rc;
+}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 6b8fd2f..3558057 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -741,6 +741,41 @@ struct ioctl_rsp {
 	__u8   Buffer[1];
 }  __attribute__((packed));
 
+struct oplock_break {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 24 */
+	__u8 OplockLevel;
+	__u8 Reserved;
+	__le32 Reserved2;
+	__u64 PersistentFid;
+	__u64 VolatileFid;
+}  __attribute__((packed));
+
+#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
+
+struct lease_break {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 44 */
+	__le16 Reserved;
+	__le32 Flags;
+	__u8 LeaseKey[16];
+	__le32 CurrentLeaseState;
+	__le32 NewLeaseState;
+	__le32 BreakReason;
+	__le32 AccessMaskHint;
+	__le32 ShareMaskHint;
+}  __attribute__((packed));
+
+struct lease_ack {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 36 */
+	__le16 Reserved;
+	__le32 Flags;
+	__u8 LeaseKey[16];
+	__le32 LeaseState;
+	__le64 LeaseDuration;
+}  __attribute__((packed));
+
 /*****************************************************************
  * All constants go here
  *****************************************************************
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 2408be1..490f922 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -200,7 +200,6 @@ extern int SMB2_QFS_attribute_info(const int xid, struct cifs_tcon *tcon,
 				   u64 persistent_fid, u64 volatile_fid);
 extern int SMB2_QFS_device_info(const int xid, struct cifs_tcon *tcon,
 				u64 persistent_fid, u64 volatile_fid);
-extern int SMB2_oplock_break(struct cifs_tcon *ptcon, __u64 netfid);
 extern int SMB2_query_info(const int xid, struct cifs_tcon *tcon,
 			   u64 persistent_file_id, u64 volatile_file_id,
 			   FILE_ALL_INFO_SMB2 *pFindData);
@@ -252,6 +251,11 @@ extern int SMB2_set_ea_info(const int xid, struct cifs_tcon *tcon,
 extern int SMB2_lock(const int xid, struct cifs_tcon *tcon,
 		     const u64 persistent_fid, const u64 volatile_fid,
 		     u64 length, u64 offset, u32 lockFlags, int wait);
+extern int SMB2_oplock_break(const int xid, struct cifs_tcon *tcon,
+			     const u64 persistent_fid, const u64 volatile_fid,
+			     const __u8 oplock_level);
+extern int SMB2_lease_break(const int xid, struct cifs_tcon *tcon,
+			    __u8 *lease_key, const __le32 lease_state);
 extern void DeleteMidQEntryComplex(struct mid_q_entry *midEntry);
 extern int new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
 			unsigned int remaining_bytes, int request_type);
@@ -262,7 +266,11 @@ extern int smb2_wait_on_complex_mid(struct cifs_ses *ses,
 				    unsigned long timeout,
 				    unsigned long time_to_wait);
 extern int smb2_readresp(struct TCP_Server_Info *server, bool cleanup);
-/* extern void smb2_oplock_break(struct work_struct *);
-extern bool is_smb2_oplock_break(struct smb2_hdr *, struct TCP_Server_Info *);*/
+extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
+extern __u8 smb2_map_lease_to_oplock(__le32 lease_state);
+extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u8 oplock);
+extern bool smb2_is_valid_oplock_break(char *buffer,
+				       struct TCP_Server_Info *srv);
+extern void smb2_oplock_break(struct work_struct *work);
 
 #endif			/* _SMB2PROTO_H */
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux