diff options
-rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 30 | ||||
-rw-r--r-- | fs/cifs/misc.c | 25 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 6 |
4 files changed, 53 insertions, 10 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 5b18d4585740..585ad3207cb1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -1333,6 +1333,7 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) | |||
1333 | } | 1333 | } |
1334 | 1334 | ||
1335 | struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); | 1335 | struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); |
1336 | void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr); | ||
1336 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file); | 1337 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file); |
1337 | 1338 | ||
1338 | #define CIFS_CACHE_READ_FLG 1 | 1339 | #define CIFS_CACHE_READ_FLG 1 |
@@ -1855,6 +1856,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock; | |||
1855 | #endif /* CONFIG_CIFS_ACL */ | 1856 | #endif /* CONFIG_CIFS_ACL */ |
1856 | 1857 | ||
1857 | void cifs_oplock_break(struct work_struct *work); | 1858 | void cifs_oplock_break(struct work_struct *work); |
1859 | void cifs_queue_oplock_break(struct cifsFileInfo *cfile); | ||
1858 | 1860 | ||
1859 | extern const struct slow_work_ops cifs_oplock_break_ops; | 1861 | extern const struct slow_work_ops cifs_oplock_break_ops; |
1860 | extern struct workqueue_struct *cifsiod_wq; | 1862 | extern struct workqueue_struct *cifsiod_wq; |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 89006e044973..9c0ccc06d172 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -360,13 +360,31 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file) | |||
360 | return cifs_file; | 360 | return cifs_file; |
361 | } | 361 | } |
362 | 362 | ||
363 | /* | 363 | /** |
364 | * Release a reference on the file private data. This may involve closing | 364 | * cifsFileInfo_put - release a reference of file priv data |
365 | * the filehandle out on the server. Must be called without holding | 365 | * |
366 | * tcon->open_file_lock and cifs_file->file_info_lock. | 366 | * Always potentially wait for oplock handler. See _cifsFileInfo_put(). |
367 | */ | 367 | */ |
368 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | 368 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file) |
369 | { | 369 | { |
370 | _cifsFileInfo_put(cifs_file, true); | ||
371 | } | ||
372 | |||
373 | /** | ||
374 | * _cifsFileInfo_put - release a reference of file priv data | ||
375 | * | ||
376 | * This may involve closing the filehandle @cifs_file out on the | ||
377 | * server. Must be called without holding tcon->open_file_lock and | ||
378 | * cifs_file->file_info_lock. | ||
379 | * | ||
380 | * If @wait_for_oplock_handler is true and we are releasing the last | ||
381 | * reference, wait for any running oplock break handler of the file | ||
382 | * and cancel any pending one. If calling this function from the | ||
383 | * oplock break handler, you need to pass false. | ||
384 | * | ||
385 | */ | ||
386 | void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) | ||
387 | { | ||
370 | struct inode *inode = d_inode(cifs_file->dentry); | 388 | struct inode *inode = d_inode(cifs_file->dentry); |
371 | struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); | 389 | struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); |
372 | struct TCP_Server_Info *server = tcon->ses->server; | 390 | struct TCP_Server_Info *server = tcon->ses->server; |
@@ -414,7 +432,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | |||
414 | 432 | ||
415 | spin_unlock(&tcon->open_file_lock); | 433 | spin_unlock(&tcon->open_file_lock); |
416 | 434 | ||
417 | oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); | 435 | oplock_break_cancelled = wait_oplock_handler ? |
436 | cancel_work_sync(&cifs_file->oplock_break) : false; | ||
418 | 437 | ||
419 | if (!tcon->need_reconnect && !cifs_file->invalidHandle) { | 438 | if (!tcon->need_reconnect && !cifs_file->invalidHandle) { |
420 | struct TCP_Server_Info *server = tcon->ses->server; | 439 | struct TCP_Server_Info *server = tcon->ses->server; |
@@ -4603,6 +4622,7 @@ void cifs_oplock_break(struct work_struct *work) | |||
4603 | cinode); | 4622 | cinode); |
4604 | cifs_dbg(FYI, "Oplock release rc = %d\n", rc); | 4623 | cifs_dbg(FYI, "Oplock release rc = %d\n", rc); |
4605 | } | 4624 | } |
4625 | _cifsFileInfo_put(cfile, false /* do not wait for ourself */); | ||
4606 | cifs_done_oplock_break(cinode); | 4626 | cifs_done_oplock_break(cinode); |
4607 | } | 4627 | } |
4608 | 4628 | ||
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index bee203055b30..1e1626a2cfc3 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -501,8 +501,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
501 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, | 501 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, |
502 | &pCifsInode->flags); | 502 | &pCifsInode->flags); |
503 | 503 | ||
504 | queue_work(cifsoplockd_wq, | 504 | cifs_queue_oplock_break(netfile); |
505 | &netfile->oplock_break); | ||
506 | netfile->oplock_break_cancelled = false; | 505 | netfile->oplock_break_cancelled = false; |
507 | 506 | ||
508 | spin_unlock(&tcon->open_file_lock); | 507 | spin_unlock(&tcon->open_file_lock); |
@@ -607,6 +606,28 @@ void cifs_put_writer(struct cifsInodeInfo *cinode) | |||
607 | spin_unlock(&cinode->writers_lock); | 606 | spin_unlock(&cinode->writers_lock); |
608 | } | 607 | } |
609 | 608 | ||
609 | /** | ||
610 | * cifs_queue_oplock_break - queue the oplock break handler for cfile | ||
611 | * | ||
612 | * This function is called from the demultiplex thread when it | ||
613 | * receives an oplock break for @cfile. | ||
614 | * | ||
615 | * Assumes the tcon->open_file_lock is held. | ||
616 | * Assumes cfile->file_info_lock is NOT held. | ||
617 | */ | ||
618 | void cifs_queue_oplock_break(struct cifsFileInfo *cfile) | ||
619 | { | ||
620 | /* | ||
621 | * Bump the handle refcount now while we hold the | ||
622 | * open_file_lock to enforce the validity of it for the oplock | ||
623 | * break handler. The matching put is done at the end of the | ||
624 | * handler. | ||
625 | */ | ||
626 | cifsFileInfo_get(cfile); | ||
627 | |||
628 | queue_work(cifsoplockd_wq, &cfile->oplock_break); | ||
629 | } | ||
630 | |||
610 | void cifs_done_oplock_break(struct cifsInodeInfo *cinode) | 631 | void cifs_done_oplock_break(struct cifsInodeInfo *cinode) |
611 | { | 632 | { |
612 | clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); | 633 | clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); |
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 0e3570e40ff8..e311f58dc1c8 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
@@ -555,7 +555,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, | |||
555 | clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, | 555 | clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, |
556 | &cinode->flags); | 556 | &cinode->flags); |
557 | 557 | ||
558 | queue_work(cifsoplockd_wq, &cfile->oplock_break); | 558 | cifs_queue_oplock_break(cfile); |
559 | kfree(lw); | 559 | kfree(lw); |
560 | return true; | 560 | return true; |
561 | } | 561 | } |
@@ -712,8 +712,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
712 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, | 712 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, |
713 | &cinode->flags); | 713 | &cinode->flags); |
714 | spin_unlock(&cfile->file_info_lock); | 714 | spin_unlock(&cfile->file_info_lock); |
715 | queue_work(cifsoplockd_wq, | 715 | |
716 | &cfile->oplock_break); | 716 | cifs_queue_oplock_break(cfile); |
717 | 717 | ||
718 | spin_unlock(&tcon->open_file_lock); | 718 | spin_unlock(&tcon->open_file_lock); |
719 | spin_unlock(&cifs_tcp_ses_lock); | 719 | spin_unlock(&cifs_tcp_ses_lock); |