[CIFS] New CIFS POSIX mkdir performance improvement

Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index eeea337..9b7e0db 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/inode.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2005
+ *   Copyright (C) International Business Machines  Corp., 2002,2007
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -734,6 +734,133 @@
 	return rc;
 }
 
+static void posix_fill_in_inode(struct inode *tmp_inode,
+	FILE_UNIX_BASIC_INFO *pData, int *pobject_type, int isNewInode)
+{
+	loff_t local_size;
+	struct timespec local_mtime;
+
+	struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
+	struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
+
+	__u32 type = le32_to_cpu(pData->Type);
+	__u64 num_of_bytes = le64_to_cpu(pData->NumOfBytes);
+	__u64 end_of_file = le64_to_cpu(pData->EndOfFile);
+	cifsInfo->time = jiffies;
+	atomic_inc(&cifsInfo->inUse);
+
+	/* save mtime and size */
+	local_mtime = tmp_inode->i_mtime;
+	local_size  = tmp_inode->i_size;
+
+	tmp_inode->i_atime =
+	    cifs_NTtimeToUnix(le64_to_cpu(pData->LastAccessTime));
+	tmp_inode->i_mtime =
+	    cifs_NTtimeToUnix(le64_to_cpu(pData->LastModificationTime));
+	tmp_inode->i_ctime =
+	    cifs_NTtimeToUnix(le64_to_cpu(pData->LastStatusChange));
+
+	tmp_inode->i_mode = le64_to_cpu(pData->Permissions);
+	/* since we set the inode type below we need to mask off type
+           to avoid strange results if bits above were corrupt */
+        tmp_inode->i_mode &= ~S_IFMT;
+	if (type == UNIX_FILE) {
+		*pobject_type = DT_REG;
+		tmp_inode->i_mode |= S_IFREG;
+	} else if (type == UNIX_SYMLINK) {
+		*pobject_type = DT_LNK;
+		tmp_inode->i_mode |= S_IFLNK;
+	} else if (type == UNIX_DIR) {
+		*pobject_type = DT_DIR;
+		tmp_inode->i_mode |= S_IFDIR;
+	} else if (type == UNIX_CHARDEV) {
+		*pobject_type = DT_CHR;
+		tmp_inode->i_mode |= S_IFCHR;
+		tmp_inode->i_rdev = MKDEV(le64_to_cpu(pData->DevMajor),
+				le64_to_cpu(pData->DevMinor) & MINORMASK);
+	} else if (type == UNIX_BLOCKDEV) {
+		*pobject_type = DT_BLK;
+		tmp_inode->i_mode |= S_IFBLK;
+		tmp_inode->i_rdev = MKDEV(le64_to_cpu(pData->DevMajor),
+				le64_to_cpu(pData->DevMinor) & MINORMASK);
+	} else if (type == UNIX_FIFO) {
+		*pobject_type = DT_FIFO;
+		tmp_inode->i_mode |= S_IFIFO;
+	} else if (type == UNIX_SOCKET) {
+		*pobject_type = DT_SOCK;
+		tmp_inode->i_mode |= S_IFSOCK;
+	} else {
+		/* safest to just call it a file */
+		*pobject_type = DT_REG;
+		tmp_inode->i_mode |= S_IFREG;
+		cFYI(1,("unknown inode type %d",type)); 
+	}
+
+	tmp_inode->i_uid = le64_to_cpu(pData->Uid);
+	tmp_inode->i_gid = le64_to_cpu(pData->Gid);
+	tmp_inode->i_nlink = le64_to_cpu(pData->Nlinks);
+
+	spin_lock(&tmp_inode->i_lock);
+	if (is_size_safe_to_change(cifsInfo, end_of_file)) {
+		/* can not safely change the file size here if the 
+		client is writing to it due to potential races */
+		i_size_write(tmp_inode, end_of_file);
+
+	/* 512 bytes (2**9) is the fake blocksize that must be used */
+	/* for this calculation, not the real blocksize */
+		tmp_inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
+	}
+	spin_unlock(&tmp_inode->i_lock);
+
+	if (S_ISREG(tmp_inode->i_mode)) {
+		cFYI(1, ("File inode"));
+		tmp_inode->i_op = &cifs_file_inode_ops;
+
+		if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+			if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+				tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
+			else
+				tmp_inode->i_fop = &cifs_file_direct_ops;
+		
+		} else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+			tmp_inode->i_fop = &cifs_file_nobrl_ops;
+		else
+			tmp_inode->i_fop = &cifs_file_ops;
+
+		if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
+		   (cifs_sb->tcon->ses->server->maxBuf < 
+			PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
+			tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+		else
+			tmp_inode->i_data.a_ops = &cifs_addr_ops;
+
+		if(isNewInode)
+			return; /* No sense invalidating pages for new inode since we
+					   have not started caching readahead file data yet */
+
+		if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
+			(local_size == tmp_inode->i_size)) {
+			cFYI(1, ("inode exists but unchanged"));
+		} else {
+			/* file may have changed on server */
+			cFYI(1, ("invalidate inode, readdir detected change"));
+			invalidate_remote_inode(tmp_inode);
+		}
+	} else if (S_ISDIR(tmp_inode->i_mode)) {
+		cFYI(1, ("Directory inode"));
+		tmp_inode->i_op = &cifs_dir_inode_ops;
+		tmp_inode->i_fop = &cifs_dir_ops;
+	} else if (S_ISLNK(tmp_inode->i_mode)) {
+		cFYI(1, ("Symbolic Link inode"));
+		tmp_inode->i_op = &cifs_symlink_inode_ops;
+/* tmp_inode->i_fop = *//* do not need to set to anything */
+	} else {
+		cFYI(1, ("Special inode")); 
+		init_special_inode(tmp_inode, tmp_inode->i_mode,
+				   tmp_inode->i_rdev);
+	}	
+}
+
 int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
 {
 	int rc = 0;
@@ -755,6 +882,53 @@
 		FreeXid(xid);
 		return -ENOMEM;
 	}
+	
+	if((pTcon->ses->capabilities & CAP_UNIX) && 
+		(CIFS_UNIX_POSIX_PATH_OPS_CAP &	
+			le64_to_cpu(pTcon->fsUnixInfo.Capability))) {
+		u32 oplock = 0;
+		FILE_UNIX_BASIC_INFO * pInfo = 
+			kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
+		if(pInfo == NULL) {
+			rc = -ENOMEM;
+			goto mkdir_out;
+		}
+			
+		rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT,
+				mode, NULL /* netfid */, pInfo, &oplock,
+				full_path, cifs_sb->local_nls, 
+				cifs_sb->mnt_cifs_flags & 
+					CIFS_MOUNT_MAP_SPECIAL_CHR);
+		if (rc) {
+			cFYI(1, ("posix mkdir returned 0x%x", rc));
+			d_drop(direntry);
+		} else {
+			if (pInfo->Type == -1) /* no return info - go query */
+				goto mkdir_get_info; 
+/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need to set uid/gid */
+			inc_nlink(inode);
+			if (pTcon->nocase)
+				direntry->d_op = &cifs_ci_dentry_ops;
+			else
+				direntry->d_op = &cifs_dentry_ops;
+			d_instantiate(direntry, newinode);
+			if (direntry->d_inode) {
+				int obj_type;
+				direntry->d_inode->i_nlink = 2;
+				/* already checked in POSIXCreate whether
+				frame was long enough */
+				posix_fill_in_inode(direntry->d_inode,
+					pInfo, &obj_type, 1 /* NewInode */);
+				/* could double check that we actually
+				 * created what we thought we did ie
+				 * a directory
+				 */	
+			}
+		}
+		kfree(pInfo);
+		goto mkdir_out;
+	}	
+	
 	/* BB add setting the equivalent of mode via CreateX w/ACLs */
 	rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls,
 			  cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -762,6 +936,7 @@
 		cFYI(1, ("cifs_mkdir returned 0x%x", rc));
 		d_drop(direntry);
 	} else {
+mkdir_get_info:		
 		inc_nlink(inode);
 		if (pTcon->ses->capabilities & CAP_UNIX)
 			rc = cifs_get_inode_info_unix(&newinode, full_path,
@@ -775,8 +950,10 @@
 		else
 			direntry->d_op = &cifs_dentry_ops;
 		d_instantiate(direntry, newinode);
-		if (direntry->d_inode)
-			direntry->d_inode->i_nlink = 2;
+		 /* setting nlink not necessary except in cases where we
+		  * failed to get it from the server or was set bogus */ 
+		if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
+				direntry->d_inode->i_nlink = 2; 
 		if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
 			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 				CIFSSMBUnixSetPerms(xid, pTcon, full_path,
@@ -812,6 +989,7 @@
 			}
 		}
 	}
+mkdir_out:	
 	kfree(full_path);
 	FreeXid(xid);
 	return rc;