CIFS: Process oplocks for SMB2

Signed-off-by: Pavel Shilovsky <piastryyy@gmail.com>
Signed-off-by: Steve French <smfrench@gmail.com>
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 0fa086c..2d88c90 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -23,6 +23,7 @@
 #include "smb2proto.h"
 #include "cifsproto.h"
 #include "cifs_debug.h"
+#include "smb2status.h"
 
 static int
 change_conf(struct TCP_Server_Info *server)
@@ -207,13 +208,14 @@
 	int rc;
 	__u64 persistent_fid, volatile_fid;
 	__le16 *utf16_path;
+	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
 	if (!utf16_path)
 		return -ENOMEM;
 
 	rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
-		       FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, NULL);
+		       FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
 	if (rc) {
 		kfree(utf16_path);
 		return rc;
@@ -358,10 +360,10 @@
 static void
 smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
 {
-	/* struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); */
+	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
 	cfile->fid.persistent_fid = fid->persistent_fid;
 	cfile->fid.volatile_fid = fid->volatile_fid;
-	/* cifs_set_oplock_level(cinode, oplock); */
+	smb2_set_oplock_level(cinode, oplock);
 	/* cinode->can_cache_brlcks = cinode->clientCanCacheAll; */
 }
 
@@ -432,6 +434,7 @@
 {
 	__le16 *utf16_path;
 	int rc;
+	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
 	__u64 persistent_fid, volatile_fid;
 
 	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
@@ -440,7 +443,7 @@
 
 	rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
 		       FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0,
-		       NULL);
+		       &oplock, NULL);
 	kfree(utf16_path);
 	if (rc) {
 		cERROR(1, "open dir failed");
@@ -477,6 +480,28 @@
 	return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
 }
 
+/*
+* If we negotiate SMB2 protocol and get STATUS_PENDING - update
+* the number of credits and return true. Otherwise - return false.
+*/
+static bool
+smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
+{
+	struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+
+	if (le32_to_cpu(hdr->Status) != STATUS_PENDING)
+		return false;
+
+	if (!length) {
+		spin_lock(&server->req_lock);
+		server->credits += le16_to_cpu(hdr->CreditRequest);
+		spin_unlock(&server->req_lock);
+		wake_up(&server->request_q);
+	}
+
+	return true;
+}
+
 struct smb_version_operations smb21_operations = {
 	.setup_request = smb2_setup_request,
 	.setup_async_request = smb2_setup_async_request,
@@ -530,6 +555,7 @@
 	.query_dir_next = smb2_query_dir_next,
 	.close_dir = smb2_close_dir,
 	.calc_smb_size = smb2_calc_size,
+	.is_status_pending = smb2_is_status_pending,
 };
 
 struct smb_version_values smb21_values = {