[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 8 Samba servers (worked to Windows). Fix rmdir so that pending search 9 9 (readdir) requests do not get invalid results which include the now 10 10 removed directory. Fix oops in cifs_dfs_ref.c when prefixpath is not reachable 11 - when using DFS. 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). 12 14 13 15 Version 1.55 14 16 ------------
+202 -99
fs/cifs/dir.c
··· 3 3 * 4 4 * vfs operations that deal with dentries 5 5 * 6 - * Copyright (C) International Business Machines Corp., 2002,2008 6 + * Copyright (C) International Business Machines Corp., 2002,2009 7 7 * Author(s): Steve French (sfrench@us.ibm.com) 8 8 * 9 9 * This library is free software; you can redistribute it and/or modify ··· 129 129 return full_path; 130 130 } 131 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 + 132 204 static void setup_cifs_dentry(struct cifsTconInfo *tcon, 133 205 struct dentry *direntry, 134 206 struct inode *newinode) ··· 222 150 int xid; 223 151 int create_options = CREATE_NOT_DIR; 224 152 int oplock = 0; 225 - /* BB below access is too much for the mknod to request */ 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 + */ 226 161 int desiredAccess = GENERIC_READ | GENERIC_WRITE; 227 162 __u16 fileHandle; 228 163 struct cifs_sb_info *cifs_sb; ··· 253 174 } 254 175 255 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 + } 256 207 257 208 if (nd && (nd->flags & LOOKUP_OPEN)) { 258 - int oflags = nd->intent.open.flags; 259 - 209 + /* if the file is going to stay open, then we 210 + need to set the desired access properly */ 260 211 desiredAccess = 0; 261 212 if (oflags & FMODE_READ) 262 - desiredAccess |= GENERIC_READ; 213 + desiredAccess |= GENERIC_READ; /* is this too little? */ 263 214 if (oflags & FMODE_WRITE) { 264 215 desiredAccess |= GENERIC_WRITE; 265 216 if (!(oflags & FMODE_READ)) ··· 308 199 309 200 /* BB add processing to set equivalent of mode - e.g. via CreateX with 310 201 ACLs */ 311 - if (oplockEnabled) 312 - oplock = REQ_OPLOCK; 313 202 314 203 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 315 204 if (buf == NULL) { ··· 340 233 } 341 234 if (rc) { 342 235 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 = { 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 = { 348 243 .mode = mode, 349 244 .ctime = NO_CHANGE_64, 350 245 .atime = NO_CHANGE_64, 351 246 .mtime = NO_CHANGE_64, 352 247 .device = 0, 353 - }; 248 + }; 354 249 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); 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(); 369 256 } 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 */ 257 + args.uid = NO_CHANGE_64; 258 + args.gid = NO_CHANGE_64; 375 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);*/ 376 267 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 - } 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(); 400 289 } 401 290 } 291 + } 402 292 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); 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)); 408 298 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); 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); 416 306 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); 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); 428 318 429 - /* set the following in open now 319 + /* set the following in open now 430 320 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; 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); 450 332 } 451 - write_unlock(&GlobalSMBSeslock); 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; 452 340 } 341 + write_unlock(&GlobalSMBSeslock); 453 342 } 454 343 cifs_create_out: 455 344 kfree(buf);