aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@etersoft.ru>2012-09-19 09:22:45 -0400
committerSteve French <smfrench@gmail.com>2012-09-24 22:46:33 -0400
commit233839b1df65a24c8b67b748fe7b18d86d0ad6d7 (patch)
treeba9af2849063c2213fc0fbecb494967f1e662806 /fs
parent0822f51426b51bd599b3a7e972b14aacaa045a92 (diff)
CIFS: Fix fast lease break after open problem
Now we walk though cifsFileInfo's list for every incoming lease break and look for an equivalent there. That approach misses lease breaks that come just after an open response - we don't have time to populate new cifsFileInfo structure to the list. Fix this by adding new list of pending opens and look for a lease there if we didn't find it in the list of cifsFileInfo structures. Signed-off-by: Pavel Shilovsky <pshilovsky@etersoft.ru> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsglob.h12
-rw-r--r--fs/cifs/cifsproto.h7
-rw-r--r--fs/cifs/connect.c1
-rw-r--r--fs/cifs/dir.c9
-rw-r--r--fs/cifs/file.c35
-rw-r--r--fs/cifs/misc.c30
-rw-r--r--fs/cifs/smb2misc.c74
7 files changed, 158 insertions, 10 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index b6ec142028e8..a39e5b7fc844 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -715,6 +715,7 @@ struct cifs_ses {
715 __u16 session_flags; 715 __u16 session_flags;
716#endif /* CONFIG_CIFS_SMB2 */ 716#endif /* CONFIG_CIFS_SMB2 */
717}; 717};
718
718/* no more than one of the following three session flags may be set */ 719/* no more than one of the following three session flags may be set */
719#define CIFS_SES_NT4 1 720#define CIFS_SES_NT4 1
720#define CIFS_SES_OS2 2 721#define CIFS_SES_OS2 2
@@ -821,6 +822,7 @@ struct cifs_tcon {
821 u64 resource_id; /* server resource id */ 822 u64 resource_id; /* server resource id */
822 struct fscache_cookie *fscache; /* cookie for share */ 823 struct fscache_cookie *fscache; /* cookie for share */
823#endif 824#endif
825 struct list_head pending_opens; /* list of incomplete opens */
824 /* BB add field for back pointer to sb struct(s)? */ 826 /* BB add field for back pointer to sb struct(s)? */
825}; 827};
826 828
@@ -863,6 +865,15 @@ cifs_get_tlink(struct tcon_link *tlink)
863/* This function is always expected to succeed */ 865/* This function is always expected to succeed */
864extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); 866extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
865 867
868#define CIFS_OPLOCK_NO_CHANGE 0xfe
869
870struct cifs_pending_open {
871 struct list_head olist;
872 struct tcon_link *tlink;
873 __u8 lease_key[16];
874 __u32 oplock;
875};
876
866/* 877/*
867 * This info hangs off the cifsFileInfo structure, pointed to by llist. 878 * This info hangs off the cifsFileInfo structure, pointed to by llist.
868 * This is used to track byte stream locks on the file 879 * This is used to track byte stream locks on the file
@@ -903,6 +914,7 @@ struct cifs_fid {
903 __u64 volatile_fid; /* volatile file id for smb2 */ 914 __u64 volatile_fid; /* volatile file id for smb2 */
904 __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ 915 __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
905#endif 916#endif
917 struct cifs_pending_open *pending_open;
906}; 918};
907 919
908struct cifs_fid_locks { 920struct cifs_fid_locks {
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index c758ee7b0307..09ea6321c55a 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -184,6 +184,13 @@ extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
184 __u64 length, __u8 type, 184 __u64 length, __u8 type,
185 struct cifsLockInfo **conf_lock, 185 struct cifsLockInfo **conf_lock,
186 bool rw_check); 186 bool rw_check);
187extern void cifs_add_pending_open(struct cifs_fid *fid,
188 struct tcon_link *tlink,
189 struct cifs_pending_open *open);
190extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
191 struct tcon_link *tlink,
192 struct cifs_pending_open *open);
193extern void cifs_del_pending_open(struct cifs_pending_open *open);
187 194
188#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) 195#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
189extern void cifs_dfs_release_automount_timer(void); 196extern void cifs_dfs_release_automount_timer(void);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 443e39633107..59c595e8a1b0 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -2645,6 +2645,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
2645 tcon->retry = volume_info->retry; 2645 tcon->retry = volume_info->retry;
2646 tcon->nocase = volume_info->nocase; 2646 tcon->nocase = volume_info->nocase;
2647 tcon->local_lease = volume_info->local_lease; 2647 tcon->local_lease = volume_info->local_lease;
2648 INIT_LIST_HEAD(&tcon->pending_opens);
2648 2649
2649 spin_lock(&cifs_tcp_ses_lock); 2650 spin_lock(&cifs_tcp_ses_lock);
2650 list_add(&tcon->tcon_list, &ses->tcon_list); 2651 list_add(&tcon->tcon_list, &ses->tcon_list);
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 4f2147c5adb6..7c0a81283645 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -382,6 +382,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
382 struct cifs_tcon *tcon; 382 struct cifs_tcon *tcon;
383 struct TCP_Server_Info *server; 383 struct TCP_Server_Info *server;
384 struct cifs_fid fid; 384 struct cifs_fid fid;
385 struct cifs_pending_open open;
385 __u32 oplock; 386 __u32 oplock;
386 struct cifsFileInfo *file_info; 387 struct cifsFileInfo *file_info;
387 388
@@ -423,16 +424,21 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
423 if (server->ops->new_lease_key) 424 if (server->ops->new_lease_key)
424 server->ops->new_lease_key(&fid); 425 server->ops->new_lease_key(&fid);
425 426
427 cifs_add_pending_open(&fid, tlink, &open);
428
426 rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, 429 rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
427 &oplock, &fid, opened); 430 &oplock, &fid, opened);
428 431
429 if (rc) 432 if (rc) {
433 cifs_del_pending_open(&open);
430 goto out; 434 goto out;
435 }
431 436
432 rc = finish_open(file, direntry, generic_file_open, opened); 437 rc = finish_open(file, direntry, generic_file_open, opened);
433 if (rc) { 438 if (rc) {
434 if (server->ops->close) 439 if (server->ops->close)
435 server->ops->close(xid, tcon, &fid); 440 server->ops->close(xid, tcon, &fid);
441 cifs_del_pending_open(&open);
436 goto out; 442 goto out;
437 } 443 }
438 444
@@ -440,6 +446,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
440 if (file_info == NULL) { 446 if (file_info == NULL) {
441 if (server->ops->close) 447 if (server->ops->close)
442 server->ops->close(xid, tcon, &fid); 448 server->ops->close(xid, tcon, &fid);
449 cifs_del_pending_open(&open);
443 rc = -ENOMEM; 450 rc = -ENOMEM;
444 } 451 }
445 452
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index e93e3d2c69e6..88e9c74e2cac 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -247,6 +247,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
247 struct cifsInodeInfo *cinode = CIFS_I(inode); 247 struct cifsInodeInfo *cinode = CIFS_I(inode);
248 struct cifsFileInfo *cfile; 248 struct cifsFileInfo *cfile;
249 struct cifs_fid_locks *fdlocks; 249 struct cifs_fid_locks *fdlocks;
250 struct cifs_tcon *tcon = tlink_tcon(tlink);
250 251
251 cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); 252 cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
252 if (cfile == NULL) 253 if (cfile == NULL)
@@ -274,10 +275,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
274 cfile->tlink = cifs_get_tlink(tlink); 275 cfile->tlink = cifs_get_tlink(tlink);
275 INIT_WORK(&cfile->oplock_break, cifs_oplock_break); 276 INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
276 mutex_init(&cfile->fh_mutex); 277 mutex_init(&cfile->fh_mutex);
277 tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);
278 278
279 spin_lock(&cifs_file_list_lock); 279 spin_lock(&cifs_file_list_lock);
280 list_add(&cfile->tlist, &(tlink_tcon(tlink)->openFileList)); 280 if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE)
281 oplock = fid->pending_open->oplock;
282 list_del(&fid->pending_open->olist);
283
284 tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);
285
286 list_add(&cfile->tlist, &tcon->openFileList);
281 /* if readable file instance put first in list*/ 287 /* if readable file instance put first in list*/
282 if (file->f_mode & FMODE_READ) 288 if (file->f_mode & FMODE_READ)
283 list_add(&cfile->flist, &cinode->openFileList); 289 list_add(&cfile->flist, &cinode->openFileList);
@@ -307,9 +313,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
307{ 313{
308 struct inode *inode = cifs_file->dentry->d_inode; 314 struct inode *inode = cifs_file->dentry->d_inode;
309 struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); 315 struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
316 struct TCP_Server_Info *server = tcon->ses->server;
310 struct cifsInodeInfo *cifsi = CIFS_I(inode); 317 struct cifsInodeInfo *cifsi = CIFS_I(inode);
311 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 318 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
312 struct cifsLockInfo *li, *tmp; 319 struct cifsLockInfo *li, *tmp;
320 struct cifs_fid fid;
321 struct cifs_pending_open open;
313 322
314 spin_lock(&cifs_file_list_lock); 323 spin_lock(&cifs_file_list_lock);
315 if (--cifs_file->count > 0) { 324 if (--cifs_file->count > 0) {
@@ -317,6 +326,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
317 return; 326 return;
318 } 327 }
319 328
329 if (server->ops->get_lease_key)
330 server->ops->get_lease_key(inode, &fid);
331
332 /* store open in pending opens to make sure we don't miss lease break */
333 cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
334
320 /* remove it from the lists */ 335 /* remove it from the lists */
321 list_del(&cifs_file->flist); 336 list_del(&cifs_file->flist);
322 list_del(&cifs_file->tlist); 337 list_del(&cifs_file->tlist);
@@ -348,6 +363,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
348 free_xid(xid); 363 free_xid(xid);
349 } 364 }
350 365
366 cifs_del_pending_open(&open);
367
351 /* 368 /*
352 * Delete any outstanding lock records. We'll lose them when the file 369 * Delete any outstanding lock records. We'll lose them when the file
353 * is closed anyway. 370 * is closed anyway.
@@ -368,6 +385,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
368} 385}
369 386
370int cifs_open(struct inode *inode, struct file *file) 387int cifs_open(struct inode *inode, struct file *file)
388
371{ 389{
372 int rc = -EACCES; 390 int rc = -EACCES;
373 unsigned int xid; 391 unsigned int xid;
@@ -380,6 +398,7 @@ int cifs_open(struct inode *inode, struct file *file)
380 char *full_path = NULL; 398 char *full_path = NULL;
381 bool posix_open_ok = false; 399 bool posix_open_ok = false;
382 struct cifs_fid fid; 400 struct cifs_fid fid;
401 struct cifs_pending_open open;
383 402
384 xid = get_xid(); 403 xid = get_xid();
385 404
@@ -401,7 +420,7 @@ int cifs_open(struct inode *inode, struct file *file)
401 cFYI(1, "inode = 0x%p file flags are 0x%x for %s", 420 cFYI(1, "inode = 0x%p file flags are 0x%x for %s",
402 inode, file->f_flags, full_path); 421 inode, file->f_flags, full_path);
403 422
404 if (tcon->ses->server->oplocks) 423 if (server->oplocks)
405 oplock = REQ_OPLOCK; 424 oplock = REQ_OPLOCK;
406 else 425 else
407 oplock = 0; 426 oplock = 0;
@@ -434,20 +453,28 @@ int cifs_open(struct inode *inode, struct file *file)
434 */ 453 */
435 } 454 }
436 455
456 if (server->ops->get_lease_key)
457 server->ops->get_lease_key(inode, &fid);
458
459 cifs_add_pending_open(&fid, tlink, &open);
460
437 if (!posix_open_ok) { 461 if (!posix_open_ok) {
438 if (server->ops->get_lease_key) 462 if (server->ops->get_lease_key)
439 server->ops->get_lease_key(inode, &fid); 463 server->ops->get_lease_key(inode, &fid);
440 464
441 rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, 465 rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
442 file->f_flags, &oplock, &fid, xid); 466 file->f_flags, &oplock, &fid, xid);
443 if (rc) 467 if (rc) {
468 cifs_del_pending_open(&open);
444 goto out; 469 goto out;
470 }
445 } 471 }
446 472
447 cfile = cifs_new_fileinfo(&fid, file, tlink, oplock); 473 cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
448 if (cfile == NULL) { 474 if (cfile == NULL) {
449 if (server->ops->close) 475 if (server->ops->close)
450 server->ops->close(xid, tcon, &fid); 476 server->ops->close(xid, tcon, &fid);
477 cifs_del_pending_open(&open);
451 rc = -ENOMEM; 478 rc = -ENOMEM;
452 goto out; 479 goto out;
453 } 480 }
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index a921b0712eff..3a00c0d0cead 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -579,3 +579,33 @@ backup_cred(struct cifs_sb_info *cifs_sb)
579 579
580 return false; 580 return false;
581} 581}
582
583void
584cifs_del_pending_open(struct cifs_pending_open *open)
585{
586 spin_lock(&cifs_file_list_lock);
587 list_del(&open->olist);
588 spin_unlock(&cifs_file_list_lock);
589}
590
591void
592cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
593 struct cifs_pending_open *open)
594{
595#ifdef CONFIG_CIFS_SMB2
596 memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
597#endif
598 open->oplock = CIFS_OPLOCK_NO_CHANGE;
599 open->tlink = tlink;
600 fid->pending_open = open;
601 list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
602}
603
604void
605cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
606 struct cifs_pending_open *open)
607{
608 spin_lock(&cifs_file_list_lock);
609 cifs_add_pending_open_locked(fid, tlink, open);
610 spin_unlock(&cifs_file_list_lock);
611}
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 3a7f8bd5127d..cd31715f03f4 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -389,6 +389,27 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state)
389 return 0; 389 return 0;
390} 390}
391 391
392struct smb2_lease_break_work {
393 struct work_struct lease_break;
394 struct tcon_link *tlink;
395 __u8 lease_key[16];
396 __le32 lease_state;
397};
398
399static void
400cifs_ses_oplock_break(struct work_struct *work)
401{
402 struct smb2_lease_break_work *lw = container_of(work,
403 struct smb2_lease_break_work, lease_break);
404 int rc;
405
406 rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key,
407 lw->lease_state);
408 cFYI(1, "Lease release rc %d", rc);
409 cifs_put_tlink(lw->tlink);
410 kfree(lw);
411}
412
392static bool 413static bool
393smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) 414smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
394{ 415{
@@ -398,6 +419,19 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
398 struct cifs_tcon *tcon; 419 struct cifs_tcon *tcon;
399 struct cifsInodeInfo *cinode; 420 struct cifsInodeInfo *cinode;
400 struct cifsFileInfo *cfile; 421 struct cifsFileInfo *cfile;
422 struct cifs_pending_open *open;
423 struct smb2_lease_break_work *lw;
424 bool found;
425 int ack_req = rsp->Flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
426
427 lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
428 if (!lw) {
429 cERROR(1, "Memory allocation failed during lease break check");
430 return false;
431 }
432
433 INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
434 lw->lease_state = rsp->NewLeaseState;
401 435
402 cFYI(1, "Checking for lease break"); 436 cFYI(1, "Checking for lease break");
403 437
@@ -405,28 +439,29 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
405 spin_lock(&cifs_tcp_ses_lock); 439 spin_lock(&cifs_tcp_ses_lock);
406 list_for_each(tmp, &server->smb_ses_list) { 440 list_for_each(tmp, &server->smb_ses_list) {
407 ses = list_entry(tmp, struct cifs_ses, smb_ses_list); 441 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
442
443 spin_lock(&cifs_file_list_lock);
408 list_for_each(tmp1, &ses->tcon_list) { 444 list_for_each(tmp1, &ses->tcon_list) {
409 tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); 445 tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
410 446
411 cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); 447 cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
412 spin_lock(&cifs_file_list_lock);
413 list_for_each(tmp2, &tcon->openFileList) { 448 list_for_each(tmp2, &tcon->openFileList) {
414 cfile = list_entry(tmp2, struct cifsFileInfo, 449 cfile = list_entry(tmp2, struct cifsFileInfo,
415 tlist); 450 tlist);
416 cinode = CIFS_I(cfile->dentry->d_inode); 451 cinode = CIFS_I(cfile->dentry->d_inode);
417 452
418 if (memcmp(cinode->lease_key, rsp->LeaseKey, 453 if (memcmp(cinode->lease_key, rsp->LeaseKey,
419 SMB2_LEASE_KEY_SIZE)) 454 SMB2_LEASE_KEY_SIZE))
420 continue; 455 continue;
421 456
457 cFYI(1, "found in the open list");
422 cFYI(1, "lease key match, lease break 0x%d", 458 cFYI(1, "lease key match, lease break 0x%d",
423 le32_to_cpu(rsp->NewLeaseState)); 459 le32_to_cpu(rsp->NewLeaseState));
424 460
425 smb2_set_oplock_level(cinode, 461 smb2_set_oplock_level(cinode,
426 smb2_map_lease_to_oplock(rsp->NewLeaseState)); 462 smb2_map_lease_to_oplock(rsp->NewLeaseState));
427 463
428 if (rsp->Flags & 464 if (ack_req)
429 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED)
430 cfile->oplock_break_cancelled = false; 465 cfile->oplock_break_cancelled = false;
431 else 466 else
432 cfile->oplock_break_cancelled = true; 467 cfile->oplock_break_cancelled = true;
@@ -437,10 +472,39 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
437 spin_unlock(&cifs_tcp_ses_lock); 472 spin_unlock(&cifs_tcp_ses_lock);
438 return true; 473 return true;
439 } 474 }
440 spin_unlock(&cifs_file_list_lock); 475
476 found = false;
477 list_for_each_entry(open, &tcon->pending_opens, olist) {
478 if (memcmp(open->lease_key, rsp->LeaseKey,
479 SMB2_LEASE_KEY_SIZE))
480 continue;
481
482 if (!found && ack_req) {
483 found = true;
484 memcpy(lw->lease_key, open->lease_key,
485 SMB2_LEASE_KEY_SIZE);
486 lw->tlink = cifs_get_tlink(open->tlink);
487 queue_work(cifsiod_wq,
488 &lw->lease_break);
489 }
490
491 cFYI(1, "found in the pending open list");
492 cFYI(1, "lease key match, lease break 0x%d",
493 le32_to_cpu(rsp->NewLeaseState));
494
495 open->oplock =
496 smb2_map_lease_to_oplock(rsp->NewLeaseState);
497 }
498 if (found) {
499 spin_unlock(&cifs_file_list_lock);
500 spin_unlock(&cifs_tcp_ses_lock);
501 return true;
502 }
441 } 503 }
504 spin_unlock(&cifs_file_list_lock);
442 } 505 }
443 spin_unlock(&cifs_tcp_ses_lock); 506 spin_unlock(&cifs_tcp_ses_lock);
507 kfree(lw);
444 cFYI(1, "Can not process lease break - no lease matched"); 508 cFYI(1, "Can not process lease break - no lease matched");
445 return false; 509 return false;
446} 510}