aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/file.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2009-09-21 06:47:50 -0400
committerSteve French <sfrench@us.ibm.com>2009-09-24 14:33:18 -0400
commit3bc303c254335dbd7c7012cc1760b12f1d5514d3 (patch)
tree7da17fbfd697216d9ed0ccd64ea9c03aaf3d52c1 /fs/cifs/file.c
parent48541bd3dd4739b4d574b44ea47660c88d833677 (diff)
cifs: convert oplock breaks to use slow_work facility (try #4)
This is the fourth respin of the patch to convert oplock breaks to use the slow_work facility. A customer of ours was testing a backport of one of the earlier patchsets, and hit a "Busy inodes after umount..." problem. An oplock break job had raced with a umount, and the superblock got torn down and its memory reused. When the oplock break job tried to dereference the inode->i_sb, the kernel oopsed. This patchset has the oplock break job hold an inode and vfsmount reference until the oplock break completes. With this, there should be no need to take a tcon reference (the vfsmount implicitly holds one already). Currently, when an oplock break comes in there's a chance that the oplock break job won't occur if the allocation of the oplock_q_entry fails. There are also some rather nasty races in the allocation and handling these structs. Rather than allocating oplock queue entries when an oplock break comes in, add a few extra fields to the cifsFileInfo struct. Get rid of the dedicated cifs_oplock_thread as well and queue the oplock break job to the slow_work thread pool. This approach also has the advantage that the oplock break jobs can potentially run in parallel rather than be serialized like they are today. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r--fs/cifs/file.c74
1 files changed, 72 insertions, 2 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index b976cea24102..90f61786f516 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -30,6 +30,7 @@
30#include <linux/writeback.h> 30#include <linux/writeback.h>
31#include <linux/task_io_accounting_ops.h> 31#include <linux/task_io_accounting_ops.h>
32#include <linux/delay.h> 32#include <linux/delay.h>
33#include <linux/mount.h>
33#include <asm/div64.h> 34#include <asm/div64.h>
34#include "cifsfs.h" 35#include "cifsfs.h"
35#include "cifspdu.h" 36#include "cifspdu.h"
@@ -51,11 +52,13 @@ static inline struct cifsFileInfo *cifs_init_private(
51 INIT_LIST_HEAD(&private_data->llist); 52 INIT_LIST_HEAD(&private_data->llist);
52 private_data->pfile = file; /* needed for writepage */ 53 private_data->pfile = file; /* needed for writepage */
53 private_data->pInode = igrab(inode); 54 private_data->pInode = igrab(inode);
55 private_data->mnt = file->f_path.mnt;
54 private_data->invalidHandle = false; 56 private_data->invalidHandle = false;
55 private_data->closePend = false; 57 private_data->closePend = false;
56 /* Initialize reference count to one. The private data is 58 /* Initialize reference count to one. The private data is
57 freed on the release of the last reference */ 59 freed on the release of the last reference */
58 atomic_set(&private_data->count, 1); 60 atomic_set(&private_data->count, 1);
61 slow_work_init(&private_data->oplock_break, &cifs_oplock_break_ops);
59 62
60 return private_data; 63 return private_data;
61} 64}
@@ -327,7 +330,7 @@ int cifs_open(struct inode *inode, struct file *file)
327 le64_to_cpu(tcon->fsUnixInfo.Capability))) { 330 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
328 int oflags = (int) cifs_posix_convert_flags(file->f_flags); 331 int oflags = (int) cifs_posix_convert_flags(file->f_flags);
329 /* can not refresh inode info since size could be stale */ 332 /* can not refresh inode info since size could be stale */
330 rc = cifs_posix_open(full_path, &inode, inode->i_sb, 333 rc = cifs_posix_open(full_path, &inode, file->f_path.mnt,
331 cifs_sb->mnt_file_mode /* ignored */, 334 cifs_sb->mnt_file_mode /* ignored */,
332 oflags, &oplock, &netfid, xid); 335 oflags, &oplock, &netfid, xid);
333 if (rc == 0) { 336 if (rc == 0) {
@@ -547,7 +550,7 @@ reopen_error_exit:
547 le64_to_cpu(tcon->fsUnixInfo.Capability))) { 550 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
548 int oflags = (int) cifs_posix_convert_flags(file->f_flags); 551 int oflags = (int) cifs_posix_convert_flags(file->f_flags);
549 /* can not refresh inode info since size could be stale */ 552 /* can not refresh inode info since size could be stale */
550 rc = cifs_posix_open(full_path, NULL, inode->i_sb, 553 rc = cifs_posix_open(full_path, NULL, file->f_path.mnt,
551 cifs_sb->mnt_file_mode /* ignored */, 554 cifs_sb->mnt_file_mode /* ignored */,
552 oflags, &oplock, &netfid, xid); 555 oflags, &oplock, &netfid, xid);
553 if (rc == 0) { 556 if (rc == 0) {
@@ -2312,6 +2315,73 @@ out:
2312 return rc; 2315 return rc;
2313} 2316}
2314 2317
2318static void
2319cifs_oplock_break(struct slow_work *work)
2320{
2321 struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
2322 oplock_break);
2323 struct inode *inode = cfile->pInode;
2324 struct cifsInodeInfo *cinode = CIFS_I(inode);
2325 struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->mnt->mnt_sb);
2326 int rc, waitrc = 0;
2327
2328 if (inode && S_ISREG(inode->i_mode)) {
2329#ifdef CONFIG_CIFS_EXPERIMENTAL
2330 if (cinode->clientCanCacheAll == 0)
2331 break_lease(inode, FMODE_READ);
2332 else if (cinode->clientCanCacheRead == 0)
2333 break_lease(inode, FMODE_WRITE);
2334#endif
2335 rc = filemap_fdatawrite(inode->i_mapping);
2336 if (cinode->clientCanCacheRead == 0) {
2337 waitrc = filemap_fdatawait(inode->i_mapping);
2338 invalidate_remote_inode(inode);
2339 }
2340 if (!rc)
2341 rc = waitrc;
2342 if (rc)
2343 cinode->write_behind_rc = rc;
2344 cFYI(1, ("Oplock flush inode %p rc %d", inode, rc));
2345 }
2346
2347 /*
2348 * releasing stale oplock after recent reconnect of smb session using
2349 * a now incorrect file handle is not a data integrity issue but do
2350 * not bother sending an oplock release if session to server still is
2351 * disconnected since oplock already released by the server
2352 */
2353 if (!cfile->closePend && !cfile->oplock_break_cancelled) {
2354 rc = CIFSSMBLock(0, cifs_sb->tcon, cfile->netfid, 0, 0, 0, 0,
2355 LOCKING_ANDX_OPLOCK_RELEASE, false);
2356 cFYI(1, ("Oplock release rc = %d", rc));
2357 }
2358}
2359
2360static int
2361cifs_oplock_break_get(struct slow_work *work)
2362{
2363 struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
2364 oplock_break);
2365 mntget(cfile->mnt);
2366 cifsFileInfo_get(cfile);
2367 return 0;
2368}
2369
2370static void
2371cifs_oplock_break_put(struct slow_work *work)
2372{
2373 struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
2374 oplock_break);
2375 mntput(cfile->mnt);
2376 cifsFileInfo_put(cfile);
2377}
2378
2379const struct slow_work_ops cifs_oplock_break_ops = {
2380 .get_ref = cifs_oplock_break_get,
2381 .put_ref = cifs_oplock_break_put,
2382 .execute = cifs_oplock_break,
2383};
2384
2315const struct address_space_operations cifs_addr_ops = { 2385const struct address_space_operations cifs_addr_ops = {
2316 .readpage = cifs_readpage, 2386 .readpage = cifs_readpage,
2317 .readpages = cifs_readpages, 2387 .readpages = cifs_readpages,