diff options
author | Steve French <sfrench@us.ibm.com> | 2008-10-23 04:42:37 +0000 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-10-23 04:42:37 +0000 |
commit | 84210e9120a8c01a14379ba1f9a9b0f963641d94 (patch) | |
tree | ed20b210bb554f283d8458ee6d146a5c4439e37f | |
parent | a364bc0b37f14ffd66c1f982af42990a9d77fa43 (diff) | |
download | linux-84210e9120a8c01a14379ba1f9a9b0f963641d94.tar.bz2 |
[CIFS] improve setlease handling
fcntl(F_SETLEASE) currently is not exported by cifs (nor by local file
systems) so cifs grants leases based on how other local processes have
opened the file not by whether the file is cacheable (oplocked). This
adds the check to make sure that the file is cacheable on the client
before checking whether we can grant the lease locally
(generic_setlease). It also adds a mount option for cifs (locallease)
if the user wants to override this and try to grant leases even
if the server did not grant oplock.
Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r-- | fs/cifs/CHANGES | 3 | ||||
-rw-r--r-- | fs/cifs/README | 16 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 41 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
-rw-r--r-- | fs/cifs/connect.c | 8 |
5 files changed, 67 insertions, 2 deletions
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index a7255751ffbc..8f528ea24c48 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -3,7 +3,8 @@ Version 1.55 Various fixes to make delete of open files behavior more predictable (when delete of an open file fails we mark the file as "delete-on-close" in a way that more servers accept, but only if we can first rename the -file to a temporary name) +file to a temporary name). Add experimental support for more safely +handling fcntl(F_SETLEASE). Version 1.54 ------------ diff --git a/fs/cifs/README b/fs/cifs/README index cbe26fe40120..a439dc1739b3 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -491,6 +491,19 @@ A partial list of the supported mount options follows: Note that this differs from the sign mount option in that it causes encryption of data sent over this mounted share but other shares mounted to the same server are unaffected. + locallease This option is rarely needed. Fcntl F_SETLEASE is + used by some applications such as Samba and NFSv4 server to + check to see whether a file is cacheable. CIFS has no way + to explicitly request a lease, but can check whether a file + is cacheable (oplocked). Unfortunately, even if a file + is not oplocked, it could still be cacheable (ie cifs client + could grant fcntl leases if no other local processes are using + the file) for cases for example such as when the server does not + support oplocks and the user is sure that the only updates to + the file will be from this client. Specifying this mount option + will allow the cifs client to check for leases (only) locally + for files which are not oplocked instead of denying leases + in that case. (EXPERIMENTAL) sec Security mode. Allowed values are: none attempt to connection as a null user (no name) krb5 Use Kerberos version 5 authentication @@ -641,6 +654,9 @@ requires enabling CONFIG_CIFS_EXPERIMENTAL cifsacl support needed to retrieve approximated mode bits based on the contents on the CIFS ACL. + lease support: cifs will check the oplock state before calling into + the vfs to see if we can grant a lease on a file. + DNOTIFY fcntl: needed for support of directory change notification and perhaps later for file leases) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index c6aad775dd6d..76919c25acc7 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -618,6 +618,37 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) return generic_file_llseek_unlocked(file, offset, origin); } +#ifdef CONFIG_CIFS_EXPERIMENTAL +static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) +{ + /* note that this is called by vfs setlease with the BKL held + although I doubt that BKL is needed here in cifs */ + struct inode *inode = file->f_path.dentry->d_inode; + + if (!(S_ISREG(inode->i_mode))) + return -EINVAL; + + /* check if file is oplocked */ + if (((arg == F_RDLCK) && + (CIFS_I(inode)->clientCanCacheRead)) || + ((arg == F_WRLCK) && + (CIFS_I(inode)->clientCanCacheAll))) + return generic_setlease(file, arg, lease); + else if (CIFS_SB(inode->i_sb)->tcon->local_lease && + !CIFS_I(inode)->clientCanCacheRead) + /* If the server claims to support oplock on this + file, then we still need to check oplock even + if the local_lease mount option is set, but there + are servers which do not support oplock for which + this mount option may be useful if the user + knows that the file won't be changed on the server + by anyone else */ + return generic_setlease(file, arg, lease); + else + return -EAGAIN; +} +#endif + struct file_system_type cifs_fs_type = { .owner = THIS_MODULE, .name = "cifs", @@ -696,6 +727,7 @@ const struct file_operations cifs_file_ops = { #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, + .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; @@ -716,6 +748,7 @@ const struct file_operations cifs_file_direct_ops = { .llseek = cifs_llseek, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, + .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_file_nobrl_ops = { @@ -736,6 +769,7 @@ const struct file_operations cifs_file_nobrl_ops = { #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, + .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; @@ -755,6 +789,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { .llseek = cifs_llseek, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, + .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; @@ -946,6 +981,12 @@ static int cifs_oplock_thread(void *dummyarg) 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( diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 178f733a368f..c791e5b5a914 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -285,6 +285,7 @@ struct cifsTconInfo { bool seal:1; /* transport encryption for this mounted share */ bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol for this mount even if server would support */ + bool local_lease:1; /* check leases (only) on local system not remote */ /* BB add field for back pointer to sb struct(s)? */ }; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 1126f7ab4606..f51b79a67e1b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -90,7 +90,8 @@ struct smb_vol { bool nocase:1; /* request case insensitive filenames */ bool nobrl:1; /* disable sending byte range locks to srv */ bool seal:1; /* request transport encryption on share */ - bool nodfs:1; + bool nodfs:1; /* Do not request DFS, even if available */ + bool local_lease:1; /* check leases only on local system, not remote */ unsigned int rsize; unsigned int wsize; unsigned int sockopt; @@ -1264,6 +1265,10 @@ cifs_parse_mount_options(char *options, const char *devname, vol->no_psx_acl = 0; } else if (strnicmp(data, "noacl", 5) == 0) { vol->no_psx_acl = 1; +#ifdef CONFIG_CIFS_EXPERIMENTAL + } else if (strnicmp(data, "locallease", 6) == 0) { + vol->local_lease = 1; +#endif } else if (strnicmp(data, "sign", 4) == 0) { vol->secFlg |= CIFSSEC_MUST_SIGN; } else if (strnicmp(data, "seal", 4) == 0) { @@ -2162,6 +2167,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, for the retry flag is used */ tcon->retry = volume_info.retry; tcon->nocase = volume_info.nocase; + tcon->local_lease = volume_info.local_lease; if (tcon->seal != volume_info.seal) cERROR(1, ("transport encryption setting " "conflicts with existing tid")); |