diff options
author | Jeff Layton <jlayton@redhat.com> | 2009-09-21 06:47:50 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2009-09-24 14:33:18 -0400 |
commit | 3bc303c254335dbd7c7012cc1760b12f1d5514d3 (patch) | |
tree | 7da17fbfd697216d9ed0ccd64ea9c03aaf3d52c1 /fs | |
parent | 48541bd3dd4739b4d574b44ea47660c88d833677 (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')
-rw-r--r-- | fs/cifs/Kconfig | 1 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 95 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 12 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 6 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 1 | ||||
-rw-r--r-- | fs/cifs/connect.c | 1 | ||||
-rw-r--r-- | fs/cifs/dir.c | 25 | ||||
-rw-r--r-- | fs/cifs/file.c | 74 | ||||
-rw-r--r-- | fs/cifs/misc.c | 29 | ||||
-rw-r--r-- | fs/cifs/transport.c | 50 |
10 files changed, 119 insertions, 175 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 6994a0f54f02..80f352596807 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig | |||
@@ -2,6 +2,7 @@ config CIFS | |||
2 | tristate "CIFS support (advanced network filesystem, SMBFS successor)" | 2 | tristate "CIFS support (advanced network filesystem, SMBFS successor)" |
3 | depends on INET | 3 | depends on INET |
4 | select NLS | 4 | select NLS |
5 | select SLOW_WORK | ||
5 | help | 6 | help |
6 | This is the client VFS module for the Common Internet File System | 7 | This is the client VFS module for the Common Internet File System |
7 | (CIFS) protocol which is the successor to the Server Message Block | 8 | (CIFS) protocol which is the successor to the Server Message Block |
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 3610e9958b4c..89142b39fec1 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -64,9 +64,6 @@ unsigned int multiuser_mount = 0; | |||
64 | unsigned int extended_security = CIFSSEC_DEF; | 64 | unsigned int extended_security = CIFSSEC_DEF; |
65 | /* unsigned int ntlmv2_support = 0; */ | 65 | /* unsigned int ntlmv2_support = 0; */ |
66 | unsigned int sign_CIFS_PDUs = 1; | 66 | unsigned int sign_CIFS_PDUs = 1; |
67 | extern struct task_struct *oplockThread; /* remove sparse warning */ | ||
68 | struct task_struct *oplockThread = NULL; | ||
69 | /* extern struct task_struct * dnotifyThread; remove sparse warning */ | ||
70 | static const struct super_operations cifs_super_ops; | 67 | static const struct super_operations cifs_super_ops; |
71 | unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; | 68 | unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; |
72 | module_param(CIFSMaxBufSize, int, 0); | 69 | module_param(CIFSMaxBufSize, int, 0); |
@@ -973,89 +970,12 @@ cifs_destroy_mids(void) | |||
973 | kmem_cache_destroy(cifs_oplock_cachep); | 970 | kmem_cache_destroy(cifs_oplock_cachep); |
974 | } | 971 | } |
975 | 972 | ||
976 | static int cifs_oplock_thread(void *dummyarg) | ||
977 | { | ||
978 | struct oplock_q_entry *oplock_item; | ||
979 | struct cifsTconInfo *pTcon; | ||
980 | struct inode *inode; | ||
981 | __u16 netfid; | ||
982 | int rc, waitrc = 0; | ||
983 | |||
984 | set_freezable(); | ||
985 | do { | ||
986 | if (try_to_freeze()) | ||
987 | continue; | ||
988 | |||
989 | spin_lock(&cifs_oplock_lock); | ||
990 | if (list_empty(&cifs_oplock_list)) { | ||
991 | spin_unlock(&cifs_oplock_lock); | ||
992 | set_current_state(TASK_INTERRUPTIBLE); | ||
993 | schedule_timeout(39*HZ); | ||
994 | } else { | ||
995 | oplock_item = list_entry(cifs_oplock_list.next, | ||
996 | struct oplock_q_entry, qhead); | ||
997 | cFYI(1, ("found oplock item to write out")); | ||
998 | pTcon = oplock_item->tcon; | ||
999 | inode = oplock_item->pinode; | ||
1000 | netfid = oplock_item->netfid; | ||
1001 | spin_unlock(&cifs_oplock_lock); | ||
1002 | DeleteOplockQEntry(oplock_item); | ||
1003 | /* can not grab inode sem here since it would | ||
1004 | deadlock when oplock received on delete | ||
1005 | since vfs_unlink holds the i_mutex across | ||
1006 | the call */ | ||
1007 | /* mutex_lock(&inode->i_mutex);*/ | ||
1008 | if (S_ISREG(inode->i_mode)) { | ||
1009 | #ifdef CONFIG_CIFS_EXPERIMENTAL | ||
1010 | if (CIFS_I(inode)->clientCanCacheAll == 0) | ||
1011 | break_lease(inode, FMODE_READ); | ||
1012 | else if (CIFS_I(inode)->clientCanCacheRead == 0) | ||
1013 | break_lease(inode, FMODE_WRITE); | ||
1014 | #endif | ||
1015 | rc = filemap_fdatawrite(inode->i_mapping); | ||
1016 | if (CIFS_I(inode)->clientCanCacheRead == 0) { | ||
1017 | waitrc = filemap_fdatawait( | ||
1018 | inode->i_mapping); | ||
1019 | invalidate_remote_inode(inode); | ||
1020 | } | ||
1021 | if (rc == 0) | ||
1022 | rc = waitrc; | ||
1023 | } else | ||
1024 | rc = 0; | ||
1025 | /* mutex_unlock(&inode->i_mutex);*/ | ||
1026 | if (rc) | ||
1027 | CIFS_I(inode)->write_behind_rc = rc; | ||
1028 | cFYI(1, ("Oplock flush inode %p rc %d", | ||
1029 | inode, rc)); | ||
1030 | |||
1031 | /* releasing stale oplock after recent reconnect | ||
1032 | of smb session using a now incorrect file | ||
1033 | handle is not a data integrity issue but do | ||
1034 | not bother sending an oplock release if session | ||
1035 | to server still is disconnected since oplock | ||
1036 | already released by the server in that case */ | ||
1037 | if (!pTcon->need_reconnect) { | ||
1038 | rc = CIFSSMBLock(0, pTcon, netfid, | ||
1039 | 0 /* len */ , 0 /* offset */, 0, | ||
1040 | 0, LOCKING_ANDX_OPLOCK_RELEASE, | ||
1041 | false /* wait flag */); | ||
1042 | cFYI(1, ("Oplock release rc = %d", rc)); | ||
1043 | } | ||
1044 | set_current_state(TASK_INTERRUPTIBLE); | ||
1045 | schedule_timeout(1); /* yield in case q were corrupt */ | ||
1046 | } | ||
1047 | } while (!kthread_should_stop()); | ||
1048 | |||
1049 | return 0; | ||
1050 | } | ||
1051 | |||
1052 | static int __init | 973 | static int __init |
1053 | init_cifs(void) | 974 | init_cifs(void) |
1054 | { | 975 | { |
1055 | int rc = 0; | 976 | int rc = 0; |
1056 | cifs_proc_init(); | 977 | cifs_proc_init(); |
1057 | INIT_LIST_HEAD(&cifs_tcp_ses_list); | 978 | INIT_LIST_HEAD(&cifs_tcp_ses_list); |
1058 | INIT_LIST_HEAD(&cifs_oplock_list); | ||
1059 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 979 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
1060 | INIT_LIST_HEAD(&GlobalDnotifyReqList); | 980 | INIT_LIST_HEAD(&GlobalDnotifyReqList); |
1061 | INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); | 981 | INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); |
@@ -1084,7 +1004,6 @@ init_cifs(void) | |||
1084 | rwlock_init(&GlobalSMBSeslock); | 1004 | rwlock_init(&GlobalSMBSeslock); |
1085 | rwlock_init(&cifs_tcp_ses_lock); | 1005 | rwlock_init(&cifs_tcp_ses_lock); |
1086 | spin_lock_init(&GlobalMid_Lock); | 1006 | spin_lock_init(&GlobalMid_Lock); |
1087 | spin_lock_init(&cifs_oplock_lock); | ||
1088 | 1007 | ||
1089 | if (cifs_max_pending < 2) { | 1008 | if (cifs_max_pending < 2) { |
1090 | cifs_max_pending = 2; | 1009 | cifs_max_pending = 2; |
@@ -1119,18 +1038,15 @@ init_cifs(void) | |||
1119 | if (rc) | 1038 | if (rc) |
1120 | goto out_unregister_key_type; | 1039 | goto out_unregister_key_type; |
1121 | #endif | 1040 | #endif |
1122 | oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); | 1041 | rc = slow_work_register_user(); |
1123 | if (IS_ERR(oplockThread)) { | 1042 | if (rc) |
1124 | rc = PTR_ERR(oplockThread); | 1043 | goto out_unregister_resolver_key; |
1125 | cERROR(1, ("error %d create oplock thread", rc)); | ||
1126 | goto out_unregister_dfs_key_type; | ||
1127 | } | ||
1128 | 1044 | ||
1129 | return 0; | 1045 | return 0; |
1130 | 1046 | ||
1131 | out_unregister_dfs_key_type: | 1047 | out_unregister_resolver_key: |
1132 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
1133 | unregister_key_type(&key_type_dns_resolver); | 1048 | unregister_key_type(&key_type_dns_resolver); |
1049 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
1134 | out_unregister_key_type: | 1050 | out_unregister_key_type: |
1135 | #endif | 1051 | #endif |
1136 | #ifdef CONFIG_CIFS_UPCALL | 1052 | #ifdef CONFIG_CIFS_UPCALL |
@@ -1165,7 +1081,6 @@ exit_cifs(void) | |||
1165 | cifs_destroy_inodecache(); | 1081 | cifs_destroy_inodecache(); |
1166 | cifs_destroy_mids(); | 1082 | cifs_destroy_mids(); |
1167 | cifs_destroy_request_bufs(); | 1083 | cifs_destroy_request_bufs(); |
1168 | kthread_stop(oplockThread); | ||
1169 | } | 1084 | } |
1170 | 1085 | ||
1171 | MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); | 1086 | MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c19419a38f62..5d0fde18039c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -18,6 +18,7 @@ | |||
18 | */ | 18 | */ |
19 | #include <linux/in.h> | 19 | #include <linux/in.h> |
20 | #include <linux/in6.h> | 20 | #include <linux/in6.h> |
21 | #include <linux/slow-work.h> | ||
21 | #include "cifs_fs_sb.h" | 22 | #include "cifs_fs_sb.h" |
22 | #include "cifsacl.h" | 23 | #include "cifsacl.h" |
23 | /* | 24 | /* |
@@ -346,14 +347,16 @@ struct cifsFileInfo { | |||
346 | /* lock scope id (0 if none) */ | 347 | /* lock scope id (0 if none) */ |
347 | struct file *pfile; /* needed for writepage */ | 348 | struct file *pfile; /* needed for writepage */ |
348 | struct inode *pInode; /* needed for oplock break */ | 349 | struct inode *pInode; /* needed for oplock break */ |
350 | struct vfsmount *mnt; | ||
349 | struct mutex lock_mutex; | 351 | struct mutex lock_mutex; |
350 | struct list_head llist; /* list of byte range locks we have. */ | 352 | struct list_head llist; /* list of byte range locks we have. */ |
351 | bool closePend:1; /* file is marked to close */ | 353 | bool closePend:1; /* file is marked to close */ |
352 | bool invalidHandle:1; /* file closed via session abend */ | 354 | bool invalidHandle:1; /* file closed via session abend */ |
353 | bool messageMode:1; /* for pipes: message vs byte mode */ | 355 | bool oplock_break_cancelled:1; |
354 | atomic_t count; /* reference count */ | 356 | atomic_t count; /* reference count */ |
355 | struct mutex fh_mutex; /* prevents reopen race after dead ses*/ | 357 | struct mutex fh_mutex; /* prevents reopen race after dead ses*/ |
356 | struct cifs_search_info srch_inf; | 358 | struct cifs_search_info srch_inf; |
359 | struct slow_work oplock_break; /* slow_work job for oplock breaks */ | ||
357 | }; | 360 | }; |
358 | 361 | ||
359 | /* Take a reference on the file private data */ | 362 | /* Take a reference on the file private data */ |
@@ -670,12 +673,6 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; | |||
670 | */ | 673 | */ |
671 | GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; | 674 | GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; |
672 | 675 | ||
673 | /* Global list of oplocks */ | ||
674 | GLOBAL_EXTERN struct list_head cifs_oplock_list; | ||
675 | |||
676 | /* Protects the cifs_oplock_list */ | ||
677 | GLOBAL_EXTERN spinlock_t cifs_oplock_lock; | ||
678 | |||
679 | /* Outstanding dir notify requests */ | 676 | /* Outstanding dir notify requests */ |
680 | GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; | 677 | GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; |
681 | /* DirNotify response queue */ | 678 | /* DirNotify response queue */ |
@@ -726,3 +723,4 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ | |||
726 | GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ | 723 | GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ |
727 | GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ | 724 | GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ |
728 | 725 | ||
726 | extern const struct slow_work_ops cifs_oplock_break_ops; | ||
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index bf3ae881b2d5..733e71b57c7a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -86,17 +86,13 @@ extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, | |||
86 | const int stage, | 86 | const int stage, |
87 | const struct nls_table *nls_cp); | 87 | const struct nls_table *nls_cp); |
88 | extern __u16 GetNextMid(struct TCP_Server_Info *server); | 88 | extern __u16 GetNextMid(struct TCP_Server_Info *server); |
89 | extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16, | ||
90 | struct cifsTconInfo *); | ||
91 | extern void DeleteOplockQEntry(struct oplock_q_entry *); | ||
92 | extern void DeleteTconOplockQEntries(struct cifsTconInfo *); | ||
93 | extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); | 89 | extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); |
94 | extern u64 cifs_UnixTimeToNT(struct timespec); | 90 | extern u64 cifs_UnixTimeToNT(struct timespec); |
95 | extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, | 91 | extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, |
96 | int offset); | 92 | int offset); |
97 | 93 | ||
98 | extern int cifs_posix_open(char *full_path, struct inode **pinode, | 94 | extern int cifs_posix_open(char *full_path, struct inode **pinode, |
99 | struct super_block *sb, int mode, int oflags, | 95 | struct vfsmount *mnt, int mode, int oflags, |
100 | __u32 *poplock, __u16 *pnetfid, int xid); | 96 | __u32 *poplock, __u16 *pnetfid, int xid); |
101 | extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, | 97 | extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, |
102 | FILE_UNIX_BASIC_INFO *info, | 98 | FILE_UNIX_BASIC_INFO *info, |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 301e307e1279..941441d3e386 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -94,6 +94,7 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) | |||
94 | list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { | 94 | list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { |
95 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); | 95 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); |
96 | open_file->invalidHandle = true; | 96 | open_file->invalidHandle = true; |
97 | open_file->oplock_break_cancelled = true; | ||
97 | } | 98 | } |
98 | write_unlock(&GlobalSMBSeslock); | 99 | write_unlock(&GlobalSMBSeslock); |
99 | /* BB Add call to invalidate_inodes(sb) for all superblocks mounted | 100 | /* BB Add call to invalidate_inodes(sb) for all superblocks mounted |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d49682433c20..43003e0bef18 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -1670,7 +1670,6 @@ cifs_put_tcon(struct cifsTconInfo *tcon) | |||
1670 | CIFSSMBTDis(xid, tcon); | 1670 | CIFSSMBTDis(xid, tcon); |
1671 | _FreeXid(xid); | 1671 | _FreeXid(xid); |
1672 | 1672 | ||
1673 | DeleteTconOplockQEntries(tcon); | ||
1674 | tconInfoFree(tcon); | 1673 | tconInfoFree(tcon); |
1675 | cifs_put_smb_ses(ses); | 1674 | cifs_put_smb_ses(ses); |
1676 | } | 1675 | } |
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 36435502b5e8..9a5df7a84698 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/stat.h> | 24 | #include <linux/stat.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/namei.h> | 26 | #include <linux/namei.h> |
27 | #include <linux/mount.h> | ||
27 | #include "cifsfs.h" | 28 | #include "cifsfs.h" |
28 | #include "cifspdu.h" | 29 | #include "cifspdu.h" |
29 | #include "cifsglob.h" | 30 | #include "cifsglob.h" |
@@ -131,11 +132,12 @@ cifs_bp_rename_retry: | |||
131 | 132 | ||
132 | static void | 133 | static void |
133 | cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle, | 134 | cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle, |
134 | struct cifsTconInfo *tcon, bool write_only) | 135 | struct vfsmount *mnt, bool write_only) |
135 | { | 136 | { |
136 | int oplock = 0; | 137 | int oplock = 0; |
137 | struct cifsFileInfo *pCifsFile; | 138 | struct cifsFileInfo *pCifsFile; |
138 | struct cifsInodeInfo *pCifsInode; | 139 | struct cifsInodeInfo *pCifsInode; |
140 | struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb); | ||
139 | 141 | ||
140 | pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); | 142 | pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); |
141 | 143 | ||
@@ -148,17 +150,19 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle, | |||
148 | pCifsFile->netfid = fileHandle; | 150 | pCifsFile->netfid = fileHandle; |
149 | pCifsFile->pid = current->tgid; | 151 | pCifsFile->pid = current->tgid; |
150 | pCifsFile->pInode = igrab(newinode); | 152 | pCifsFile->pInode = igrab(newinode); |
153 | pCifsFile->mnt = mnt; | ||
151 | pCifsFile->invalidHandle = false; | 154 | pCifsFile->invalidHandle = false; |
152 | pCifsFile->closePend = false; | 155 | pCifsFile->closePend = false; |
153 | mutex_init(&pCifsFile->fh_mutex); | 156 | mutex_init(&pCifsFile->fh_mutex); |
154 | mutex_init(&pCifsFile->lock_mutex); | 157 | mutex_init(&pCifsFile->lock_mutex); |
155 | INIT_LIST_HEAD(&pCifsFile->llist); | 158 | INIT_LIST_HEAD(&pCifsFile->llist); |
156 | atomic_set(&pCifsFile->count, 1); | 159 | atomic_set(&pCifsFile->count, 1); |
160 | slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops); | ||
157 | 161 | ||
158 | /* set the following in open now | 162 | /* set the following in open now |
159 | pCifsFile->pfile = file; */ | 163 | pCifsFile->pfile = file; */ |
160 | write_lock(&GlobalSMBSeslock); | 164 | write_lock(&GlobalSMBSeslock); |
161 | list_add(&pCifsFile->tlist, &tcon->openFileList); | 165 | list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList); |
162 | pCifsInode = CIFS_I(newinode); | 166 | pCifsInode = CIFS_I(newinode); |
163 | if (pCifsInode) { | 167 | if (pCifsInode) { |
164 | /* if readable file instance put first in list*/ | 168 | /* if readable file instance put first in list*/ |
@@ -179,14 +183,14 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle, | |||
179 | } | 183 | } |
180 | 184 | ||
181 | int cifs_posix_open(char *full_path, struct inode **pinode, | 185 | int cifs_posix_open(char *full_path, struct inode **pinode, |
182 | struct super_block *sb, int mode, int oflags, | 186 | struct vfsmount *mnt, int mode, int oflags, |
183 | __u32 *poplock, __u16 *pnetfid, int xid) | 187 | __u32 *poplock, __u16 *pnetfid, int xid) |
184 | { | 188 | { |
185 | int rc; | 189 | int rc; |
186 | bool write_only = false; | 190 | bool write_only = false; |
187 | FILE_UNIX_BASIC_INFO *presp_data; | 191 | FILE_UNIX_BASIC_INFO *presp_data; |
188 | __u32 posix_flags = 0; | 192 | __u32 posix_flags = 0; |
189 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | 193 | struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb); |
190 | struct cifs_fattr fattr; | 194 | struct cifs_fattr fattr; |
191 | 195 | ||
192 | cFYI(1, ("posix open %s", full_path)); | 196 | cFYI(1, ("posix open %s", full_path)); |
@@ -243,7 +247,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode, | |||
243 | 247 | ||
244 | /* get new inode and set it up */ | 248 | /* get new inode and set it up */ |
245 | if (*pinode == NULL) { | 249 | if (*pinode == NULL) { |
246 | *pinode = cifs_iget(sb, &fattr); | 250 | *pinode = cifs_iget(mnt->mnt_sb, &fattr); |
247 | if (!*pinode) { | 251 | if (!*pinode) { |
248 | rc = -ENOMEM; | 252 | rc = -ENOMEM; |
249 | goto posix_open_ret; | 253 | goto posix_open_ret; |
@@ -252,7 +256,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode, | |||
252 | cifs_fattr_to_inode(*pinode, &fattr); | 256 | cifs_fattr_to_inode(*pinode, &fattr); |
253 | } | 257 | } |
254 | 258 | ||
255 | cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only); | 259 | cifs_fill_fileinfo(*pinode, *pnetfid, mnt, write_only); |
256 | 260 | ||
257 | posix_open_ret: | 261 | posix_open_ret: |
258 | kfree(presp_data); | 262 | kfree(presp_data); |
@@ -322,7 +326,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, | |||
322 | if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && | 326 | if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && |
323 | (CIFS_UNIX_POSIX_PATH_OPS_CAP & | 327 | (CIFS_UNIX_POSIX_PATH_OPS_CAP & |
324 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { | 328 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { |
325 | rc = cifs_posix_open(full_path, &newinode, inode->i_sb, | 329 | rc = cifs_posix_open(full_path, &newinode, nd->path.mnt, |
326 | mode, oflags, &oplock, &fileHandle, xid); | 330 | mode, oflags, &oplock, &fileHandle, xid); |
327 | /* EIO could indicate that (posix open) operation is not | 331 | /* EIO could indicate that (posix open) operation is not |
328 | supported, despite what server claimed in capability | 332 | supported, despite what server claimed in capability |
@@ -469,8 +473,8 @@ cifs_create_set_dentry: | |||
469 | /* mknod case - do not leave file open */ | 473 | /* mknod case - do not leave file open */ |
470 | CIFSSMBClose(xid, tcon, fileHandle); | 474 | CIFSSMBClose(xid, tcon, fileHandle); |
471 | } else if (!(posix_create) && (newinode)) { | 475 | } else if (!(posix_create) && (newinode)) { |
472 | cifs_fill_fileinfo(newinode, fileHandle, | 476 | cifs_fill_fileinfo(newinode, fileHandle, nd->path.mnt, |
473 | cifs_sb->tcon, write_only); | 477 | write_only); |
474 | } | 478 | } |
475 | cifs_create_out: | 479 | cifs_create_out: |
476 | kfree(buf); | 480 | kfree(buf); |
@@ -682,8 +686,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, | |||
682 | if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && | 686 | if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && |
683 | (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && | 687 | (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && |
684 | (nd->intent.open.flags & O_CREAT)) { | 688 | (nd->intent.open.flags & O_CREAT)) { |
685 | rc = cifs_posix_open(full_path, &newInode, | 689 | rc = cifs_posix_open(full_path, &newInode, nd->path.mnt, |
686 | parent_dir_inode->i_sb, | ||
687 | nd->intent.open.create_mode, | 690 | nd->intent.open.create_mode, |
688 | nd->intent.open.flags, &oplock, | 691 | nd->intent.open.flags, &oplock, |
689 | &fileHandle, xid); | 692 | &fileHandle, xid); |
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 | ||
2318 | static void | ||
2319 | cifs_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 | |||
2360 | static int | ||
2361 | cifs_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 | |||
2370 | static void | ||
2371 | cifs_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 | |||
2379 | const 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 | |||
2315 | const struct address_space_operations cifs_addr_ops = { | 2385 | const struct address_space_operations cifs_addr_ops = { |
2316 | .readpage = cifs_readpage, | 2386 | .readpage = cifs_readpage, |
2317 | .readpages = cifs_readpages, | 2387 | .readpages = cifs_readpages, |
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 191e6220bc76..0241b25ac33f 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -32,7 +32,6 @@ | |||
32 | 32 | ||
33 | extern mempool_t *cifs_sm_req_poolp; | 33 | extern mempool_t *cifs_sm_req_poolp; |
34 | extern mempool_t *cifs_req_poolp; | 34 | extern mempool_t *cifs_req_poolp; |
35 | extern struct task_struct *oplockThread; | ||
36 | 35 | ||
37 | /* The xid serves as a useful identifier for each incoming vfs request, | 36 | /* The xid serves as a useful identifier for each incoming vfs request, |
38 | in a similar way to the mid which is useful to track each sent smb, | 37 | in a similar way to the mid which is useful to track each sent smb, |
@@ -500,6 +499,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) | |||
500 | struct cifsTconInfo *tcon; | 499 | struct cifsTconInfo *tcon; |
501 | struct cifsInodeInfo *pCifsInode; | 500 | struct cifsInodeInfo *pCifsInode; |
502 | struct cifsFileInfo *netfile; | 501 | struct cifsFileInfo *netfile; |
502 | int rc; | ||
503 | 503 | ||
504 | cFYI(1, ("Checking for oplock break or dnotify response")); | 504 | cFYI(1, ("Checking for oplock break or dnotify response")); |
505 | if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && | 505 | if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && |
@@ -569,19 +569,30 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) | |||
569 | if (pSMB->Fid != netfile->netfid) | 569 | if (pSMB->Fid != netfile->netfid) |
570 | continue; | 570 | continue; |
571 | 571 | ||
572 | read_unlock(&GlobalSMBSeslock); | 572 | /* |
573 | read_unlock(&cifs_tcp_ses_lock); | 573 | * don't do anything if file is about to be |
574 | * closed anyway. | ||
575 | */ | ||
576 | if (netfile->closePend) { | ||
577 | read_unlock(&GlobalSMBSeslock); | ||
578 | read_unlock(&cifs_tcp_ses_lock); | ||
579 | return true; | ||
580 | } | ||
581 | |||
574 | cFYI(1, ("file id match, oplock break")); | 582 | cFYI(1, ("file id match, oplock break")); |
575 | pCifsInode = CIFS_I(netfile->pInode); | 583 | pCifsInode = CIFS_I(netfile->pInode); |
576 | pCifsInode->clientCanCacheAll = false; | 584 | pCifsInode->clientCanCacheAll = false; |
577 | if (pSMB->OplockLevel == 0) | 585 | if (pSMB->OplockLevel == 0) |
578 | pCifsInode->clientCanCacheRead = false; | 586 | pCifsInode->clientCanCacheRead = false; |
579 | AllocOplockQEntry(netfile->pInode, | 587 | rc = slow_work_enqueue(&netfile->oplock_break); |
580 | netfile->netfid, tcon); | 588 | if (rc) { |
581 | cFYI(1, ("about to wake up oplock thread")); | 589 | cERROR(1, ("failed to enqueue oplock " |
582 | if (oplockThread) | 590 | "break: %d\n", rc)); |
583 | wake_up_process(oplockThread); | 591 | } else { |
584 | 592 | netfile->oplock_break_cancelled = false; | |
593 | } | ||
594 | read_unlock(&GlobalSMBSeslock); | ||
595 | read_unlock(&cifs_tcp_ses_lock); | ||
585 | return true; | 596 | return true; |
586 | } | 597 | } |
587 | read_unlock(&GlobalSMBSeslock); | 598 | read_unlock(&GlobalSMBSeslock); |
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 1da4ab250eae..07b8e71544ee 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -103,56 +103,6 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) | |||
103 | mempool_free(midEntry, cifs_mid_poolp); | 103 | mempool_free(midEntry, cifs_mid_poolp); |
104 | } | 104 | } |
105 | 105 | ||
106 | struct oplock_q_entry * | ||
107 | AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon) | ||
108 | { | ||
109 | struct oplock_q_entry *temp; | ||
110 | if ((pinode == NULL) || (tcon == NULL)) { | ||
111 | cERROR(1, ("Null parms passed to AllocOplockQEntry")); | ||
112 | return NULL; | ||
113 | } | ||
114 | temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep, | ||
115 | GFP_KERNEL); | ||
116 | if (temp == NULL) | ||
117 | return temp; | ||
118 | else { | ||
119 | temp->pinode = pinode; | ||
120 | temp->tcon = tcon; | ||
121 | temp->netfid = fid; | ||
122 | spin_lock(&cifs_oplock_lock); | ||
123 | list_add_tail(&temp->qhead, &cifs_oplock_list); | ||
124 | spin_unlock(&cifs_oplock_lock); | ||
125 | } | ||
126 | return temp; | ||
127 | } | ||
128 | |||
129 | void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry) | ||
130 | { | ||
131 | spin_lock(&cifs_oplock_lock); | ||
132 | /* should we check if list empty first? */ | ||
133 | list_del(&oplockEntry->qhead); | ||
134 | spin_unlock(&cifs_oplock_lock); | ||
135 | kmem_cache_free(cifs_oplock_cachep, oplockEntry); | ||
136 | } | ||
137 | |||
138 | |||
139 | void DeleteTconOplockQEntries(struct cifsTconInfo *tcon) | ||
140 | { | ||
141 | struct oplock_q_entry *temp; | ||
142 | |||
143 | if (tcon == NULL) | ||
144 | return; | ||
145 | |||
146 | spin_lock(&cifs_oplock_lock); | ||
147 | list_for_each_entry(temp, &cifs_oplock_list, qhead) { | ||
148 | if ((temp->tcon) && (temp->tcon == tcon)) { | ||
149 | list_del(&temp->qhead); | ||
150 | kmem_cache_free(cifs_oplock_cachep, temp); | ||
151 | } | ||
152 | } | ||
153 | spin_unlock(&cifs_oplock_lock); | ||
154 | } | ||
155 | |||
156 | static int | 106 | static int |
157 | smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) | 107 | smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) |
158 | { | 108 | { |