Commit 2dd29d3133ad4c7926ea03b8431e604373c4ad65

Authored by Steve French
1 parent 5268df2ead

[CIFS] New CIFS POSIX mkdir performance improvement

Signed-off-by: Steve French <sfrench@us.ibm.com>

Showing 5 changed files with 340 additions and 15 deletions Side-by-side Diff

... ... @@ -12,7 +12,10 @@
12 12 on server which does not support the Unix Extensions). Remove read only dos
13 13 attribute on chmod when adding any write permission (ie on any of
14 14 user/group/other (not all of user/group/other ie 0222) when
15   -mounted to windows.
  15 +mounted to windows. Add support for POSIX MkDir (slight performance
  16 +enhancement and eliminates the network race between the mkdir and set
  17 +path info of the mode).
  18 +
16 19  
17 20 Version 1.47
18 21 ------------
... ... @@ -1388,7 +1388,7 @@
1388 1388 #define SMB_SET_POSIX_LOCK 0x208
1389 1389 #define SMB_POSIX_OPEN 0x209
1390 1390 #define SMB_POSIX_UNLINK 0x20a
1391   -#define SMB_SET_FILE_UNIX_INFO2
  1391 +#define SMB_SET_FILE_UNIX_INFO2 0x20b
1392 1392 #define SMB_SET_FILE_BASIC_INFO2 0x3ec
1393 1393 #define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */
1394 1394 #define SMB_FILE_ALL_INFO2 0x3fa
1395 1395  
1396 1396  
1397 1397  
... ... @@ -2109,22 +2109,40 @@
2109 2109  
2110 2110 /* end of POSIX ACL definitions */
2111 2111  
  2112 +/* POSIX Open Flags */
  2113 +#define SMB_O_RDONLY 0x1
  2114 +#define SMB_O_WRONLY 0x2
  2115 +#define SMB_O_RDWR 0x4
  2116 +#define SMB_O_CREAT 0x10
  2117 +#define SMB_O_EXCL 0x20
  2118 +#define SMB_O_TRUNC 0x40
  2119 +#define SMB_O_APPEND 0x80
  2120 +#define SMB_O_SYNC 0x100
  2121 +#define SMB_O_DIRECTORY 0x200
  2122 +#define SMB_O_NOFOLLOW 0x400
  2123 +#define SMB_O_DIRECT 0x800
  2124 +
2112 2125 typedef struct {
2113   - __u32 OpenFlags; /* same as NT CreateX */
2114   - __u32 PosixOpenFlags;
2115   - __u32 Mode;
2116   - __u16 Level; /* reply level requested (see QPathInfo levels) */
2117   - __u16 Pad; /* reserved - MBZ */
  2126 + __le32 OpenFlags; /* same as NT CreateX */
  2127 + __le32 PosixOpenFlags;
  2128 + __le64 Permissions;
  2129 + __le16 Level; /* reply level requested (see QPathInfo levels) */
2118 2130 } __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
2119 2131  
2120 2132 typedef struct {
2121   - /* reply varies based on requested level */
  2133 + __le16 OplockFlags;
  2134 + __u16 Fid;
  2135 + __le32 CreateAction;
  2136 + __le16 ReturnedLevel;
  2137 + __le16 Pad;
  2138 + /* struct following varies based on requested level */
2122 2139 } __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
2123 2140  
2124 2141  
2125 2142 struct file_internal_info {
2126 2143 __u64 UniqueId; /* inode number */
2127 2144 } __attribute__((packed)); /* level 0x3ee */
  2145 +
2128 2146 struct file_mode_info {
2129 2147 __le32 Mode;
2130 2148 } __attribute__((packed)); /* level 0x3f8 */
1 1 /*
2 2 * fs/cifs/cifsproto.h
3 3 *
4   - * Copyright (c) International Business Machines Corp., 2002,2006
  4 + * Copyright (c) International Business Machines Corp., 2002,2007
5 5 * Author(s): Steve French (sfrench@us.ibm.com)
6 6 *
7 7 * This library is free software; you can redistribute it and/or modify
... ... @@ -244,6 +244,11 @@
244 244 const int access_flags, const int omode,
245 245 __u16 * netfid, int *pOplock, FILE_ALL_INFO *,
246 246 const struct nls_table *nls_codepage, int remap);
  247 +extern int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon,
  248 + u32 posix_flags, __u64 mode, __u16 * netfid,
  249 + FILE_UNIX_BASIC_INFO *pRetData,
  250 + __u32 *pOplock, const char *name,
  251 + const struct nls_table *nls_codepage, int remap);
247 252 extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon,
248 253 const int smb_file_id);
249 254  
1 1 /*
2 2 * fs/cifs/cifssmb.c
3 3 *
4   - * Copyright (C) International Business Machines Corp., 2002,2006
  4 + * Copyright (C) International Business Machines Corp., 2002,2007
5 5 * Author(s): Steve French (sfrench@us.ibm.com)
6 6 *
7 7 * Contains the routines for constructing the SMB PDUs themselves
... ... @@ -24,8 +24,8 @@
24 24 /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */
25 25 /* These are mostly routines that operate on a pathname, or on a tree id */
26 26 /* (mounted volume), but there are eight handle based routines which must be */
27   - /* treated slightly different for reconnection purposes since we never want */
28   - /* to reuse a stale file handle and the caller knows the file handle */
  27 + /* treated slightly differently for reconnection purposes since we never */
  28 + /* want to reuse a stale file handle and only the caller knows the file info */
29 29  
30 30 #include <linux/fs.h>
31 31 #include <linux/kernel.h>
... ... @@ -911,6 +911,127 @@
911 911 if (rc == -EAGAIN)
912 912 goto MkDirRetry;
913 913 return rc;
  914 +}
  915 +
  916 +int
  917 +CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
  918 + __u64 mode, __u16 * netfid, FILE_UNIX_BASIC_INFO *pRetData,
  919 + __u32 *pOplock, const char *name,
  920 + const struct nls_table *nls_codepage, int remap)
  921 +{
  922 + TRANSACTION2_SPI_REQ *pSMB = NULL;
  923 + TRANSACTION2_SPI_RSP *pSMBr = NULL;
  924 + int name_len;
  925 + int rc = 0;
  926 + int bytes_returned = 0;
  927 + char *data_offset;
  928 + __u16 params, param_offset, offset, byte_count, count;
  929 + OPEN_PSX_REQ * pdata;
  930 + OPEN_PSX_RSP * psx_rsp;
  931 +
  932 + cFYI(1, ("In POSIX Create"));
  933 +PsxCreat:
  934 + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
  935 + (void **) &pSMBr);
  936 + if (rc)
  937 + return rc;
  938 +
  939 + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
  940 + name_len =
  941 + cifsConvertToUCS((__le16 *) pSMB->FileName, name,
  942 + PATH_MAX, nls_codepage, remap);
  943 + name_len++; /* trailing null */
  944 + name_len *= 2;
  945 + } else { /* BB improve the check for buffer overruns BB */
  946 + name_len = strnlen(name, PATH_MAX);
  947 + name_len++; /* trailing null */
  948 + strncpy(pSMB->FileName, name, name_len);
  949 + }
  950 +
  951 + params = 6 + name_len;
  952 + count = sizeof(OPEN_PSX_REQ);
  953 + pSMB->MaxParameterCount = cpu_to_le16(2);
  954 + pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
  955 + pSMB->MaxSetupCount = 0;
  956 + pSMB->Reserved = 0;
  957 + pSMB->Flags = 0;
  958 + pSMB->Timeout = 0;
  959 + pSMB->Reserved2 = 0;
  960 + param_offset = offsetof(struct smb_com_transaction2_spi_req,
  961 + InformationLevel) - 4;
  962 + offset = param_offset + params;
  963 + data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
  964 + pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
  965 + pdata->Level = SMB_QUERY_FILE_UNIX_BASIC;
  966 + pdata->Permissions = cpu_to_le64(mode);
  967 + pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
  968 + pdata->OpenFlags = cpu_to_le32(*pOplock);
  969 + pSMB->ParameterOffset = cpu_to_le16(param_offset);
  970 + pSMB->DataOffset = cpu_to_le16(offset);
  971 + pSMB->SetupCount = 1;
  972 + pSMB->Reserved3 = 0;
  973 + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
  974 + byte_count = 3 /* pad */ + params + count;
  975 +
  976 + pSMB->DataCount = cpu_to_le16(count);
  977 + pSMB->ParameterCount = cpu_to_le16(params);
  978 + pSMB->TotalDataCount = pSMB->DataCount;
  979 + pSMB->TotalParameterCount = pSMB->ParameterCount;
  980 + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
  981 + pSMB->Reserved4 = 0;
  982 + pSMB->hdr.smb_buf_length += byte_count;
  983 + pSMB->ByteCount = cpu_to_le16(byte_count);
  984 + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
  985 + (struct smb_hdr *) pSMBr, &bytes_returned, 0);
  986 + if (rc) {
  987 + cFYI(1, ("Posix create returned %d", rc));
  988 + goto psx_create_err;
  989 + }
  990 +
  991 + cFYI(1,("copying inode info"));
  992 + rc = validate_t2((struct smb_t2_rsp *)pSMBr);
  993 +
  994 + if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
  995 + rc = -EIO; /* bad smb */
  996 + goto psx_create_err;
  997 + }
  998 +
  999 + /* copy return information to pRetData */
  1000 + psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
  1001 + + le16_to_cpu(pSMBr->t2.DataOffset));
  1002 +
  1003 + *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
  1004 + if(netfid)
  1005 + *netfid = psx_rsp->Fid; /* cifs fid stays in le */
  1006 + /* Let caller know file was created so we can set the mode. */
  1007 + /* Do we care about the CreateAction in any other cases? */
  1008 + if(cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
  1009 + *pOplock |= CIFS_CREATE_ACTION;
  1010 + /* check to make sure response data is there */
  1011 + if(psx_rsp->ReturnedLevel != SMB_QUERY_FILE_UNIX_BASIC)
  1012 + pRetData->Type = -1; /* unknown */
  1013 + else {
  1014 + if(pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
  1015 + + sizeof(FILE_UNIX_BASIC_INFO)) {
  1016 + cERROR(1,("Open response data too small"));
  1017 + pRetData->Type = -1;
  1018 + goto psx_create_err;
  1019 + }
  1020 + memcpy((char *) pRetData,
  1021 + (char *)&psx_rsp + sizeof(OPEN_PSX_RSP),
  1022 + sizeof (FILE_UNIX_BASIC_INFO));
  1023 + }
  1024 +
  1025 +
  1026 +psx_create_err:
  1027 + cifs_buf_release(pSMB);
  1028 +
  1029 + cifs_stats_inc(&tcon->num_mkdirs);
  1030 +
  1031 + if (rc == -EAGAIN)
  1032 + goto PsxCreat;
  1033 +
  1034 + return rc;
914 1035 }
915 1036  
916 1037 static __u16 convert_disposition(int disposition)
1 1 /*
2 2 * fs/cifs/inode.c
3 3 *
4   - * Copyright (C) International Business Machines Corp., 2002,2005
  4 + * Copyright (C) International Business Machines Corp., 2002,2007
5 5 * Author(s): Steve French (sfrench@us.ibm.com)
6 6 *
7 7 * This library is free software; you can redistribute it and/or modify
... ... @@ -734,6 +734,133 @@
734 734 return rc;
735 735 }
736 736  
  737 +static void posix_fill_in_inode(struct inode *tmp_inode,
  738 + FILE_UNIX_BASIC_INFO *pData, int *pobject_type, int isNewInode)
  739 +{
  740 + loff_t local_size;
  741 + struct timespec local_mtime;
  742 +
  743 + struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
  744 + struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
  745 +
  746 + __u32 type = le32_to_cpu(pData->Type);
  747 + __u64 num_of_bytes = le64_to_cpu(pData->NumOfBytes);
  748 + __u64 end_of_file = le64_to_cpu(pData->EndOfFile);
  749 + cifsInfo->time = jiffies;
  750 + atomic_inc(&cifsInfo->inUse);
  751 +
  752 + /* save mtime and size */
  753 + local_mtime = tmp_inode->i_mtime;
  754 + local_size = tmp_inode->i_size;
  755 +
  756 + tmp_inode->i_atime =
  757 + cifs_NTtimeToUnix(le64_to_cpu(pData->LastAccessTime));
  758 + tmp_inode->i_mtime =
  759 + cifs_NTtimeToUnix(le64_to_cpu(pData->LastModificationTime));
  760 + tmp_inode->i_ctime =
  761 + cifs_NTtimeToUnix(le64_to_cpu(pData->LastStatusChange));
  762 +
  763 + tmp_inode->i_mode = le64_to_cpu(pData->Permissions);
  764 + /* since we set the inode type below we need to mask off type
  765 + to avoid strange results if bits above were corrupt */
  766 + tmp_inode->i_mode &= ~S_IFMT;
  767 + if (type == UNIX_FILE) {
  768 + *pobject_type = DT_REG;
  769 + tmp_inode->i_mode |= S_IFREG;
  770 + } else if (type == UNIX_SYMLINK) {
  771 + *pobject_type = DT_LNK;
  772 + tmp_inode->i_mode |= S_IFLNK;
  773 + } else if (type == UNIX_DIR) {
  774 + *pobject_type = DT_DIR;
  775 + tmp_inode->i_mode |= S_IFDIR;
  776 + } else if (type == UNIX_CHARDEV) {
  777 + *pobject_type = DT_CHR;
  778 + tmp_inode->i_mode |= S_IFCHR;
  779 + tmp_inode->i_rdev = MKDEV(le64_to_cpu(pData->DevMajor),
  780 + le64_to_cpu(pData->DevMinor) & MINORMASK);
  781 + } else if (type == UNIX_BLOCKDEV) {
  782 + *pobject_type = DT_BLK;
  783 + tmp_inode->i_mode |= S_IFBLK;
  784 + tmp_inode->i_rdev = MKDEV(le64_to_cpu(pData->DevMajor),
  785 + le64_to_cpu(pData->DevMinor) & MINORMASK);
  786 + } else if (type == UNIX_FIFO) {
  787 + *pobject_type = DT_FIFO;
  788 + tmp_inode->i_mode |= S_IFIFO;
  789 + } else if (type == UNIX_SOCKET) {
  790 + *pobject_type = DT_SOCK;
  791 + tmp_inode->i_mode |= S_IFSOCK;
  792 + } else {
  793 + /* safest to just call it a file */
  794 + *pobject_type = DT_REG;
  795 + tmp_inode->i_mode |= S_IFREG;
  796 + cFYI(1,("unknown inode type %d",type));
  797 + }
  798 +
  799 + tmp_inode->i_uid = le64_to_cpu(pData->Uid);
  800 + tmp_inode->i_gid = le64_to_cpu(pData->Gid);
  801 + tmp_inode->i_nlink = le64_to_cpu(pData->Nlinks);
  802 +
  803 + spin_lock(&tmp_inode->i_lock);
  804 + if (is_size_safe_to_change(cifsInfo, end_of_file)) {
  805 + /* can not safely change the file size here if the
  806 + client is writing to it due to potential races */
  807 + i_size_write(tmp_inode, end_of_file);
  808 +
  809 + /* 512 bytes (2**9) is the fake blocksize that must be used */
  810 + /* for this calculation, not the real blocksize */
  811 + tmp_inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
  812 + }
  813 + spin_unlock(&tmp_inode->i_lock);
  814 +
  815 + if (S_ISREG(tmp_inode->i_mode)) {
  816 + cFYI(1, ("File inode"));
  817 + tmp_inode->i_op = &cifs_file_inode_ops;
  818 +
  819 + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
  820 + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
  821 + tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
  822 + else
  823 + tmp_inode->i_fop = &cifs_file_direct_ops;
  824 +
  825 + } else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
  826 + tmp_inode->i_fop = &cifs_file_nobrl_ops;
  827 + else
  828 + tmp_inode->i_fop = &cifs_file_ops;
  829 +
  830 + if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
  831 + (cifs_sb->tcon->ses->server->maxBuf <
  832 + PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
  833 + tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
  834 + else
  835 + tmp_inode->i_data.a_ops = &cifs_addr_ops;
  836 +
  837 + if(isNewInode)
  838 + return; /* No sense invalidating pages for new inode since we
  839 + have not started caching readahead file data yet */
  840 +
  841 + if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
  842 + (local_size == tmp_inode->i_size)) {
  843 + cFYI(1, ("inode exists but unchanged"));
  844 + } else {
  845 + /* file may have changed on server */
  846 + cFYI(1, ("invalidate inode, readdir detected change"));
  847 + invalidate_remote_inode(tmp_inode);
  848 + }
  849 + } else if (S_ISDIR(tmp_inode->i_mode)) {
  850 + cFYI(1, ("Directory inode"));
  851 + tmp_inode->i_op = &cifs_dir_inode_ops;
  852 + tmp_inode->i_fop = &cifs_dir_ops;
  853 + } else if (S_ISLNK(tmp_inode->i_mode)) {
  854 + cFYI(1, ("Symbolic Link inode"));
  855 + tmp_inode->i_op = &cifs_symlink_inode_ops;
  856 +/* tmp_inode->i_fop = *//* do not need to set to anything */
  857 + } else {
  858 + cFYI(1, ("Special inode"));
  859 + init_special_inode(tmp_inode, tmp_inode->i_mode,
  860 + tmp_inode->i_rdev);
  861 + }
  862 +}
  863 +
737 864 int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
738 865 {
739 866 int rc = 0;
... ... @@ -755,6 +882,53 @@
755 882 FreeXid(xid);
756 883 return -ENOMEM;
757 884 }
  885 +
  886 + if((pTcon->ses->capabilities & CAP_UNIX) &&
  887 + (CIFS_UNIX_POSIX_PATH_OPS_CAP &
  888 + le64_to_cpu(pTcon->fsUnixInfo.Capability))) {
  889 + u32 oplock = 0;
  890 + FILE_UNIX_BASIC_INFO * pInfo =
  891 + kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
  892 + if(pInfo == NULL) {
  893 + rc = -ENOMEM;
  894 + goto mkdir_out;
  895 + }
  896 +
  897 + rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT,
  898 + mode, NULL /* netfid */, pInfo, &oplock,
  899 + full_path, cifs_sb->local_nls,
  900 + cifs_sb->mnt_cifs_flags &
  901 + CIFS_MOUNT_MAP_SPECIAL_CHR);
  902 + if (rc) {
  903 + cFYI(1, ("posix mkdir returned 0x%x", rc));
  904 + d_drop(direntry);
  905 + } else {
  906 + if (pInfo->Type == -1) /* no return info - go query */
  907 + goto mkdir_get_info;
  908 +/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need to set uid/gid */
  909 + inc_nlink(inode);
  910 + if (pTcon->nocase)
  911 + direntry->d_op = &cifs_ci_dentry_ops;
  912 + else
  913 + direntry->d_op = &cifs_dentry_ops;
  914 + d_instantiate(direntry, newinode);
  915 + if (direntry->d_inode) {
  916 + int obj_type;
  917 + direntry->d_inode->i_nlink = 2;
  918 + /* already checked in POSIXCreate whether
  919 + frame was long enough */
  920 + posix_fill_in_inode(direntry->d_inode,
  921 + pInfo, &obj_type, 1 /* NewInode */);
  922 + /* could double check that we actually
  923 + * created what we thought we did ie
  924 + * a directory
  925 + */
  926 + }
  927 + }
  928 + kfree(pInfo);
  929 + goto mkdir_out;
  930 + }
  931 +
758 932 /* BB add setting the equivalent of mode via CreateX w/ACLs */
759 933 rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls,
760 934 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
... ... @@ -762,6 +936,7 @@
762 936 cFYI(1, ("cifs_mkdir returned 0x%x", rc));
763 937 d_drop(direntry);
764 938 } else {
  939 +mkdir_get_info:
765 940 inc_nlink(inode);
766 941 if (pTcon->ses->capabilities & CAP_UNIX)
767 942 rc = cifs_get_inode_info_unix(&newinode, full_path,
... ... @@ -775,8 +950,10 @@
775 950 else
776 951 direntry->d_op = &cifs_dentry_ops;
777 952 d_instantiate(direntry, newinode);
778   - if (direntry->d_inode)
779   - direntry->d_inode->i_nlink = 2;
  953 + /* setting nlink not necessary except in cases where we
  954 + * failed to get it from the server or was set bogus */
  955 + if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
  956 + direntry->d_inode->i_nlink = 2;
780 957 if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
781 958 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
782 959 CIFSSMBUnixSetPerms(xid, pTcon, full_path,
... ... @@ -812,6 +989,7 @@
812 989 }
813 990 }
814 991 }
  992 +mkdir_out:
815 993 kfree(full_path);
816 994 FreeXid(xid);
817 995 return rc;