Commit 0e8422433e621f9df297bbe07e12b851679d3dbe

Authored by Sachin Prabhu
Committed by Greg Kroah-Hartman
1 parent 714a56fcac

Complete oplock break jobs before closing file handle

commit ca7df8e0bb2a5ec79691de8a1a4c0e611fe04e60 upstream.

Commit
c11f1df5003d534fd067f0168bfad7befffb3b5c
requires writers to wait for any pending oplock break handler to
complete before proceeding to write. This is done by waiting on bit
CIFS_INODE_PENDING_OPLOCK_BREAK in cifsFileInfo->flags. This bit is
cleared by the oplock break handler job queued on the workqueue once it
has completed handling the oplock break allowing writers to proceed with
writing to the file.

While testing, it was noticed that the filehandle could be closed while
there is a pending oplock break which results in the oplock break
handler on the cifsiod workqueue being cancelled before it has had a
chance to execute and clear the CIFS_INODE_PENDING_OPLOCK_BREAK bit.
Any subsequent attempt to write to this file hangs waiting for the
CIFS_INODE_PENDING_OPLOCK_BREAK bit to be cleared.

We fix this by ensuring that we also clear the bit
CIFS_INODE_PENDING_OPLOCK_BREAK when we remove the oplock break handler
from the workqueue.

The bug was found by Red Hat QA while testing using ltp's fsstress
command.

Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
Acked-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Jeff Layton <jlayton@samba.org>
Signed-off-by: Steve French <steve.french@primarydata.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 1 changed file with 5 additions and 1 deletions Side-by-side Diff

... ... @@ -366,6 +366,7 @@
366 366 struct cifsLockInfo *li, *tmp;
367 367 struct cifs_fid fid;
368 368 struct cifs_pending_open open;
  369 + bool oplock_break_cancelled;
369 370  
370 371 spin_lock(&cifs_file_list_lock);
371 372 if (--cifs_file->count > 0) {
... ... @@ -397,7 +398,7 @@
397 398 }
398 399 spin_unlock(&cifs_file_list_lock);
399 400  
400   - cancel_work_sync(&cifs_file->oplock_break);
  401 + oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
401 402  
402 403 if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
403 404 struct TCP_Server_Info *server = tcon->ses->server;
... ... @@ -408,6 +409,9 @@
408 409 server->ops->close(xid, tcon, &cifs_file->fid);
409 410 _free_xid(xid);
410 411 }
  412 +
  413 + if (oplock_break_cancelled)
  414 + cifs_done_oplock_break(cifsi);
411 415  
412 416 cifs_del_pending_open(&open);
413 417