[CIFS] improve posix semantics of file create

Samba server added support for a new posix open/create/mkdir operation
a year or so ago, and we added support to cifs for mkdir to use it,
but had not added the corresponding code to file create.

The following patch helps improve the performance of the cifs create
path (to Samba and servers which support the cifs posix protocol
extensions). Using Connectathon basic test1, with 2000 files, the
performance improved about 15%, and also helped reduce network traffic
(17% fewer SMBs sent over the wire) due to saving a network round trip
for the SetPathInfo on every file create.

It should also help the semantics (and probably the performance) of
write (e.g. when posix byte range locks are on the file) on file
handles opened with posix create, and adds support for a few flags
which would have to be ignored otherwise.

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

+205 -100
+3 -1
fs/cifs/CHANGES
··· 8 Samba servers (worked to Windows). Fix rmdir so that pending search 9 (readdir) requests do not get invalid results which include the now 10 removed directory. Fix oops in cifs_dfs_ref.c when prefixpath is not reachable 11 - when using DFS. 12 13 Version 1.55 14 ------------
··· 8 Samba servers (worked to Windows). Fix rmdir so that pending search 9 (readdir) requests do not get invalid results which include the now 10 removed directory. Fix oops in cifs_dfs_ref.c when prefixpath is not reachable 11 + when using DFS. Add better file create support to servers which support 12 + the CIFS POSIX protocol extensions (this adds support for new flags 13 + on create, and improves semantics for write of locked ranges). 14 15 Version 1.55 16 ------------
+202 -99
fs/cifs/dir.c
··· 3 * 4 * vfs operations that deal with dentries 5 * 6 - * Copyright (C) International Business Machines Corp., 2002,2008 7 * Author(s): Steve French (sfrench@us.ibm.com) 8 * 9 * This library is free software; you can redistribute it and/or modify ··· 129 return full_path; 130 } 131 132 static void setup_cifs_dentry(struct cifsTconInfo *tcon, 133 struct dentry *direntry, 134 struct inode *newinode) ··· 222 int xid; 223 int create_options = CREATE_NOT_DIR; 224 int oplock = 0; 225 - /* BB below access is too much for the mknod to request */ 226 int desiredAccess = GENERIC_READ | GENERIC_WRITE; 227 __u16 fileHandle; 228 struct cifs_sb_info *cifs_sb; ··· 253 } 254 255 mode &= ~current->fs->umask; 256 257 if (nd && (nd->flags & LOOKUP_OPEN)) { 258 - int oflags = nd->intent.open.flags; 259 - 260 desiredAccess = 0; 261 if (oflags & FMODE_READ) 262 - desiredAccess |= GENERIC_READ; 263 if (oflags & FMODE_WRITE) { 264 desiredAccess |= GENERIC_WRITE; 265 if (!(oflags & FMODE_READ)) ··· 308 309 /* BB add processing to set equivalent of mode - e.g. via CreateX with 310 ACLs */ 311 - if (oplockEnabled) 312 - oplock = REQ_OPLOCK; 313 314 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 315 if (buf == NULL) { ··· 340 } 341 if (rc) { 342 cFYI(1, ("cifs_create returned 0x%x", rc)); 343 - } else { 344 - /* If Open reported that we actually created a file 345 - then we now have to set the mode if possible */ 346 - if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) { 347 - struct cifs_unix_set_info_args args = { 348 .mode = mode, 349 .ctime = NO_CHANGE_64, 350 .atime = NO_CHANGE_64, 351 .mtime = NO_CHANGE_64, 352 .device = 0, 353 - }; 354 355 - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { 356 - args.uid = (__u64) current_fsuid(); 357 - if (inode->i_mode & S_ISGID) 358 - args.gid = (__u64) inode->i_gid; 359 - else 360 - args.gid = (__u64) current_fsgid(); 361 - } else { 362 - args.uid = NO_CHANGE_64; 363 - args.gid = NO_CHANGE_64; 364 - } 365 - CIFSSMBUnixSetInfo(xid, tcon, full_path, &args, 366 - cifs_sb->local_nls, 367 - cifs_sb->mnt_cifs_flags & 368 - CIFS_MOUNT_MAP_SPECIAL_CHR); 369 } else { 370 - /* BB implement mode setting via Windows security 371 - descriptors e.g. */ 372 - /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ 373 - 374 - /* Could set r/o dos attribute if mode & 0222 == 0 */ 375 } 376 377 - /* server might mask mode so we have to query for it */ 378 - if (tcon->unix_ext) 379 - rc = cifs_get_inode_info_unix(&newinode, full_path, 380 - inode->i_sb, xid); 381 - else { 382 - rc = cifs_get_inode_info(&newinode, full_path, 383 - buf, inode->i_sb, xid, 384 - &fileHandle); 385 - if (newinode) { 386 - if (cifs_sb->mnt_cifs_flags & 387 - CIFS_MOUNT_DYNPERM) 388 - newinode->i_mode = mode; 389 - if ((oplock & CIFS_CREATE_ACTION) && 390 - (cifs_sb->mnt_cifs_flags & 391 - CIFS_MOUNT_SET_UID)) { 392 - newinode->i_uid = current_fsuid(); 393 - if (inode->i_mode & S_ISGID) 394 - newinode->i_gid = 395 - inode->i_gid; 396 - else 397 - newinode->i_gid = 398 - current_fsgid(); 399 - } 400 } 401 } 402 403 - if (rc != 0) { 404 - cFYI(1, ("Create worked, get_inode_info failed rc = %d", 405 - rc)); 406 - } else 407 - setup_cifs_dentry(tcon, direntry, newinode); 408 409 - if ((nd == NULL /* nfsd case - nfs srv does not set nd */) || 410 - (!(nd->flags & LOOKUP_OPEN))) { 411 - /* mknod case - do not leave file open */ 412 - CIFSSMBClose(xid, tcon, fileHandle); 413 - } else if (newinode) { 414 - struct cifsFileInfo *pCifsFile = 415 - kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); 416 417 - if (pCifsFile == NULL) 418 - goto cifs_create_out; 419 - pCifsFile->netfid = fileHandle; 420 - pCifsFile->pid = current->tgid; 421 - pCifsFile->pInode = newinode; 422 - pCifsFile->invalidHandle = false; 423 - pCifsFile->closePend = false; 424 - init_MUTEX(&pCifsFile->fh_sem); 425 - mutex_init(&pCifsFile->lock_mutex); 426 - INIT_LIST_HEAD(&pCifsFile->llist); 427 - atomic_set(&pCifsFile->wrtPending, 0); 428 429 - /* set the following in open now 430 pCifsFile->pfile = file; */ 431 - write_lock(&GlobalSMBSeslock); 432 - list_add(&pCifsFile->tlist, &tcon->openFileList); 433 - pCifsInode = CIFS_I(newinode); 434 - if (pCifsInode) { 435 - /* if readable file instance put first in list*/ 436 - if (write_only) { 437 - list_add_tail(&pCifsFile->flist, 438 - &pCifsInode->openFileList); 439 - } else { 440 - list_add(&pCifsFile->flist, 441 - &pCifsInode->openFileList); 442 - } 443 - if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { 444 - pCifsInode->clientCanCacheAll = true; 445 - pCifsInode->clientCanCacheRead = true; 446 - cFYI(1, ("Exclusive Oplock inode %p", 447 - newinode)); 448 - } else if ((oplock & 0xF) == OPLOCK_READ) 449 - pCifsInode->clientCanCacheRead = true; 450 } 451 - write_unlock(&GlobalSMBSeslock); 452 } 453 } 454 cifs_create_out: 455 kfree(buf);
··· 3 * 4 * vfs operations that deal with dentries 5 * 6 + * Copyright (C) International Business Machines Corp., 2002,2009 7 * Author(s): Steve French (sfrench@us.ibm.com) 8 * 9 * This library is free software; you can redistribute it and/or modify ··· 129 return full_path; 130 } 131 132 + static int cifs_posix_open(char *full_path, struct inode **pinode, 133 + struct super_block *sb, int mode, int oflags, 134 + int *poplock, __u16 *pnetfid, int xid) 135 + { 136 + int rc; 137 + __u32 oplock; 138 + FILE_UNIX_BASIC_INFO *presp_data; 139 + __u32 posix_flags = 0; 140 + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 141 + 142 + cFYI(1, ("posix open %s", full_path)); 143 + 144 + presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); 145 + if (presp_data == NULL) 146 + return -ENOMEM; 147 + 148 + /* So far cifs posix extensions can only map the following flags. 149 + There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but 150 + so far we do not seem to need them, and we can treat them as local only */ 151 + if ((oflags & (FMODE_READ | FMODE_WRITE)) == 152 + (FMODE_READ | FMODE_WRITE)) 153 + posix_flags = SMB_O_RDWR; 154 + else if (oflags & FMODE_READ) 155 + posix_flags = SMB_O_RDONLY; 156 + else if (oflags & FMODE_WRITE) 157 + posix_flags = SMB_O_WRONLY; 158 + if (oflags & O_CREAT) 159 + posix_flags |= SMB_O_CREAT; 160 + if (oflags & O_EXCL) 161 + posix_flags |= SMB_O_EXCL; 162 + if (oflags & O_TRUNC) 163 + posix_flags |= SMB_O_TRUNC; 164 + if (oflags & O_APPEND) 165 + posix_flags |= SMB_O_APPEND; 166 + if (oflags & O_SYNC) 167 + posix_flags |= SMB_O_SYNC; 168 + if (oflags & O_DIRECTORY) 169 + posix_flags |= SMB_O_DIRECTORY; 170 + if (oflags & O_NOFOLLOW) 171 + posix_flags |= SMB_O_NOFOLLOW; 172 + if (oflags & O_DIRECT) 173 + posix_flags |= SMB_O_DIRECT; 174 + 175 + 176 + rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode, 177 + pnetfid, presp_data, &oplock, full_path, 178 + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 179 + CIFS_MOUNT_MAP_SPECIAL_CHR); 180 + if (rc) 181 + goto posix_open_ret; 182 + 183 + if (presp_data->Type == cpu_to_le32(-1)) 184 + goto posix_open_ret; /* open ok, caller does qpathinfo */ 185 + 186 + /* get new inode and set it up */ 187 + if (!pinode) 188 + goto posix_open_ret; /* caller does not need info */ 189 + 190 + *pinode = cifs_new_inode(sb, &presp_data->UniqueId); 191 + 192 + /* We do not need to close the file if new_inode fails since 193 + the caller will retry qpathinfo as long as inode is null */ 194 + if (*pinode == NULL) 195 + goto posix_open_ret; 196 + 197 + posix_fill_in_inode(*pinode, presp_data, 1); 198 + 199 + posix_open_ret: 200 + kfree(presp_data); 201 + return rc; 202 + } 203 + 204 static void setup_cifs_dentry(struct cifsTconInfo *tcon, 205 struct dentry *direntry, 206 struct inode *newinode) ··· 150 int xid; 151 int create_options = CREATE_NOT_DIR; 152 int oplock = 0; 153 + int oflags; 154 + /* 155 + * BB below access is probably too much for mknod to request 156 + * but we have to do query and setpathinfo so requesting 157 + * less could fail (unless we want to request getatr and setatr 158 + * permissions (only). At least for POSIX we do not have to 159 + * request so much. 160 + */ 161 int desiredAccess = GENERIC_READ | GENERIC_WRITE; 162 __u16 fileHandle; 163 struct cifs_sb_info *cifs_sb; ··· 174 } 175 176 mode &= ~current->fs->umask; 177 + if (oplockEnabled) 178 + oplock = REQ_OPLOCK; 179 + 180 + if (nd && (nd->flags & LOOKUP_OPEN)) 181 + oflags = nd->intent.open.flags; 182 + else 183 + oflags = FMODE_READ; 184 + 185 + if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && 186 + (CIFS_UNIX_POSIX_PATH_OPS_CAP & 187 + le64_to_cpu(tcon->fsUnixInfo.Capability))) { 188 + rc = cifs_posix_open(full_path, &newinode, inode->i_sb, 189 + mode, oflags, &oplock, &fileHandle, xid); 190 + /* EIO could indicate that (posix open) operation is not 191 + supported, despite what server claimed in capability 192 + negotation. EREMOTE indicates DFS junction, which is not 193 + handled in posix open */ 194 + 195 + if ((rc == 0) && (newinode == NULL)) 196 + goto cifs_create_get_file_info; /* query inode info */ 197 + else if (rc == 0) /* success, no need to query */ 198 + goto cifs_create_set_dentry; 199 + else if ((rc != -EIO) && (rc != -EREMOTE) && 200 + (rc != -EOPNOTSUPP)) /* path not found or net err */ 201 + goto cifs_create_out; 202 + /* else fallthrough to retry, using older open call, this is 203 + case where server does not support this SMB level, and 204 + falsely claims capability (also get here for DFS case 205 + which should be rare for path not covered on files) */ 206 + } 207 208 if (nd && (nd->flags & LOOKUP_OPEN)) { 209 + /* if the file is going to stay open, then we 210 + need to set the desired access properly */ 211 desiredAccess = 0; 212 if (oflags & FMODE_READ) 213 + desiredAccess |= GENERIC_READ; /* is this too little? */ 214 if (oflags & FMODE_WRITE) { 215 desiredAccess |= GENERIC_WRITE; 216 if (!(oflags & FMODE_READ)) ··· 199 200 /* BB add processing to set equivalent of mode - e.g. via CreateX with 201 ACLs */ 202 203 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 204 if (buf == NULL) { ··· 233 } 234 if (rc) { 235 cFYI(1, ("cifs_create returned 0x%x", rc)); 236 + goto cifs_create_out; 237 + } 238 + 239 + /* If Open reported that we actually created a file 240 + then we now have to set the mode if possible */ 241 + if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) { 242 + struct cifs_unix_set_info_args args = { 243 .mode = mode, 244 .ctime = NO_CHANGE_64, 245 .atime = NO_CHANGE_64, 246 .mtime = NO_CHANGE_64, 247 .device = 0, 248 + }; 249 250 + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { 251 + args.uid = (__u64) current_fsuid(); 252 + if (inode->i_mode & S_ISGID) 253 + args.gid = (__u64) inode->i_gid; 254 + else 255 + args.gid = (__u64) current_fsgid(); 256 } else { 257 + args.uid = NO_CHANGE_64; 258 + args.gid = NO_CHANGE_64; 259 } 260 + CIFSSMBUnixSetInfo(xid, tcon, full_path, &args, 261 + cifs_sb->local_nls, 262 + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 263 + } else { 264 + /* BB implement mode setting via Windows security 265 + descriptors e.g. */ 266 + /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ 267 268 + /* Could set r/o dos attribute if mode & 0222 == 0 */ 269 + } 270 + 271 + cifs_create_get_file_info: 272 + /* server might mask mode so we have to query for it */ 273 + if (tcon->unix_ext) 274 + rc = cifs_get_inode_info_unix(&newinode, full_path, 275 + inode->i_sb, xid); 276 + else { 277 + rc = cifs_get_inode_info(&newinode, full_path, buf, 278 + inode->i_sb, xid, &fileHandle); 279 + if (newinode) { 280 + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) 281 + newinode->i_mode = mode; 282 + if ((oplock & CIFS_CREATE_ACTION) && 283 + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { 284 + newinode->i_uid = current_fsuid(); 285 + if (inode->i_mode & S_ISGID) 286 + newinode->i_gid = inode->i_gid; 287 + else 288 + newinode->i_gid = current_fsgid(); 289 } 290 } 291 + } 292 293 + cifs_create_set_dentry: 294 + if (rc == 0) 295 + setup_cifs_dentry(tcon, direntry, newinode); 296 + else 297 + cFYI(1, ("Create worked, get_inode_info failed rc = %d", rc)); 298 299 + /* nfsd case - nfs srv does not set nd */ 300 + if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) { 301 + /* mknod case - do not leave file open */ 302 + CIFSSMBClose(xid, tcon, fileHandle); 303 + } else if (newinode) { 304 + struct cifsFileInfo *pCifsFile = 305 + kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); 306 307 + if (pCifsFile == NULL) 308 + goto cifs_create_out; 309 + pCifsFile->netfid = fileHandle; 310 + pCifsFile->pid = current->tgid; 311 + pCifsFile->pInode = newinode; 312 + pCifsFile->invalidHandle = false; 313 + pCifsFile->closePend = false; 314 + init_MUTEX(&pCifsFile->fh_sem); 315 + mutex_init(&pCifsFile->lock_mutex); 316 + INIT_LIST_HEAD(&pCifsFile->llist); 317 + atomic_set(&pCifsFile->wrtPending, 0); 318 319 + /* set the following in open now 320 pCifsFile->pfile = file; */ 321 + write_lock(&GlobalSMBSeslock); 322 + list_add(&pCifsFile->tlist, &tcon->openFileList); 323 + pCifsInode = CIFS_I(newinode); 324 + if (pCifsInode) { 325 + /* if readable file instance put first in list*/ 326 + if (write_only) { 327 + list_add_tail(&pCifsFile->flist, 328 + &pCifsInode->openFileList); 329 + } else { 330 + list_add(&pCifsFile->flist, 331 + &pCifsInode->openFileList); 332 } 333 + if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { 334 + pCifsInode->clientCanCacheAll = true; 335 + pCifsInode->clientCanCacheRead = true; 336 + cFYI(1, ("Exclusive Oplock inode %p", 337 + newinode)); 338 + } else if ((oplock & 0xF) == OPLOCK_READ) 339 + pCifsInode->clientCanCacheRead = true; 340 } 341 + write_unlock(&GlobalSMBSeslock); 342 } 343 cifs_create_out: 344 kfree(buf);