[CIFS] file create with acl support enabled is slow

Shirish Pargaonkar noted:
With cifsacl mount option, when a file is created on the Windows server,
exclusive oplock is broken right away because the get cifs acl code
again opens the file to obtain security descriptor.
The client does not have the newly created file handle or inode in any
of its lists yet so it does not respond to oplock break and server waits for
its duration and then responds to the second open. This slows down file
creation signficantly.  The fix is to pass the file descriptor to the get
cifsacl code wherever available so that get cifs acl code does not send
second open (NT Create ANDX) and oplock is not broken.

CC: Shirish Pargaonkar <shirishp@us.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index f93932c..1f5a428 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsacl.c
  *
- *   Copyright (C) International Business Machines  Corp., 2007
+ *   Copyright (C) International Business Machines  Corp., 2007,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   Contains the routines for mapping CIFS/NTFS ACLs
@@ -556,9 +556,9 @@
 
 /* Retrieve an ACL from the server */
 static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
-				       const char *path)
+				       const char *path, const __u16 *pfid)
 {
-	struct cifsFileInfo *open_file;
+	struct cifsFileInfo *open_file = NULL;
 	int unlock_file = FALSE;
 	int xid;
 	int rc = -EIO;
@@ -573,7 +573,11 @@
 		return NULL;
 
 	xid = GetXid();
-	open_file = find_readable_file(CIFS_I(inode));
+	if (pfid == NULL)
+		open_file = find_readable_file(CIFS_I(inode));
+	else
+		fid = *pfid;
+
 	sb = inode->i_sb;
 	if (sb == NULL) {
 		FreeXid(xid);
@@ -584,7 +588,7 @@
 	if (open_file) {
 		unlock_file = TRUE;
 		fid = open_file->netfid;
-	} else {
+	} else if (pfid == NULL) {
 		int oplock = FALSE;
 		/* open file */
 		rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
@@ -600,10 +604,11 @@
 
 	rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen);
 	cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen));
-	if (unlock_file == TRUE)
+	if (unlock_file == TRUE) /* find_readable_file increments ref count */
 		atomic_dec(&open_file->wrtPending);
-	else
+	else if (pfid == NULL) /* if opened above we have to close the handle */
 		CIFSSMBClose(xid, cifs_sb->tcon, fid);
+	/* else handle was passed in by caller */
 
 	FreeXid(xid);
 	return pntsd;
@@ -664,14 +669,14 @@
 }
 
 /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
-void acl_to_uid_mode(struct inode *inode, const char *path)
+void acl_to_uid_mode(struct inode *inode, const char *path, const __u16 *pfid)
 {
 	struct cifs_ntsd *pntsd = NULL;
 	u32 acllen = 0;
 	int rc = 0;
 
 	cFYI(DBG2, ("converting ACL to mode for %s", path));
-	pntsd = get_cifs_acl(&acllen, inode, path);
+	pntsd = get_cifs_acl(&acllen, inode, path, pfid);
 
 	/* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
 	if (pntsd)
@@ -694,7 +699,7 @@
 	cFYI(DBG2, ("set ACL from mode for %s", path));
 
 	/* Get the security descriptor */
-	pntsd = get_cifs_acl(&acllen, inode, path);
+	pntsd = get_cifs_acl(&acllen, inode, path, NULL);
 
 	/* Add three ACEs for owner, group, everyone getting rid of
 	   other ACEs as chmod disables ACEs and set the security descriptor */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index a0414bd..7e5e0e7 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -92,11 +92,12 @@
 extern int cifs_get_inode_info(struct inode **pinode,
 			const unsigned char *search_path,
 			FILE_ALL_INFO * pfile_info,
-			struct super_block *sb, int xid);
+			struct super_block *sb, int xid, const __u16 *pfid);
 extern int cifs_get_inode_info_unix(struct inode **pinode,
 			const unsigned char *search_path,
 			struct super_block *sb, int xid);
-extern void acl_to_uid_mode(struct inode *inode, const char *search_path);
+extern void acl_to_uid_mode(struct inode *inode, const char *path,
+			    const __u16 *pfid);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 4e83b47..0f5c62b 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -229,7 +229,8 @@
 						 inode->i_sb, xid);
 		else {
 			rc = cifs_get_inode_info(&newinode, full_path,
-						 buf, inode->i_sb, xid);
+						 buf, inode->i_sb, xid,
+						 &fileHandle);
 			if (newinode) {
 				newinode->i_mode = mode;
 				if ((oplock & CIFS_CREATE_ACTION) &&
@@ -483,7 +484,7 @@
 					      parent_dir_inode->i_sb, xid);
 	else
 		rc = cifs_get_inode_info(&newInode, full_path, NULL,
-					 parent_dir_inode->i_sb, xid);
+					 parent_dir_inode->i_sb, xid, NULL);
 
 	if ((rc == 0) && (newInode != NULL)) {
 		if (pTcon->nocase)
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index fa849c9..40b6900 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -145,7 +145,7 @@
 			full_path, inode->i_sb, xid);
 	else
 		rc = cifs_get_inode_info(&file->f_path.dentry->d_inode,
-			full_path, buf, inode->i_sb, xid);
+			full_path, buf, inode->i_sb, xid, NULL);
 
 	if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
 		pCifsInode->clientCanCacheAll = TRUE;
@@ -440,7 +440,7 @@
 				else
 					rc = cifs_get_inode_info(&inode,
 						full_path, NULL, inode->i_sb,
-						xid);
+						xid, NULL);
 			} /* else we are writing out data to server already
 			     and could deadlock if we tried to flush data, and
 			     since we do not know if we have data that would
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index e57e5c4..7e4c244 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -371,7 +371,7 @@
 
 int cifs_get_inode_info(struct inode **pinode,
 	const unsigned char *search_path, FILE_ALL_INFO *pfindData,
-	struct super_block *sb, int xid)
+	struct super_block *sb, int xid, const __u16 *pfid)
 {
 	int rc = 0;
 	struct cifsTconInfo *pTcon;
@@ -569,7 +569,7 @@
 		/* fill in 0777 bits from ACL */
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
 			cFYI(1, ("Getting mode bits from ACL"));
-			acl_to_uid_mode(inode, search_path);
+			acl_to_uid_mode(inode, search_path, pfid);
 		}
 #endif
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
@@ -616,7 +616,8 @@
 	if (cifs_sb->tcon->unix_ext)
 		rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
 	else
-		rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid);
+		rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid,
+					 NULL);
 	if (rc && cifs_sb->tcon->ipc) {
 		cFYI(1, ("ipc connection - fake read inode"));
 		inode->i_mode |= S_IFDIR;
@@ -949,7 +950,7 @@
 						      inode->i_sb, xid);
 		else
 			rc = cifs_get_inode_info(&newinode, full_path, NULL,
-						 inode->i_sb, xid);
+						 inode->i_sb, xid, NULL);
 
 		if (pTcon->nocase)
 			direntry->d_op = &cifs_ci_dentry_ops;
@@ -1231,7 +1232,7 @@
 		}
 	} else {
 		rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL,
-					 direntry->d_sb, xid);
+					 direntry->d_sb, xid, NULL);
 		if (rc) {
 			cFYI(1, ("error on getting revalidate info %d", rc));
 /*			if (rc != -ENOENT)
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 1d6fb01..d4e7ec9 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -205,7 +205,7 @@
 						      inode->i_sb, xid);
 		else
 			rc = cifs_get_inode_info(&newinode, full_path, NULL,
-						 inode->i_sb, xid);
+						 inode->i_sb, xid, NULL);
 
 		if (rc != 0) {
 			cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d",