Commit edc2bedb authored by Pavel Shilovsky's avatar Pavel Shilovsky

Add strict cache mode for 2.6.32

parent c8cd2ff8
...@@ -433,6 +433,11 @@ A partial list of the supported mount options follows: ...@@ -433,6 +433,11 @@ A partial list of the supported mount options follows:
if oplock (caching token) is granted and held. Note that if oplock (caching token) is granted and held. Note that
direct allows write operations larger than page size direct allows write operations larger than page size
to be sent to the server. to be sent to the server.
strictcache Use for switching on strict cache mode. In this mode the
client reads from the cache all the time it has Oplock Level II,
otherwise - reads from the server. All written data are stored
in the cache, but if the client doesn't have Exclusive Oplock,
it writes the data to the server.
acl Allow setfacl and getfacl to manage posix ACLs if server acl Allow setfacl and getfacl to manage posix ACLs if server
supports them. (default) supports them. (default)
noacl Do not allow setfacl and getfacl calls on this mount noacl Do not allow setfacl and getfacl calls on this mount
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ #define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */
#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ #define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/
#define CIFS_MOUNT_WINE_MODE 0x8000 /* use pid forwarding for wine apps */ #define CIFS_MOUNT_WINE_MODE 0x8000 /* use pid forwarding for wine apps */
#define CIFS_MOUNT_STRICT_IO 0x10000 /* strict cache mode */
struct cifs_sb_info { struct cifs_sb_info {
struct cifsTconInfo *tcon; /* primary mount */ struct cifsTconInfo *tcon; /* primary mount */
......
...@@ -309,8 +309,7 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -309,8 +309,7 @@ cifs_alloc_inode(struct super_block *sb)
/* Until the file is open and we have gotten oplock /* Until the file is open and we have gotten oplock
info back from the server, can not assume caching of info back from the server, can not assume caching of
file data or metadata */ file data or metadata */
cifs_inode->clientCanCacheRead = false; cifs_set_oplock_level(cifs_inode, 0);
cifs_inode->clientCanCacheAll = false;
cifs_inode->delete_pending = false; cifs_inode->delete_pending = false;
cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
cifs_inode->server_eof = 0; cifs_inode->server_eof = 0;
...@@ -628,10 +627,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -628,10 +627,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
{ {
struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
ssize_t written; ssize_t written;
int rc;
written = generic_file_aio_write(iocb, iov, nr_segs, pos); written = generic_file_aio_write(iocb, iov, nr_segs, pos);
if (!CIFS_I(inode)->clientCanCacheAll)
filemap_fdatawrite(inode->i_mapping); if (CIFS_I(inode)->clientCanCacheAll)
return written;
rc = filemap_fdatawrite(inode->i_mapping);
if (rc)
cFYI(1, ("cifs_file_aio_write: %d rc on %p inode", rc, inode));
return written; return written;
} }
...@@ -765,6 +771,25 @@ const struct file_operations cifs_file_ops = { ...@@ -765,6 +771,25 @@ const struct file_operations cifs_file_ops = {
#endif /* CONFIG_CIFS_EXPERIMENTAL */ #endif /* CONFIG_CIFS_EXPERIMENTAL */
}; };
const struct file_operations cifs_file_strict_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = cifs_strict_writev,
.open = cifs_open,
.release = cifs_close,
.lock = cifs_lock,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
.mmap = cifs_file_strict_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_ops = { const struct file_operations cifs_file_direct_ops = {
/* no aio, no readv - /* no aio, no readv -
BB reevaluate whether they can be done with directio, no cache */ BB reevaluate whether they can be done with directio, no cache */
...@@ -785,6 +810,7 @@ const struct file_operations cifs_file_direct_ops = { ...@@ -785,6 +810,7 @@ const struct file_operations cifs_file_direct_ops = {
.setlease = cifs_setlease, .setlease = cifs_setlease,
#endif /* CONFIG_CIFS_EXPERIMENTAL */ #endif /* CONFIG_CIFS_EXPERIMENTAL */
}; };
const struct file_operations cifs_file_nobrl_ops = { const struct file_operations cifs_file_nobrl_ops = {
.read = do_sync_read, .read = do_sync_read,
.write = do_sync_write, .write = do_sync_write,
...@@ -806,6 +832,24 @@ const struct file_operations cifs_file_nobrl_ops = { ...@@ -806,6 +832,24 @@ const struct file_operations cifs_file_nobrl_ops = {
#endif /* CONFIG_CIFS_EXPERIMENTAL */ #endif /* CONFIG_CIFS_EXPERIMENTAL */
}; };
const struct file_operations cifs_file_strict_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = cifs_strict_writev,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
.mmap = cifs_file_strict_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_nobrl_ops = { const struct file_operations cifs_file_direct_nobrl_ops = {
/* no aio, no readv - /* no aio, no readv -
BB reevaluate whether they can be done with directio, no cache */ BB reevaluate whether they can be done with directio, no cache */
......
...@@ -62,6 +62,7 @@ extern int cifs_rmdir(struct inode *, struct dentry *); ...@@ -62,6 +62,7 @@ extern int cifs_rmdir(struct inode *, struct dentry *);
extern int cifs_rename(struct inode *, struct dentry *, struct inode *, extern int cifs_rename(struct inode *, struct dentry *, struct inode *,
struct dentry *); struct dentry *);
extern int cifs_revalidate(struct dentry *); extern int cifs_revalidate(struct dentry *);
extern void cifs_invalidate_mapping(struct inode *inode);
extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int cifs_setattr(struct dentry *, struct iattr *); extern int cifs_setattr(struct dentry *, struct iattr *);
...@@ -73,19 +74,27 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations; ...@@ -73,19 +74,27 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations;
/* Functions related to files and directories */ /* Functions related to files and directories */
extern const struct file_operations cifs_file_ops; extern const struct file_operations cifs_file_ops;
extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */
extern const struct file_operations cifs_file_nobrl_ops; extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */
extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */ extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */
extern const struct file_operations cifs_file_direct_nobrl_ops;
extern const struct file_operations cifs_file_strict_nobrl_ops;
extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_open(struct inode *inode, struct file *file);
extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file);
extern int cifs_closedir(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file);
extern ssize_t cifs_user_read(struct file *file, char __user *read_data, extern ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset); size_t read_size, loff_t *poffset);
extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
size_t write_size, loff_t *poffset); size_t write_size, loff_t *poffset);
extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, struct dentry *, int); extern int cifs_fsync(struct file *, struct dentry *, int);
extern int cifs_strict_fsync(struct file *, struct dentry *, int);
extern int cifs_flush(struct file *, fl_owner_t id); extern int cifs_flush(struct file *, fl_owner_t id);
extern int cifs_file_mmap(struct file * , struct vm_area_struct *); extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);
extern const struct file_operations cifs_dir_ops; extern const struct file_operations cifs_dir_ops;
extern int cifs_dir_open(struct inode *inode, struct file *file); extern int cifs_dir_open(struct inode *inode, struct file *file);
extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
......
...@@ -388,6 +388,7 @@ struct cifsInodeInfo { ...@@ -388,6 +388,7 @@ struct cifsInodeInfo {
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 delete_pending:1; /* DELETE_ON_CLOSE is set */ bool delete_pending:1; /* DELETE_ON_CLOSE is set */
bool invalid_mapping:1; /* uptodate cache flag */
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 */
struct inode vfs_inode; struct inode vfs_inode;
......
...@@ -70,6 +70,8 @@ extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); ...@@ -70,6 +70,8 @@ extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *);
#endif #endif
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written);
extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize(struct smb_hdr *ptr);
extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
extern int decode_negTokenInit(unsigned char *security_blob, int length, extern int decode_negTokenInit(unsigned char *security_blob, int length,
...@@ -90,10 +92,12 @@ extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); ...@@ -90,10 +92,12 @@ 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 void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode,
__u16 fileHandle, struct file *file, __u16 fileHandle, struct file *file,
struct vfsmount *mnt, unsigned int oflags); struct vfsmount *mnt, unsigned int oflags,
__u32 oplock);
extern int cifs_posix_open(char *full_path, struct inode **pinode, extern int cifs_posix_open(char *full_path, struct inode **pinode,
struct vfsmount *mnt, struct vfsmount *mnt,
struct super_block *sb, struct super_block *sb,
......
...@@ -82,6 +82,7 @@ struct smb_vol { ...@@ -82,6 +82,7 @@ struct smb_vol {
bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
bool server_ino:1; /* use inode numbers from server ie UniqueId */ bool server_ino:1; /* use inode numbers from server ie UniqueId */
bool direct_io:1; bool direct_io:1;
bool strict_io:1; /* strict cache behavior */
bool remap:1; /* set to remap seven reserved chars in filenames */ bool remap:1; /* set to remap seven reserved chars in filenames */
bool posix_paths:1; /* unset to not ask for posix pathnames. */ bool posix_paths:1; /* unset to not ask for posix pathnames. */
bool no_linux_ext:1; bool no_linux_ext:1;
...@@ -1303,7 +1304,7 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1303,7 +1304,7 @@ cifs_parse_mount_options(char *options, const char *devname,
} else if (strnicmp(data, "wine", 4) == 0) { } else if (strnicmp(data, "wine", 4) == 0) {
vol->wine_mode = 1; vol->wine_mode = 1;
vol->mand_lock = 1; vol->mand_lock = 1;
vol->direct_io = 1; vol->strict_io = 1;
} else if (strnicmp(data, "cifsacl", 7) == 0) { } else if (strnicmp(data, "cifsacl", 7) == 0) {
vol->cifs_acl = 1; vol->cifs_acl = 1;
} else if (strnicmp(data, "nocifsacl", 9) == 0) { } else if (strnicmp(data, "nocifsacl", 9) == 0) {
...@@ -1328,6 +1329,8 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1328,6 +1329,8 @@ cifs_parse_mount_options(char *options, const char *devname,
vol->direct_io = 1; vol->direct_io = 1;
} else if (strnicmp(data, "forcedirectio", 13) == 0) { } else if (strnicmp(data, "forcedirectio", 13) == 0) {
vol->direct_io = 1; vol->direct_io = 1;
} else if (strnicmp(data, "strictcache", 11) == 0) {
vol->strict_io = 1;
} else if (strnicmp(data, "noac", 4) == 0) { } else if (strnicmp(data, "noac", 4) == 0) {
printk(KERN_WARNING "CIFS: Mount option noac not " printk(KERN_WARNING "CIFS: Mount option noac not "
"supported. Instead set " "supported. Instead set "
...@@ -2191,6 +2194,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, ...@@ -2191,6 +2194,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
if (pvolume_info->dynperm) if (pvolume_info->dynperm)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
if (pvolume_info->strict_io)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO;
if (pvolume_info->direct_io) { if (pvolume_info->direct_io) {
cFYI(1, ("mounting share using direct i/o")); cFYI(1, ("mounting share using direct i/o"));
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
......
...@@ -132,9 +132,9 @@ cifs_bp_rename_retry: ...@@ -132,9 +132,9 @@ cifs_bp_rename_retry:
struct cifsFileInfo * struct cifsFileInfo *
cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
struct file *file, struct vfsmount *mnt, unsigned int oflags) struct file *file, struct vfsmount *mnt, unsigned int oflags,
__u32 oplock)
{ {
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); struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
...@@ -143,9 +143,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, ...@@ -143,9 +143,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
if (pCifsFile == NULL) if (pCifsFile == NULL)
return pCifsFile; return pCifsFile;
if (oplockEnabled)
oplock = REQ_OPLOCK;
pCifsFile->netfid = fileHandle; pCifsFile->netfid = fileHandle;
pCifsFile->pid = current->tgid; pCifsFile->pid = current->tgid;
pCifsFile->pInode = igrab(newinode); pCifsFile->pInode = igrab(newinode);
...@@ -159,9 +156,12 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, ...@@ -159,9 +156,12 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
atomic_set(&pCifsFile->count, 1); atomic_set(&pCifsFile->count, 1);
slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops); slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
pCifsInode = CIFS_I(newinode);
if (pCifsInode)
cifs_set_oplock_level(pCifsInode, oplock);
write_lock(&GlobalSMBSeslock); write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList); list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
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 (oflags & FMODE_READ) if (oflags & FMODE_READ)
...@@ -169,13 +169,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, ...@@ -169,13 +169,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
else else
list_add_tail(&pCifsFile->flist, list_add_tail(&pCifsFile->flist,
&pCifsInode->openFileList); &pCifsInode->openFileList);
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock inode %p", newinode));
} else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
} }
write_unlock(&GlobalSMBSeslock); write_unlock(&GlobalSMBSeslock);
...@@ -252,7 +245,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode, ...@@ -252,7 +245,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
} }
if (mnt) if (mnt)
cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags); cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags,
*poplock);
posix_open_ret: posix_open_ret:
kfree(presp_data); kfree(presp_data);
...@@ -468,7 +462,7 @@ cifs_create_set_dentry: ...@@ -468,7 +462,7 @@ cifs_create_set_dentry:
CIFSSMBClose(xid, tcon, fileHandle); CIFSSMBClose(xid, tcon, fileHandle);
} else if (!(posix_create) && (newinode)) { } else if (!(posix_create) && (newinode)) {
cifs_new_fileinfo(newinode, fileHandle, NULL, cifs_new_fileinfo(newinode, fileHandle, NULL,
nd->path.mnt, oflags); nd->path.mnt, oflags, oplock);
} }
cifs_create_out: cifs_create_out:
kfree(buf); kfree(buf);
......
...@@ -151,13 +151,7 @@ cifs_posix_open_inode_helper(struct inode *inode, struct file *file, ...@@ -151,13 +151,7 @@ cifs_posix_open_inode_helper(struct inode *inode, struct file *file,
} */ } */
psx_client_can_cache: psx_client_can_cache:
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { cifs_set_oplock_level(pCifsInode, oplock);
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock granted on inode %p",
file->f_path.dentry->d_inode));
} else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
/* will have to change the unlock if we reenable the /* will have to change the unlock if we reenable the
filemap_fdatawrite (which does not seem necessary */ filemap_fdatawrite (which does not seem necessary */
...@@ -200,7 +194,7 @@ cifs_fill_filedata(struct file *file) ...@@ -200,7 +194,7 @@ cifs_fill_filedata(struct file *file)
/* 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_open_inode_helper(struct inode *inode, struct file *file, static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile, struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile,
struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf, struct cifsTconInfo *pTcon, FILE_ALL_INFO *buf,
char *full_path, int xid) char *full_path, int xid)
{ {
struct timespec temp; struct timespec temp;
...@@ -241,14 +235,6 @@ client_can_cache: ...@@ -241,14 +235,6 @@ client_can_cache:
rc = cifs_get_inode_info(&file->f_path.dentry->d_inode, rc = cifs_get_inode_info(&file->f_path.dentry->d_inode,
full_path, buf, inode->i_sb, xid, NULL); full_path, buf, inode->i_sb, xid, NULL);
if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock granted on inode %p",
file->f_path.dentry->d_inode));
} else if ((*oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
return rc; return rc;
} }
...@@ -398,7 +384,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -398,7 +384,7 @@ int cifs_open(struct inode *inode, struct file *file)
} }
pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt,
file->f_flags); file->f_flags, oplock);
file->private_data = pCifsFile; file->private_data = pCifsFile;
if (file->private_data == NULL) { if (file->private_data == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -406,7 +392,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -406,7 +392,7 @@ int cifs_open(struct inode *inode, struct file *file)
} }
rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, tcon, rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, tcon,
&oplock, buf, full_path, xid); buf, full_path, xid);
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
...@@ -563,8 +549,7 @@ reopen_success: ...@@ -563,8 +549,7 @@ reopen_success:
CIFS_I(inode)->write_behind_rc = rc; CIFS_I(inode)->write_behind_rc = rc;
/* temporarily disable caching while we /* temporarily disable caching while we
go to server to get inode info */ go to server to get inode info */
pCifsInode->clientCanCacheAll = false; cifs_set_oplock_level(pCifsInode, 0);
pCifsInode->clientCanCacheRead = false;
if (tcon->unix_ext) if (tcon->unix_ext)
rc = cifs_get_inode_info_unix(&inode, rc = cifs_get_inode_info_unix(&inode,
full_path, inode->i_sb, xid); full_path, inode->i_sb, xid);
...@@ -578,18 +563,7 @@ reopen_success: ...@@ -578,18 +563,7 @@ reopen_success:
invalidate the current end of file on the server invalidate the current end of file on the server
we can not go to the server to get the new inod we can not go to the server to get the new inod
info */ info */
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { cifs_set_oplock_level(pCifsInode, oplock);
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock granted on inode %p",
file->f_path.dentry->d_inode));
} else if ((oplock & 0xF) == OPLOCK_READ) {
pCifsInode->clientCanCacheRead = true;
pCifsInode->clientCanCacheAll = false;
} else {
pCifsInode->clientCanCacheRead = false;
pCifsInode->clientCanCacheAll = false;
}
cifs_relock_file(pCifsFile); cifs_relock_file(pCifsFile);
} }
} }
...@@ -665,10 +639,14 @@ int cifs_close(struct inode *inode, struct file *file) ...@@ -665,10 +639,14 @@ int cifs_close(struct inode *inode, struct file *file)
read_lock(&GlobalSMBSeslock); read_lock(&GlobalSMBSeslock);
if (list_empty(&(CIFS_I(inode)->openFileList))) { if (list_empty(&(CIFS_I(inode)->openFileList))) {
cFYI(1, ("closing last open instance for inode %p", inode)); cFYI(1, ("closing last open instance for inode %p", inode));
/* in strict cache mode we need invalidate mapping on the last
close because it may cause a error when we open this file
again and get at least level II oplock */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
CIFS_I(inode)->invalid_mapping = true;
/* if the file is not open we do not know if we can cache info /* if the file is not open we do not know if we can cache info
on this inode, much less write behind and read ahead */ on this inode, much less write behind and read ahead */
CIFS_I(inode)->clientCanCacheRead = false; cifs_set_oplock_level(CIFS_I(inode), 0);
CIFS_I(inode)->clientCanCacheAll = false;
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&GlobalSMBSeslock);
if ((rc == 0) && CIFS_I(inode)->write_behind_rc) if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
...@@ -974,7 +952,7 @@ cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) ...@@ -974,7 +952,7 @@ cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset)
} }
/* update the file size (if needed) after a write */ /* update the file size (if needed) after a write */
static void void
cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written) unsigned int bytes_written)
{ {
...@@ -1545,6 +1523,7 @@ retry: ...@@ -1545,6 +1523,7 @@ retry:
break; break;
} }
if (n_iov) { if (n_iov) {
retry_write:
/* Search for a writable handle every time we call /* Search for a writable handle every time we call
* CIFSSMBWrite2. We can't rely on the last handle * CIFSSMBWrite2. We can't rely on the last handle
* we used to still be valid * we used to still be valid
...@@ -1566,36 +1545,55 @@ retry: ...@@ -1566,36 +1545,55 @@ retry:
&bytes_written, iov, n_iov, &bytes_written, iov, n_iov,
long_op); long_op);
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
cifs_update_eof(cifsi, offset, bytes_written); }
if (rc || bytes_written < bytes_to_write) { cFYI(1, ("Write2 rc=%d, wrote=%u", rc, bytes_written));
cERROR(1, ("Write2 ret %d, wrote %d",
rc, bytes_written)); /*
/* BB what if continued retry is * For now, treat a short write as if nothing got
requested via mount flags? */ * written. A zero length write however indicates
if (rc == -ENOSPC) * ENOSPC or EFBIG. We have no way to know which
set_bit(AS_ENOSPC, &mapping->flags); * though, so call it ENOSPC for now. EFBIG would
else * get translated to AS_EIO anyway.
set_bit(AS_EIO, &mapping->flags); *
} else { * FIXME: make it take into account the data that did
* get written
*/
if (rc == 0) {
if (bytes_written == 0)
rc = -ENOSPC;
else if (bytes_written < bytes_to_write)
rc = -EAGAIN;
}
/* retry on data-integrity flush */
if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN)
goto retry_write;
/* fix the stats and EOF */
if (bytes_written > 0) {
cifs_stats_bytes_written(cifs_sb->tcon, cifs_stats_bytes_written(cifs_sb->tcon,
bytes_written); bytes_written);
} cifs_update_eof(cifsi, offset, bytes_written);
} }
for (i = 0; i < n_iov; i++) { for (i = 0; i < n_iov; i++) {
page = pvec.pages[first + i]; page = pvec.pages[first + i];
/* Should we also set page error on /* on retryable write error, redirty page */
success rc but too little data written? */ if (rc == -EAGAIN)
/* BB investigate retry logic on temporary redirty_page_for_writepage(wbc, page);
server crash cases and how recovery works else if (rc != 0)
when page marked as error */
if (rc)
SetPageError(page); SetPageError(page);
kunmap(page); kunmap(page);
unlock_page(page); unlock_page(page);
end_page_writeback(page); end_page_writeback(page);
page_cache_release(page); page_cache_release(page);
} }
if (rc != -EAGAIN)
mapping_set_error(mapping, rc);
else
rc = 0;
if ((wbc->nr_to_write -= n_iov) <= 0) if ((wbc->nr_to_write -= n_iov) <= 0)
done = 1; done = 1;
index = next; index = next;
...@@ -1706,7 +1704,7 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, ...@@ -1706,7 +1704,7 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
return rc; return rc;
} }
int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) int cifs_strict_fsync(struct file *file, struct dentry *dentry, int datasync)
{ {
int xid; int xid;
int rc = 0; int rc = 0;
...@@ -1714,21 +1712,40 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) ...@@ -1714,21 +1712,40 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
struct cifsFileInfo *smbfile = struct cifsFileInfo *smbfile =
(struct cifsFileInfo *)file->private_data; (struct cifsFileInfo *)file->private_data;
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
xid = GetXid(); xid = GetXid();
cFYI(1, ("Sync file - name: %s datasync: 0x%x", cFYI(1, ("Sync file - name: %s datasync: 0x%x",
dentry->d_name.name, datasync)); dentry->d_name.name, datasync));
rc = filemap_write_and_wait(inode->i_mapping); if (!CIFS_I(inode)->clientCanCacheRead)
if (rc == 0) { cifs_invalidate_mapping(inode);
rc = CIFS_I(inode)->write_behind_rc;
CIFS_I(inode)->write_behind_rc = 0; tcon = cifs_sb->tcon;
tcon = CIFS_SB(inode->i_sb)->tcon; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
if (!rc && tcon && smbfile && rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
!(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
FreeXid(xid);
return rc;
}
int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
{
int xid;
int rc = 0;
struct cifsTconInfo *tcon;
struct cifsFileInfo *smbfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
xid = GetXid();
cFYI(1, ("Sync file - name: %s datasync: 0x%x",
dentry->d_name.name, datasync));
tcon = cifs_sb->tcon;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
}
FreeXid(xid); FreeXid(xid);
return rc; return rc;
...@@ -1792,21 +1809,247 @@ int cifs_flush(struct file *file, fl_owner_t id) ...@@ -1792,21 +1809,247 @@ int cifs_flush(struct file *file, fl_owner_t id)
return rc; return rc;
} }
ssize_t cifs_user_read(struct file *file, char __user *read_data, static int
size_t read_size, loff_t *poffset) cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
{ {
int rc = -EACCES; int rc = 0;
unsigned long i;
for (i = 0; i < num_pages; i++) {
pages[i] = alloc_page(__GFP_HIGHMEM);
if (!pages[i]) {
/*
* save number of pages we have already allocated and
* return with ENOMEM error
*/
num_pages = i;
rc = -ENOMEM;
goto error;
}
}
return rc;
error:
for (i = 0; i < num_pages; i++)
put_page(pages[i]);
return rc;
}
static inline
size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
{
size_t num_pages;
size_t clen;
clen = min_t(const size_t, len, wsize);
num_pages = clen / PAGE_CACHE_SIZE;
if (clen % PAGE_CACHE_SIZE)
num_pages++;
if (cur_len)
*cur_len = clen;
return num_pages;
}
static ssize_t
cifs_iovec_write(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
unsigned int written;
unsigned long num_pages, npages, i;
size_t copied, len, cur_len;
ssize_t total_written = 0;
struct kvec *to_send;
struct page **pages;
struct iov_iter it;
struct inode *inode;
struct cifsFileInfo *open_file;
struct cifsTconInfo *pTcon;
struct cifs_sb_info *cifs_sb;
int xid, rc;
__u32 netpid;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
rc = generic_write_checks(file, poffset, &len, 0);
if (rc)
return rc;
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
if (!pages)
return -ENOMEM;
to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
if (!to_send) {
kfree(pages);
return -ENOMEM;
}
rc = cifs_write_allocate_pages(pages, num_pages);
if (rc) {
kfree(pages);
kfree(to_send);
return rc;
}
xid = GetXid();
if (file->private_data == NULL) {
rc = -EBADF;
for (i = 0; i < num_pages; i++)
put_page(pages[i]);
kfree(pages);
kfree(to_send);
FreeXid(xid);
return rc;
}
open_file = file->private_data;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_WINE_MODE)
netpid = open_file->pid;
else
netpid = current->tgid;
pTcon = cifs_sb->tcon;
inode = file->f_path.dentry->d_inode;
iov_iter_init(&it, iov, nr_segs, len, 0);
npages = num_pages;
do {
size_t save_len = cur_len;
for (i = 0; i < npages; i++) {
copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
copied = iov_iter_copy_from_user(pages[i], &it, 0,
copied);
cur_len -= copied;
iov_iter_advance(&it, copied);
to_send[i+1].iov_base = kmap(pages[i]);
to_send[i+1].iov_len = copied;
}
cur_len = save_len - cur_len;
do {
if (open_file->invalidHandle) {
rc = cifs_reopen_file(file, false);
if (rc != 0)
break;
}
rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid,
netpid, cur_len, *poffset, &written,
to_send, npages, 0);
} while (rc == -EAGAIN);
for (i = 0; i < npages; i++)
kunmap(pages[i]);
if (written) {
len -= written;
total_written += written;
cifs_update_eof(CIFS_I(inode), *poffset, written);
*poffset += written;
} else if (rc < 0) {
if (!total_written)
total_written = rc;
break;
}
/* get length and number of kvecs of the next write */
npages = get_numpages(cifs_sb->wsize, len, &cur_len);
} while (len > 0);
if (total_written > 0) {
spin_lock(&inode->i_lock);
if (*poffset > inode->i_size)
i_size_write(inode, *poffset);
spin_unlock(&inode->i_lock);
}
cifs_stats_bytes_written(pTcon, total_written);
mark_inode_dirty_sync(inode);
for (i = 0; i < num_pages; i++)
put_page(pages[i]);
kfree(to_send);
kfree(pages);
FreeXid(xid);
return total_written;
}
static ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t written;
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
/*
* BB - optimize the way when signing is disabled. We can drop this
* extra memory-to-memory copying and use iovec buffers for constructing
* write request.
*/
written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
if (written > 0) {
CIFS_I(inode)->invalid_mapping = true;
iocb->ki_pos = pos;
}
return written;
}
ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
if (CIFS_I(inode)->clientCanCacheAll)
return generic_file_aio_write(iocb, iov, nr_segs, pos);
/*
* In strict cache mode we need to write the data to the server exactly
* from the pos to pos+len-1 rather than flush all affected pages
* because it may cause a error with mandatory locks on these pages but
* not on the region from pos to ppos+len-1.
*/
return cifs_user_writev(iocb, iov, nr_segs, pos);
}
static ssize_t
cifs_iovec_read(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
int rc;
int xid;
ssize_t total_read;
unsigned int bytes_read = 0; unsigned int bytes_read = 0;
unsigned int total_read = 0; size_t len, cur_len;
unsigned int current_read_size; int iov_offset = 0;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
int xid;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
char *smb_read_data;
char __user *current_offset;
struct smb_com_read_rsp *pSMBr; struct smb_com_read_rsp *pSMBr;
__u32 netpid; __u32 netpid;
char *read_data;
if (!nr_segs)
return 0;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
...@@ -1827,13 +2070,11 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1827,13 +2070,11 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
if ((file->f_flags & O_ACCMODE) == O_WRONLY) if ((file->f_flags & O_ACCMODE) == O_WRONLY)
cFYI(1, ("attempting read on write only file instance")); cFYI(1, ("attempting read on write only file instance"));
for (total_read = 0, current_offset = read_data; for (total_read = 0; total_read < len; total_read += bytes_read) {
read_size > total_read; cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
total_read += bytes_read, current_offset += bytes_read) {
current_read_size = min_t(const int, read_size - total_read,
cifs_sb->rsize);
rc = -EAGAIN; rc = -EAGAIN;
smb_read_data = NULL; read_data = NULL;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
int buf_type = CIFS_NO_BUFFER; int buf_type = CIFS_NO_BUFFER;
if ((open_file->invalidHandle) && if ((open_file->invalidHandle) &&
...@@ -1842,27 +2083,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1842,27 +2083,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
if (rc != 0) if (rc != 0)
break; break;
} }
rc = CIFSSMBRead(xid, pTcon, rc = CIFSSMBRead(xid, pTcon, open_file->netfid,
open_file->netfid, netpid, netpid, cur_len, *poffset, &bytes_read,
current_read_size, *poffset, &read_data, &buf_type);
&bytes_read, &smb_read_data, pSMBr = (struct smb_com_read_rsp *)read_data;
&buf_type); if (read_data) {
pSMBr = (struct smb_com_read_rsp *)smb_read_data; char *data_offset = read_data + 4 +
if (smb_read_data) { le16_to_cpu(pSMBr->DataOffset);
if (copy_to_user(current_offset, if (memcpy_toiovecend(iov, data_offset,
smb_read_data + iov_offset, bytes_read))
4 /* RFC1001 length field */ +
le16_to_cpu(pSMBr->DataOffset),
bytes_read))
rc = -EFAULT; rc = -EFAULT;
if (buf_type == CIFS_SMALL_BUFFER) if (buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(smb_read_data); cifs_small_buf_release(read_data);
else if (buf_type == CIFS_LARGE_BUFFER) else if (buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(smb_read_data); cifs_buf_release(read_data);
smb_read_data = NULL; read_data = NULL;
iov_offset += bytes_read;
} }
} }
if (rc || (bytes_read == 0)) { if (rc || (bytes_read == 0)) {
if (total_read) { if (total_read) {
break; break;
...@@ -1875,10 +2114,54 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1875,10 +2114,54 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
*poffset += bytes_read; *poffset += bytes_read;
} }
} }
FreeXid(xid); FreeXid(xid);
return total_read; return total_read;
} }
ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset)
{
struct iovec iov;
iov.iov_base = read_data;
iov.iov_len = read_size;
return cifs_iovec_read(file, &iov, 1, poffset);
}
static ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t read;
read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos);
if (read > 0)
iocb->ki_pos = pos;
return read;
}
ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
if (CIFS_I(inode)->clientCanCacheRead)
return generic_file_aio_read(iocb, iov, nr_segs, pos);
/*
* In strict cache mode we need to read from the server all the time
* if we don't have level II oplock because the server can delay mtime
* change - so we can't make a decision about inode invalidating.
* And we can also fail with pagereading if there are mandatory locks
* on pages affected by this read but not on the region from pos to
* pos+len-1.
*/
return cifs_user_readv(iocb, iov, nr_segs, pos);
}
static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
loff_t *poffset) loff_t *poffset)
...@@ -1956,6 +2239,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1956,6 +2239,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
return total_read; return total_read;
} }
int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
{
int rc, xid;
struct inode *inode = file->f_path.dentry->d_inode;
xid = GetXid();
if (!CIFS_I(inode)->clientCanCacheRead)
cifs_invalidate_mapping(inode);
rc = generic_file_mmap(file, vma);
FreeXid(xid);
return rc;
}
int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
......
...@@ -42,13 +42,17 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) ...@@ -42,13 +42,17 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
inode->i_fop = &cifs_file_direct_nobrl_ops; inode->i_fop = &cifs_file_direct_nobrl_ops;
else else
inode->i_fop = &cifs_file_direct_ops; inode->i_fop = &cifs_file_direct_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
inode->i_fop = &cifs_file_strict_nobrl_ops;
else
inode->i_fop = &cifs_file_strict_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
inode->i_fop = &cifs_file_nobrl_ops; inode->i_fop = &cifs_file_nobrl_ops;
else { /* not direct, send byte range locks */ else { /* not direct, send byte range locks */
inode->i_fop = &cifs_file_ops; inode->i_fop = &cifs_file_ops;
} }
/* check if server can support readpages */ /* check if server can support readpages */
if (cifs_sb->tcon->ses->server->maxBuf < if (cifs_sb->tcon->ses->server->maxBuf <
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
...@@ -1409,16 +1413,64 @@ cifs_rename_exit: ...@@ -1409,16 +1413,64 @@ cifs_rename_exit:
return rc; return rc;
} }
static bool cifs_check_inval(struct inode *inode)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
if (cifs_i->clientCanCacheRead)
return false;
if (!lookupCacheEnabled)
return true;
if (cifs_i->time == 0)
return true;
/* FIXME: the actimeo should be tunable */
if (time_after_eq(jiffies, cifs_i->time + HZ))
return true;
/* hardlinked files get "special" treatment */
if (S_ISREG(inode->i_mode) && inode->i_nlink != 1)
return true;
return false;
}
/*
* Zap the cache. Called when invalid_mapping flag is set.
*/
void
cifs_invalidate_mapping(struct inode *inode)
{
int rc;
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
cifs_i->invalid_mapping = false;
/* write back any cached data */
if (inode->i_mapping && inode->i_mapping->nrpages != 0) {
rc = filemap_write_and_wait(inode->i_mapping);
if (rc)
cifs_i->write_behind_rc = rc;
rc = invalidate_inode_pages2(inode->i_mapping);
if (rc) {
cERROR(1, ("%s: could not invalidate inode %p",
__func__, inode));
cifs_i->invalid_mapping = true;
}
}
}
int cifs_revalidate(struct dentry *direntry) int cifs_revalidate(struct dentry *direntry)
{ {
int xid; int xid;
int rc = 0, wbrc = 0; int rc = 0;
char *full_path; char *full_path;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsInodeInfo *cifsInode; struct cifsInodeInfo *cifsInode;
loff_t local_size; loff_t local_size;
struct timespec local_mtime; struct timespec local_mtime;
bool invalidate_inode = false;
if (direntry->d_inode == NULL) if (direntry->d_inode == NULL)
return -ENOENT; return -ENOENT;
...@@ -1428,10 +1480,6 @@ int cifs_revalidate(struct dentry *direntry) ...@@ -1428,10 +1480,6 @@ int cifs_revalidate(struct dentry *direntry)
if (cifsInode == NULL) if (cifsInode == NULL)
return -ENOENT; return -ENOENT;
/* no sense revalidating inode info on file that no one can write */
if (CIFS_I(direntry->d_inode)->clientCanCacheRead)
return rc;
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(direntry->d_sb); cifs_sb = CIFS_SB(direntry->d_sb);
...@@ -1449,19 +1497,8 @@ int cifs_revalidate(struct dentry *direntry) ...@@ -1449,19 +1497,8 @@ int cifs_revalidate(struct dentry *direntry)
direntry->d_inode->i_count.counter, direntry, direntry->d_inode->i_count.counter, direntry,
direntry->d_time, jiffies)); direntry->d_time, jiffies));
if (cifsInode->time == 0) { if (!cifs_check_inval(direntry->d_inode))
/* was set to zero previously to force revalidate */ goto check_inval;
} else if (time_before(jiffies, cifsInode->time + HZ) &&
lookupCacheEnabled) {
if ((S_ISREG(direntry->d_inode->i_mode) == 0) ||
(direntry->d_inode->i_nlink == 1)) {
kfree(full_path);
FreeXid(xid);
return rc;
} else {
cFYI(1, ("Have to revalidate file due to hardlinks"));
}
}
/* save mtime and size */ /* save mtime and size */
local_mtime = direntry->d_inode->i_mtime; local_mtime = direntry->d_inode->i_mtime;
...@@ -1495,48 +1532,16 @@ int cifs_revalidate(struct dentry *direntry) ...@@ -1495,48 +1532,16 @@ int cifs_revalidate(struct dentry *direntry)
(local_size == direntry->d_inode->i_size)) { (local_size == direntry->d_inode->i_size)) {
cFYI(1, ("cifs_revalidate - inode unchanged")); cFYI(1, ("cifs_revalidate - inode unchanged"));
} else { } else {
/* file may have changed on server */ cifsInode->invalid_mapping = true;
if (cifsInode->clientCanCacheRead) {
/* no need to invalidate inode pages since we were the
only ones who could have modified the file and the
server copy is staler than ours */
} else {
invalidate_inode = true;
}
} }
/* can not grab this sem since kernel filesys locking documentation /* can not grab this sem since kernel filesys locking documentation
indicates i_mutex may be taken by the kernel on lookup and rename indicates i_mutex may be taken by the kernel on lookup and rename
which could deadlock if we grab the i_mutex here as well */ which could deadlock if we grab the i_mutex here as well */
check_inval:
/* mutex_lock(&direntry->d_inode->i_mutex);*/ /* mutex_lock(&direntry->d_inode->i_mutex);*/
/* need to write out dirty pages here */ if (cifsInode->invalid_mapping)
if (direntry->d_inode->i_mapping) { cifs_invalidate_mapping(direntry->d_inode);
/* do we need to lock inode until after invalidate completes
below? */
wbrc = filemap_fdatawrite(direntry->d_inode->i_mapping);
if (wbrc)
CIFS_I(direntry->d_inode)->write_behind_rc = wbrc;
}
if (invalidate_inode) {
/* shrink_dcache not necessary now that cifs dentry ops
are exported for negative dentries */
/* if (S_ISDIR(direntry->d_inode->i_mode))
shrink_dcache_parent(direntry); */
if (S_ISREG(direntry->d_inode->i_mode)) {
if (direntry->d_inode->i_mapping) {
wbrc = filemap_fdatawait(direntry->d_inode->i_mapping);
if (wbrc)
CIFS_I(direntry->d_inode)->write_behind_rc = wbrc;
}
/* may eventually have to do this for open files too */
if (list_empty(&(cifsInode->openFileList))) {
/* changed on server - flush read ahead pages */
cFYI(1, ("Invalidating read ahead data on "
"closed file"));
invalidate_remote_inode(direntry->d_inode);
}
}
}
/* mutex_unlock(&direntry->d_inode->i_mutex); */ /* mutex_unlock(&direntry->d_inode->i_mutex); */
kfree(full_path); kfree(full_path);
......
...@@ -569,6 +569,10 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -569,6 +569,10 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
if (pSMB->Fid != netfile->netfid) if (pSMB->Fid != netfile->netfid)
continue; continue;
cFYI(1, ("file id match, oplock break"));
pCifsInode = CIFS_I(netfile->pInode);
cifs_set_oplock_level(pCifsInode,
pSMB->OplockLevel ? OPLOCK_READ : 0);
/* /*
* don't do anything if file is about to be * don't do anything if file is about to be
* closed anyway. * closed anyway.
...@@ -579,11 +583,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -579,11 +583,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
return true; return true;
} }
cFYI(1, ("file id match, oplock break"));
pCifsInode = CIFS_I(netfile->pInode);
pCifsInode->clientCanCacheAll = false;
if (pSMB->OplockLevel == 0)
pCifsInode->clientCanCacheRead = false;
rc = slow_work_enqueue(&netfile->oplock_break); rc = slow_work_enqueue(&netfile->oplock_break);
if (rc) { if (rc) {
cERROR(1, ("failed to enqueue oplock " cERROR(1, ("failed to enqueue oplock "
...@@ -729,3 +728,23 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) ...@@ -729,3 +728,23 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
cifs_sb->tcon->treeName)); cifs_sb->tcon->treeName));
} }
} }
void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
{
oplock &= 0xF;
if (oplock == OPLOCK_EXCLUSIVE) {
cinode->clientCanCacheAll = true;
cinode->clientCanCacheRead = true;
cFYI(1, ("Exclusive Oplock granted on inode %p",
&cinode->vfs_inode));
} else if (oplock == OPLOCK_READ) {
cinode->clientCanCacheAll = false;
cinode->clientCanCacheRead = true;
cFYI(1, ("Level II Oplock granted on inode %p",
&cinode->vfs_inode));
} else {
cinode->clientCanCacheAll = false;
cinode->clientCanCacheRead = false;
}
}
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