Commit 07e2e6ba authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  cifs: fix locking and list handling code in cifs_open and its helper
  [CIFS] Remove build warning
  cifs: fix problems with last two commits
  [CIFS] Fix build break when keys support turned off
  cifs: eliminate cifs_init_private
  cifs: convert oplock breaks to use slow_work facility (try #4)
  cifs: have cifsFileInfo hold an extra inode reference
  cifs: take read lock on GlobalSMBSes_lock in is_valid_oplock_break
  cifs: remove cifsInodeInfo.oplockPending flag
  cifs: fix oplock request handling in posix codepath
  [CIFS] Re-enable Lanman security
parents d8f654ef 3321b791
...@@ -2,6 +2,7 @@ config CIFS ...@@ -2,6 +2,7 @@ config CIFS
tristate "CIFS support (advanced network filesystem, SMBFS successor)" tristate "CIFS support (advanced network filesystem, SMBFS successor)"
depends on INET depends on INET
select NLS select NLS
select SLOW_WORK
help help
This is the client VFS module for the Common Internet File System This is the client VFS module for the Common Internet File System
(CIFS) protocol which is the successor to the Server Message Block (CIFS) protocol which is the successor to the Server Message Block
......
...@@ -64,9 +64,6 @@ unsigned int multiuser_mount = 0; ...@@ -64,9 +64,6 @@ unsigned int multiuser_mount = 0;
unsigned int extended_security = CIFSSEC_DEF; unsigned int extended_security = CIFSSEC_DEF;
/* unsigned int ntlmv2_support = 0; */ /* unsigned int ntlmv2_support = 0; */
unsigned int sign_CIFS_PDUs = 1; unsigned int sign_CIFS_PDUs = 1;
extern struct task_struct *oplockThread; /* remove sparse warning */
struct task_struct *oplockThread = NULL;
/* extern struct task_struct * dnotifyThread; remove sparse warning */
static const struct super_operations cifs_super_ops; static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
module_param(CIFSMaxBufSize, int, 0); module_param(CIFSMaxBufSize, int, 0);
...@@ -972,89 +969,12 @@ cifs_destroy_mids(void) ...@@ -972,89 +969,12 @@ cifs_destroy_mids(void)
kmem_cache_destroy(cifs_oplock_cachep); kmem_cache_destroy(cifs_oplock_cachep);
} }
static int cifs_oplock_thread(void *dummyarg)
{
struct oplock_q_entry *oplock_item;
struct cifsTconInfo *pTcon;
struct inode *inode;
__u16 netfid;
int rc, waitrc = 0;
set_freezable();
do {
if (try_to_freeze())
continue;
spin_lock(&cifs_oplock_lock);
if (list_empty(&cifs_oplock_list)) {
spin_unlock(&cifs_oplock_lock);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(39*HZ);
} else {
oplock_item = list_entry(cifs_oplock_list.next,
struct oplock_q_entry, qhead);
cFYI(1, ("found oplock item to write out"));
pTcon = oplock_item->tcon;
inode = oplock_item->pinode;
netfid = oplock_item->netfid;
spin_unlock(&cifs_oplock_lock);
DeleteOplockQEntry(oplock_item);
/* can not grab inode sem here since it would
deadlock when oplock received on delete
since vfs_unlink holds the i_mutex across
the call */
/* mutex_lock(&inode->i_mutex);*/
if (S_ISREG(inode->i_mode)) {
#ifdef CONFIG_CIFS_EXPERIMENTAL
if (CIFS_I(inode)->clientCanCacheAll == 0)
break_lease(inode, FMODE_READ);
else if (CIFS_I(inode)->clientCanCacheRead == 0)
break_lease(inode, FMODE_WRITE);
#endif
rc = filemap_fdatawrite(inode->i_mapping);
if (CIFS_I(inode)->clientCanCacheRead == 0) {
waitrc = filemap_fdatawait(
inode->i_mapping);
invalidate_remote_inode(inode);
}
if (rc == 0)
rc = waitrc;
} else
rc = 0;
/* mutex_unlock(&inode->i_mutex);*/
if (rc)
CIFS_I(inode)->write_behind_rc = rc;
cFYI(1, ("Oplock flush inode %p rc %d",
inode, rc));
/* releasing stale oplock after recent reconnect
of smb session using a now incorrect file
handle is not a data integrity issue but do
not bother sending an oplock release if session
to server still is disconnected since oplock
already released by the server in that case */
if (!pTcon->need_reconnect) {
rc = CIFSSMBLock(0, pTcon, netfid,
0 /* len */ , 0 /* offset */, 0,
0, LOCKING_ANDX_OPLOCK_RELEASE,
false /* wait flag */);
cFYI(1, ("Oplock release rc = %d", rc));
}
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1); /* yield in case q were corrupt */
}
} while (!kthread_should_stop());
return 0;
}
static int __init static int __init
init_cifs(void) init_cifs(void)
{ {
int rc = 0; int rc = 0;
cifs_proc_init(); cifs_proc_init();
INIT_LIST_HEAD(&cifs_tcp_ses_list); INIT_LIST_HEAD(&cifs_tcp_ses_list);
INIT_LIST_HEAD(&cifs_oplock_list);
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
INIT_LIST_HEAD(&GlobalDnotifyReqList); INIT_LIST_HEAD(&GlobalDnotifyReqList);
INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
...@@ -1083,7 +1003,6 @@ init_cifs(void) ...@@ -1083,7 +1003,6 @@ init_cifs(void)
rwlock_init(&GlobalSMBSeslock); rwlock_init(&GlobalSMBSeslock);
rwlock_init(&cifs_tcp_ses_lock); rwlock_init(&cifs_tcp_ses_lock);
spin_lock_init(&GlobalMid_Lock); spin_lock_init(&GlobalMid_Lock);
spin_lock_init(&cifs_oplock_lock);
if (cifs_max_pending < 2) { if (cifs_max_pending < 2) {
cifs_max_pending = 2; cifs_max_pending = 2;
...@@ -1118,16 +1037,13 @@ init_cifs(void) ...@@ -1118,16 +1037,13 @@ init_cifs(void)
if (rc) if (rc)
goto out_unregister_key_type; goto out_unregister_key_type;
#endif #endif
oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); rc = slow_work_register_user();
if (IS_ERR(oplockThread)) { if (rc)
rc = PTR_ERR(oplockThread); goto out_unregister_resolver_key;
cERROR(1, ("error %d create oplock thread", rc));
goto out_unregister_dfs_key_type;
}
return 0; return 0;
out_unregister_dfs_key_type: out_unregister_resolver_key:
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
unregister_key_type(&key_type_dns_resolver); unregister_key_type(&key_type_dns_resolver);
out_unregister_key_type: out_unregister_key_type:
...@@ -1164,7 +1080,6 @@ exit_cifs(void) ...@@ -1164,7 +1080,6 @@ exit_cifs(void)
cifs_destroy_inodecache(); cifs_destroy_inodecache();
cifs_destroy_mids(); cifs_destroy_mids();
cifs_destroy_request_bufs(); cifs_destroy_request_bufs();
kthread_stop(oplockThread);
} }
MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
*/ */
#include <linux/in.h> #include <linux/in.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/slow-work.h>
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifsacl.h" #include "cifsacl.h"
/* /*
...@@ -346,14 +347,16 @@ struct cifsFileInfo { ...@@ -346,14 +347,16 @@ struct cifsFileInfo {
/* lock scope id (0 if none) */ /* lock scope id (0 if none) */
struct file *pfile; /* needed for writepage */ struct file *pfile; /* needed for writepage */
struct inode *pInode; /* needed for oplock break */ struct inode *pInode; /* needed for oplock break */
struct vfsmount *mnt;
struct mutex lock_mutex; struct mutex lock_mutex;
struct list_head llist; /* list of byte range locks we have. */ struct list_head llist; /* list of byte range locks we have. */
bool closePend:1; /* file is marked to close */ bool closePend:1; /* file is marked to close */
bool invalidHandle:1; /* file closed via session abend */ bool invalidHandle:1; /* file closed via session abend */
bool messageMode:1; /* for pipes: message vs byte mode */ bool oplock_break_cancelled:1;
atomic_t count; /* reference count */ atomic_t count; /* reference count */
struct mutex fh_mutex; /* prevents reopen race after dead ses*/ struct mutex fh_mutex; /* prevents reopen race after dead ses*/
struct cifs_search_info srch_inf; struct cifs_search_info srch_inf;
struct slow_work oplock_break; /* slow_work job for oplock breaks */
}; };
/* Take a reference on the file private data */ /* Take a reference on the file private data */
...@@ -365,8 +368,10 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) ...@@ -365,8 +368,10 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
/* Release a reference on the file private data */ /* Release a reference on the file private data */
static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file) static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{ {
if (atomic_dec_and_test(&cifs_file->count)) if (atomic_dec_and_test(&cifs_file->count)) {
iput(cifs_file->pInode);
kfree(cifs_file); kfree(cifs_file);
}
} }
/* /*
...@@ -382,7 +387,6 @@ struct cifsInodeInfo { ...@@ -382,7 +387,6 @@ struct cifsInodeInfo {
unsigned long time; /* jiffies of last update/check of inode */ unsigned long time; /* jiffies of last update/check of inode */
bool clientCanCacheRead:1; /* read oplock */ bool clientCanCacheRead:1; /* read oplock */
bool clientCanCacheAll:1; /* read and writebehind oplock */ bool clientCanCacheAll:1; /* read and writebehind oplock */
bool oplockPending:1;
bool delete_pending:1; /* DELETE_ON_CLOSE is set */ bool delete_pending:1; /* DELETE_ON_CLOSE is set */
u64 server_eof; /* current file size on server */ u64 server_eof; /* current file size on server */
u64 uniqueid; /* server inode number */ u64 uniqueid; /* server inode number */
...@@ -585,9 +589,9 @@ require use of the stronger protocol */ ...@@ -585,9 +589,9 @@ require use of the stronger protocol */
#define CIFSSEC_MUST_LANMAN 0x10010 #define CIFSSEC_MUST_LANMAN 0x10010
#define CIFSSEC_MUST_PLNTXT 0x20020 #define CIFSSEC_MUST_PLNTXT 0x20020
#ifdef CONFIG_CIFS_UPCALL #ifdef CONFIG_CIFS_UPCALL
#define CIFSSEC_MASK 0xAF0AF /* allows weak security but also krb5 */ #define CIFSSEC_MASK 0xBF0BF /* allows weak security but also krb5 */
#else #else
#define CIFSSEC_MASK 0xA70A7 /* current flags supported if weak */ #define CIFSSEC_MASK 0xB70B7 /* current flags supported if weak */
#endif /* UPCALL */ #endif /* UPCALL */
#else /* do not allow weak pw hash */ #else /* do not allow weak pw hash */
#ifdef CONFIG_CIFS_UPCALL #ifdef CONFIG_CIFS_UPCALL
...@@ -669,12 +673,6 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; ...@@ -669,12 +673,6 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
*/ */
GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;
/* Global list of oplocks */
GLOBAL_EXTERN struct list_head cifs_oplock_list;
/* Protects the cifs_oplock_list */
GLOBAL_EXTERN spinlock_t cifs_oplock_lock;
/* Outstanding dir notify requests */ /* Outstanding dir notify requests */
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
/* DirNotify response queue */ /* DirNotify response queue */
...@@ -725,3 +723,4 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ ...@@ -725,3 +723,4 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */
GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
extern const struct slow_work_ops cifs_oplock_break_ops;
...@@ -86,18 +86,17 @@ extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -86,18 +86,17 @@ extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
const int stage, const int stage,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
extern __u16 GetNextMid(struct TCP_Server_Info *server); extern __u16 GetNextMid(struct TCP_Server_Info *server);
extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16,
struct cifsTconInfo *);
extern void DeleteOplockQEntry(struct oplock_q_entry *);
extern void DeleteTconOplockQEntries(struct cifsTconInfo *);
extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
extern u64 cifs_UnixTimeToNT(struct timespec); extern u64 cifs_UnixTimeToNT(struct timespec);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
int offset); int offset);
extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode,
__u16 fileHandle, struct file *file,
struct vfsmount *mnt, unsigned int oflags);
extern int cifs_posix_open(char *full_path, struct inode **pinode, extern int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, int mode, int oflags, struct vfsmount *mnt, int mode, int oflags,
int *poplock, __u16 *pnetfid, int xid); __u32 *poplock, __u16 *pnetfid, int xid);
extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
FILE_UNIX_BASIC_INFO *info, FILE_UNIX_BASIC_INFO *info,
struct cifs_sb_info *cifs_sb); struct cifs_sb_info *cifs_sb);
......
...@@ -94,6 +94,7 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) ...@@ -94,6 +94,7 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
open_file = list_entry(tmp, struct cifsFileInfo, tlist); open_file = list_entry(tmp, struct cifsFileInfo, tlist);
open_file->invalidHandle = true; open_file->invalidHandle = true;
open_file->oplock_break_cancelled = true;
} }
write_unlock(&GlobalSMBSeslock); write_unlock(&GlobalSMBSeslock);
/* BB Add call to invalidate_inodes(sb) for all superblocks mounted /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
......
...@@ -1670,7 +1670,6 @@ cifs_put_tcon(struct cifsTconInfo *tcon) ...@@ -1670,7 +1670,6 @@ cifs_put_tcon(struct cifsTconInfo *tcon)
CIFSSMBTDis(xid, tcon); CIFSSMBTDis(xid, tcon);
_FreeXid(xid); _FreeXid(xid);
DeleteTconOplockQEntries(tcon);
tconInfoFree(tcon); tconInfoFree(tcon);
cifs_put_smb_ses(ses); cifs_put_smb_ses(ses);
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mount.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsglob.h" #include "cifsglob.h"
...@@ -129,44 +130,45 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -129,44 +130,45 @@ build_path_from_dentry(struct dentry *direntry)
return full_path; return full_path;
} }
static void struct cifsFileInfo *
cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle, cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
struct cifsTconInfo *tcon, bool write_only) struct file *file, struct vfsmount *mnt, unsigned int oflags)
{ {
int oplock = 0; int oplock = 0;
struct cifsFileInfo *pCifsFile; struct cifsFileInfo *pCifsFile;
struct cifsInodeInfo *pCifsInode; struct cifsInodeInfo *pCifsInode;
struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (pCifsFile == NULL) if (pCifsFile == NULL)
return; return pCifsFile;
if (oplockEnabled) if (oplockEnabled)
oplock = REQ_OPLOCK; oplock = REQ_OPLOCK;
pCifsFile->netfid = fileHandle; pCifsFile->netfid = fileHandle;
pCifsFile->pid = current->tgid; pCifsFile->pid = current->tgid;
pCifsFile->pInode = newinode; pCifsFile->pInode = igrab(newinode);
pCifsFile->mnt = mnt;
pCifsFile->pfile = file;
pCifsFile->invalidHandle = false; pCifsFile->invalidHandle = false;
pCifsFile->closePend = false; pCifsFile->closePend = false;
mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->fh_mutex);
mutex_init(&pCifsFile->lock_mutex); mutex_init(&pCifsFile->lock_mutex);
INIT_LIST_HEAD(&pCifsFile->llist); INIT_LIST_HEAD(&pCifsFile->llist);
atomic_set(&pCifsFile->count, 1); atomic_set(&pCifsFile->count, 1);
slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
/* set the following in open now
pCifsFile->pfile = file; */
write_lock(&GlobalSMBSeslock); write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &tcon->openFileList); list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
pCifsInode = CIFS_I(newinode); pCifsInode = CIFS_I(newinode);
if (pCifsInode) { if (pCifsInode) {
/* if readable file instance put first in list*/ /* if readable file instance put first in list*/
if (write_only) if (oflags & FMODE_READ)
list_add(&pCifsFile->flist, &pCifsInode->openFileList);
else
list_add_tail(&pCifsFile->flist, list_add_tail(&pCifsFile->flist,
&pCifsInode->openFileList); &pCifsInode->openFileList);
else
list_add(&pCifsFile->flist, &pCifsInode->openFileList);
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheAll = true;
...@@ -176,18 +178,18 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle, ...@@ -176,18 +178,18 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheRead = true;
} }
write_unlock(&GlobalSMBSeslock); write_unlock(&GlobalSMBSeslock);
return pCifsFile;
} }
int cifs_posix_open(char *full_path, struct inode **pinode, int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, int mode, int oflags, struct vfsmount *mnt, int mode, int oflags,
int *poplock, __u16 *pnetfid, int xid) __u32 *poplock, __u16 *pnetfid, int xid)
{ {
int rc; int rc;
__u32 oplock;
bool write_only = false;
FILE_UNIX_BASIC_INFO *presp_data; FILE_UNIX_BASIC_INFO *presp_data;
__u32 posix_flags = 0; __u32 posix_flags = 0;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
struct cifs_fattr fattr; struct cifs_fattr fattr;
cFYI(1, ("posix open %s", full_path)); cFYI(1, ("posix open %s", full_path));
...@@ -223,12 +225,9 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -223,12 +225,9 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
if (oflags & O_DIRECT) if (oflags & O_DIRECT)
posix_flags |= SMB_O_DIRECT; posix_flags |= SMB_O_DIRECT;
if (!(oflags & FMODE_READ))
write_only = true;
mode &= ~current_umask(); mode &= ~current_umask();
rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode, rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
pnetfid, presp_data, &oplock, full_path, pnetfid, presp_data, poplock, full_path,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) if (rc)
...@@ -244,7 +243,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -244,7 +243,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
/* get new inode and set it up */ /* get new inode and set it up */
if (*pinode == NULL) { if (*pinode == NULL) {
*pinode = cifs_iget(sb, &fattr); *pinode = cifs_iget(mnt->mnt_sb, &fattr);
if (!*pinode) { if (!*pinode) {
rc = -ENOMEM; rc = -ENOMEM;
goto posix_open_ret; goto posix_open_ret;
...@@ -253,7 +252,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -253,7 +252,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
cifs_fattr_to_inode(*pinode, &fattr); cifs_fattr_to_inode(*pinode, &fattr);
} }
cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only); cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags);
posix_open_ret: posix_open_ret:
kfree(presp_data); kfree(presp_data);
...@@ -280,7 +279,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -280,7 +279,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
int rc = -ENOENT; int rc = -ENOENT;
int xid; int xid;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
int oplock = 0; __u32 oplock = 0;
int oflags; int oflags;
bool posix_create = false; bool posix_create = false;
/* /*
...@@ -298,7 +297,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -298,7 +297,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
FILE_ALL_INFO *buf = NULL; FILE_ALL_INFO *buf = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
int disposition = FILE_OVERWRITE_IF; int disposition = FILE_OVERWRITE_IF;
bool write_only = false;
xid = GetXid(); xid = GetXid();
...@@ -323,7 +321,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -323,7 +321,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP & (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
rc = cifs_posix_open(full_path, &newinode, inode->i_sb, rc = cifs_posix_open(full_path, &newinode, nd->path.mnt,
mode, oflags, &oplock, &fileHandle, xid); mode, oflags, &oplock, &fileHandle, xid);
/* EIO could indicate that (posix open) operation is not /* EIO could indicate that (posix open) operation is not
supported, despite what server claimed in capability supported, despite what server claimed in capability
...@@ -351,11 +349,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -351,11 +349,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
desiredAccess = 0; desiredAccess = 0;
if (oflags & FMODE_READ) if (oflags & FMODE_READ)
desiredAccess |= GENERIC_READ; /* is this too little? */ desiredAccess |= GENERIC_READ; /* is this too little? */
if (oflags & FMODE_WRITE) { if (oflags & FMODE_WRITE)
desiredAccess |= GENERIC_WRITE; desiredAccess |= GENERIC_WRITE;
if (!(oflags & FMODE_READ))
write_only = true;
}
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
disposition = FILE_CREATE; disposition = FILE_CREATE;
...@@ -470,8 +465,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -470,8 +465,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
/* mknod case - do not leave file open */ /* mknod case - do not leave file open */
CIFSSMBClose(xid, tcon, fileHandle); CIFSSMBClose(xid, tcon, fileHandle);
} else if (!(posix_create) && (newinode)) { } else if (!(posix_create) && (newinode)) {
cifs_fill_fileinfo(newinode, fileHandle, cifs_new_fileinfo(newinode, fileHandle, NULL,
cifs_sb->tcon, write_only); nd->path.mnt, oflags);
} }
cifs_create_out: cifs_create_out:
kfree(buf); kfree(buf);
...@@ -611,7 +606,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -611,7 +606,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
{ {
int xid; int xid;
int rc = 0; /* to get around spurious gcc warning, set to zero here */ int rc = 0; /* to get around spurious gcc warning, set to zero here */
int oplock = 0; __u32 oplock = 0;
__u16 fileHandle = 0; __u16 fileHandle = 0;
bool posix_open = false; bool posix_open = false;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
...@@ -683,8 +678,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -683,8 +678,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
(nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
(nd->intent.open.flags & O_CREAT)) { (nd->intent.open.flags & O_CREAT)) {
rc = cifs_posix_open(full_path, &newInode, rc = cifs_posix_open(full_path, &newInode, nd->path.mnt,
parent_dir_inode->i_sb,
nd->intent.open.create_mode, nd->intent.open.create_mode,
nd->intent.open.flags, &oplock, nd->intent.open.flags, &oplock,
&fileHandle, xid); &fileHandle, xid);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/task_io_accounting_ops.h> #include <linux/task_io_accounting_ops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mount.h>
#include <asm/div64.h> #include <asm/div64.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifspdu.h" #include "cifspdu.h"
...@@ -39,27 +40,6 @@ ...@@ -39,27 +40,6 @@
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
static inline struct cifsFileInfo *cifs_init_private(
struct cifsFileInfo *private_data, struct inode *inode,
struct file *file, __u16 netfid)
{
memset(private_data, 0, sizeof(struct cifsFileInfo));
private_data->netfid = netfid;
private_data->pid = current->tgid;
mutex_init(&private_data->fh_mutex);
mutex_init(&private_data->lock_mutex);
INIT_LIST_HEAD(&private_data->llist);
private_data->pfile = file; /* needed for writepage */
private_data->pInode = inode;
private_data->invalidHandle = false;
private_data->closePend = false;
/* Initialize reference count to one. The private data is
freed on the release of the last reference */
atomic_set(&private_data->count, 1);
return private_data;
}
static inline int cifs_convert_flags(unsigned int flags) static inline int cifs_convert_flags(unsigned int flags)
{ {
if ((flags & O_ACCMODE) == O_RDONLY) if ((flags & O_ACCMODE) == O_RDONLY)
...@@ -123,9 +103,11 @@ static inline int cifs_get_disposition(unsigned int flags) ...@@ -123,9 +103,11 @@ static inline int cifs_get_disposition(unsigned int flags)
} }
/* all arguments to this function must be checked for validity in caller */ /* all arguments to this function must be checked for validity in caller */
static inline int cifs_posix_open_inode_helper(struct inode *inode, static inline int
struct file *file, struct cifsInodeInfo *pCifsInode, cifs_posix_open_inode_helper(struct inode *inode, struct file *file,
struct cifsFileInfo *pCifsFile, int oplock, u16 netfid) struct cifsInodeInfo *pCifsInode,
struct cifsFileInfo *pCifsFile, __u32 oplock,
u16 netfid)
{ {
write_lock(&GlobalSMBSeslock); write_lock(&GlobalSMBSeslock);
...@@ -219,17 +201,6 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, ...@@ -219,17 +201,6 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
struct timespec temp; struct timespec temp;
int rc; int rc;
/* want handles we can use to read with first
in the list so we do not have to walk the
list to search for one in write_begin */
if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
list_add_tail(&pCifsFile->flist,
&pCifsInode->openFileList);
} else {
list_add(&pCifsFile->flist,
&pCifsInode->openFileList);
}
write_unlock(&GlobalSMBSeslock);
if (pCifsInode->clientCanCacheRead) { if (pCifsInode->clientCanCacheRead) {
/* we have the inode open somewhere else /* we have the inode open somewhere else
no need to discard cache data */ no need to discard cache data */
...@@ -279,7 +250,8 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, ...@@ -279,7 +250,8 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
int cifs_open(struct inode *inode, struct file *file) int cifs_open(struct inode *inode, struct file *file)
{ {
int rc = -EACCES; int rc = -EACCES;
int xid, oplock; int xid;
__u32 oplock;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsFileInfo *pCifsFile; struct cifsFileInfo *pCifsFile;
...@@ -324,7 +296,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -324,7 +296,7 @@ int cifs_open(struct inode *inode, struct file *file)
le64_to_cpu(tcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
int oflags = (int) cifs_posix_convert_flags(file->f_flags); int oflags = (int) cifs_posix_convert_flags(file->f_flags);
/* can not refresh inode info since size could be stale */ /* can not refresh inode info since size could be stale */
rc = cifs_posix_open(full_path, &inode, inode->i_sb, rc = cifs_posix_open(full_path, &inode, file->f_path.mnt,
cifs_sb->mnt_file_mode /* ignored */, cifs_sb->mnt_file_mode /* ignored */,
oflags, &oplock, &netfid, xid); oflags, &oplock, &netfid, xid);
if (rc == 0) { if (rc == 0) {
...@@ -414,24 +386,17 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -414,24 +386,17 @@ int cifs_open(struct inode *inode, struct file *file)
cFYI(1, ("cifs_open returned 0x%x", rc)); cFYI(1, ("cifs_open returned 0x%x", rc));
goto out; goto out;
} }
file->private_data =
kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt,
file->f_flags);
file->private_data = pCifsFile;
if (file->private_data == NULL) { if (file->private_data == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
} }
pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &tcon->openFileList);
pCifsInode = CIFS_I(file->f_path.dentry->d_inode); rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, tcon,
if (pCifsInode) { &oplock, buf, full_path, xid);
rc = cifs_open_inode_helper(inode, file, pCifsInode,
pCifsFile, tcon,
&oplock, buf, full_path, xid);
} else {
write_unlock(&GlobalSMBSeslock);
}
if (oplock & CIFS_CREATE_ACTION) { if (oplock & CIFS_CREATE_ACTION) {
/* time to set mode which we can not set earlier due to /* time to set mode which we can not set earlier due to
...@@ -474,7 +439,8 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile) ...@@ -474,7 +439,8 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile)
static int cifs_reopen_file(struct file *file, bool can_flush) static int cifs_reopen_file(struct file *file, bool can_flush)
{ {
int rc = -EACCES; int rc = -EACCES;
int xid, oplock; int xid;
__u32 oplock;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsFileInfo *pCifsFile; struct cifsFileInfo *pCifsFile;
...@@ -543,7 +509,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -543,7 +509,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
le64_to_cpu(tcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
int oflags = (int) cifs_posix_convert_flags(file->f_flags); int oflags = (int) cifs_posix_convert_flags(file->f_flags);
/* can not refresh inode info since size could be stale */ /* can not refresh inode info since size could be stale */
rc = cifs_posix_open(full_path, NULL, inode->i_sb, rc = cifs_posix_open(full_path, NULL, file->f_path.mnt,
cifs_sb->mnt_file_mode /* ignored */, cifs_sb->mnt_file_mode /* ignored */,
oflags, &oplock, &netfid, xid); oflags, &oplock, &netfid, xid);
if (rc == 0) { if (rc == 0) {
...@@ -2308,6 +2274,73 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping, ...@@ -2308,6 +2274,73 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
return rc; return rc;
} }
static void
cifs_oplock_break(struct slow_work *work)
{
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
oplock_break);
struct inode *inode = cfile->pInode;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->mnt->mnt_sb);
int rc, waitrc = 0;
if (inode && S_ISREG(inode->i_mode)) {
#ifdef CONFIG_CIFS_EXPERIMENTAL
if (cinode->clientCanCacheAll == 0)
break_lease(inode, FMODE_READ);
else if (cinode->clientCanCacheRead == 0)
break_lease(inode, FMODE_WRITE);
#endif
rc = filemap_fdatawrite(inode->i_mapping);
if (cinode->clientCanCacheRead == 0) {
waitrc = filemap_fdatawait(inode->i_mapping);
invalidate_remote_inode(inode);
}
if (!rc)
rc = waitrc;
if (rc)
cinode->write_behind_rc = rc;
cFYI(1, ("Oplock flush inode %p rc %d", inode, rc));
}
/*
* releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do
* not bother sending an oplock release if session to server still is
* disconnected since oplock already released by the server
*/
if (!cfile->closePend && !cfile->oplock_break_cancelled) {
rc = CIFSSMBLock(0, cifs_sb->tcon, cfile->netfid, 0, 0, 0, 0,
LOCKING_ANDX_OPLOCK_RELEASE, false);
cFYI(1, ("Oplock release rc = %d", rc));
}
}
static int
cifs_oplock_break_get(struct slow_work *work)
{
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
oplock_break);
mntget(cfile->mnt);
cifsFileInfo_get(cfile);
return 0;
}
static void
cifs_oplock_break_put(struct slow_work *work)
{
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
oplock_break);
mntput(cfile->mnt);
cifsFileInfo_put(cfile);
}
const struct slow_work_ops cifs_oplock_break_ops = {
.get_ref = cifs_oplock_break_get,
.put_ref = cifs_oplock_break_put,
.execute = cifs_oplock_break,
};
const struct address_space_operations cifs_addr_ops = { const struct address_space_operations cifs_addr_ops = {
.readpage = cifs_readpage, .readpage = cifs_readpage,
.readpages = cifs_readpages, .readpages = cifs_readpages,
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_req_poolp;
extern struct task_struct *oplockThread;
/* The xid serves as a useful identifier for each incoming vfs request, /* The xid serves as a useful identifier for each incoming vfs request,
in a similar way to the mid which is useful to track each sent smb, 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,6 +499,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsInodeInfo *pCifsInode; struct cifsInodeInfo *pCifsInode;
struct cifsFileInfo *netfile; struct cifsFileInfo *netfile;
int rc;
cFYI(1, ("Checking for oplock break or dnotify response")); cFYI(1, ("Checking for oplock break or dnotify response"));
if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
...@@ -562,30 +562,40 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -562,30 +562,40 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
continue; continue;
cifs_stats_inc(&tcon->num_oplock_brks); cifs_stats_inc(&tcon->num_oplock_brks);
write_lock(&GlobalSMBSeslock); read_lock(&GlobalSMBSeslock);
list_for_each(tmp2, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp2, struct cifsFileInfo, netfile = list_entry(tmp2, struct cifsFileInfo,
tlist); tlist);
if (pSMB->Fid != netfile->netfid) if (pSMB->Fid != netfile->netfid)
continue; continue;
write_unlock(&GlobalSMBSeslock); /*
read_unlock(&cifs_tcp_ses_lock); * don't do anything if file is about to be
* closed anyway.
*/
if (netfile->closePend) {
read_unlock(&GlobalSMBSeslock);
read_unlock(&cifs_tcp_ses_lock);
return true;
}
cFYI(1, ("file id match, oplock break")); cFYI(1, ("file id match, oplock break"));
pCifsInode = CIFS_I(netfile->pInode); pCifsInode = CIFS_I(netfile->pInode);
pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheAll = false;
if (pSMB->OplockLevel == 0) if (pSMB->OplockLevel == 0)
pCifsInode->clientCanCacheRead = false; pCifsInode->clientCanCacheRead = false;
pCifsInode->oplockPending = true; rc = slow_work_enqueue(&netfile->oplock_break);
AllocOplockQEntry(netfile->pInode, if (rc) {
netfile->netfid, tcon); cERROR(1, ("failed to enqueue oplock "
cFYI(1, ("about to wake up oplock thread")); "break: %d\n", rc));
if (oplockThread) } else {
wake_up_process(oplockThread); netfile->oplock_break_cancelled = false;
}
read_unlock(&GlobalSMBSeslock);
read_unlock(&cifs_tcp_ses_lock);
return true; return true;
} }
write_unlock(&GlobalSMBSeslock); read_unlock(&GlobalSMBSeslock);
read_unlock(&cifs_tcp_ses_lock); read_unlock(&cifs_tcp_ses_lock);
cFYI(1, ("No matching file for oplock break")); cFYI(1, ("No matching file for oplock break"));
return true; return true;
......
...@@ -146,7 +146,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) ...@@ -146,7 +146,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
} }
} }
void static void
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
...@@ -161,7 +161,7 @@ cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, ...@@ -161,7 +161,7 @@ cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
cifs_fill_common_info(fattr, cifs_sb); cifs_fill_common_info(fattr, cifs_sb);
} }
void static void
cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
......
...@@ -103,56 +103,6 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) ...@@ -103,56 +103,6 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
mempool_free(midEntry, cifs_mid_poolp); mempool_free(midEntry, cifs_mid_poolp);
} }
struct oplock_q_entry *
AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon)
{
struct oplock_q_entry *temp;
if ((pinode == NULL) || (tcon == NULL)) {
cERROR(1, ("Null parms passed to AllocOplockQEntry"));
return NULL;
}
temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
GFP_KERNEL);
if (temp == NULL)
return temp;
else {
temp->pinode = pinode;
temp->tcon = tcon;
temp->netfid = fid;
spin_lock(&cifs_oplock_lock);
list_add_tail(&temp->qhead, &cifs_oplock_list);
spin_unlock(&cifs_oplock_lock);
}
return temp;
}
void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry)
{
spin_lock(&cifs_oplock_lock);
/* should we check if list empty first? */
list_del(&oplockEntry->qhead);
spin_unlock(&cifs_oplock_lock);
kmem_cache_free(cifs_oplock_cachep, oplockEntry);
}
void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
{
struct oplock_q_entry *temp;
if (tcon == NULL)
return;
spin_lock(&cifs_oplock_lock);
list_for_each_entry(temp, &cifs_oplock_list, qhead) {
if ((temp->tcon) && (temp->tcon == tcon)) {
list_del(&temp->qhead);
kmem_cache_free(cifs_oplock_cachep, temp);
}
}
spin_unlock(&cifs_oplock_lock);
}
static int static int
smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment