aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSuresh Jayaraman <sjayaraman@suse.de>2010-09-23 08:55:58 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-09-23 08:55:58 -0400
commit5eebde23223aeb0ad2d9e3be6590ff8bbfab0fc2 (patch)
tree7cfabe0191855550292b0a0c9b317315dffb2a50 /fs
parent63185942c5f138c62de16b4cbc7eee494a58fea8 (diff)
nfs: introduce mount option '-olocal_lock' to make locks local
NFS clients since 2.6.12 support flock locks by emulating fcntl byte-range locks. Due to this, some windows applications which seem to use both flock (share mode lock mapped as flock by Samba) and fcntl locks sequentially on the same file, can't lock as they falsely assume the file is already locked. The problem was reported on a setup with windows clients accessing excel files on a Samba exported share which is originally a NFS mount from a NetApp filer. Older NFS clients (< 2.6.12) did not see this problem as flock locks were considered local. To support legacy flock behavior, this patch adds a mount option "-olocal_lock=" which can take the following values: 'none' - Neither flock locks nor POSIX locks are local 'flock' - flock locks are local 'posix' - fcntl/POSIX locks are local 'all' - Both flock locks and POSIX locks are local Testing: - This patch was tested by using -olocal_lock option with different values and the NLM calls were noted from the network packet captured. 'none' - NLM calls were seen during both flock() and fcntl(), flock lock was granted, fcntl was denied 'flock' - no NLM calls for flock(), NLM call was seen for fcntl(), granted 'posix' - NLM call was seen for flock() - granted, no NLM call for fcntl() 'all' - no NLM calls were seen during both flock() and fcntl() - No bugs were seen during NFSv4 locking/unlocking in general and NFSv4 reboot recovery. Cc: Neil Brown <neilb@suse.de> Signed-off-by: Suresh Jayaraman <sjayaraman@suse.de> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/client.c6
-rw-r--r--fs/nfs/file.c45
-rw-r--r--fs/nfs/super.c59
3 files changed, 94 insertions, 16 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 4e7df2adb212..5f01f42b3991 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -635,7 +635,8 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
635 */ 635 */
636static void nfs_destroy_server(struct nfs_server *server) 636static void nfs_destroy_server(struct nfs_server *server)
637{ 637{
638 if (!(server->flags & NFS_MOUNT_NONLM)) 638 if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) ||
639 !(server->flags & NFS_MOUNT_LOCAL_FCNTL))
639 nlmclnt_done(server->nlm_host); 640 nlmclnt_done(server->nlm_host);
640} 641}
641 642
@@ -657,7 +658,8 @@ static int nfs_start_lockd(struct nfs_server *server)
657 658
658 if (nlm_init.nfs_version > 3) 659 if (nlm_init.nfs_version > 3)
659 return 0; 660 return 0;
660 if (server->flags & NFS_MOUNT_NONLM) 661 if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) &&
662 (server->flags & NFS_MOUNT_LOCAL_FCNTL))
661 return 0; 663 return 0;
662 664
663 switch (clp->cl_proto) { 665 switch (clp->cl_proto) {
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index eb51bd6201da..59cbe1ba0511 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -684,7 +684,8 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
684 return ret; 684 return ret;
685} 685}
686 686
687static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) 687static int
688do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
688{ 689{
689 struct inode *inode = filp->f_mapping->host; 690 struct inode *inode = filp->f_mapping->host;
690 int status = 0; 691 int status = 0;
@@ -699,7 +700,7 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
699 if (nfs_have_delegation(inode, FMODE_READ)) 700 if (nfs_have_delegation(inode, FMODE_READ))
700 goto out_noconflict; 701 goto out_noconflict;
701 702
702 if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) 703 if (is_local)
703 goto out_noconflict; 704 goto out_noconflict;
704 705
705 status = NFS_PROTO(inode)->lock(filp, cmd, fl); 706 status = NFS_PROTO(inode)->lock(filp, cmd, fl);
@@ -730,7 +731,8 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl)
730 return res; 731 return res;
731} 732}
732 733
733static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) 734static int
735do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
734{ 736{
735 struct inode *inode = filp->f_mapping->host; 737 struct inode *inode = filp->f_mapping->host;
736 int status; 738 int status;
@@ -745,15 +747,19 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
745 * If we're signalled while cleaning up locks on process exit, we 747 * If we're signalled while cleaning up locks on process exit, we
746 * still need to complete the unlock. 748 * still need to complete the unlock.
747 */ 749 */
748 /* Use local locking if mounted with "-onolock" */ 750 /*
749 if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) 751 * Use local locking if mounted with "-onolock" or with appropriate
752 * "-olocal_lock="
753 */
754 if (!is_local)
750 status = NFS_PROTO(inode)->lock(filp, cmd, fl); 755 status = NFS_PROTO(inode)->lock(filp, cmd, fl);
751 else 756 else
752 status = do_vfs_lock(filp, fl); 757 status = do_vfs_lock(filp, fl);
753 return status; 758 return status;
754} 759}
755 760
756static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) 761static int
762do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
757{ 763{
758 struct inode *inode = filp->f_mapping->host; 764 struct inode *inode = filp->f_mapping->host;
759 int status; 765 int status;
@@ -766,8 +772,11 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
766 if (status != 0) 772 if (status != 0)
767 goto out; 773 goto out;
768 774
769 /* Use local locking if mounted with "-onolock" */ 775 /*
770 if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) 776 * Use local locking if mounted with "-onolock" or with appropriate
777 * "-olocal_lock="
778 */
779 if (!is_local)
771 status = NFS_PROTO(inode)->lock(filp, cmd, fl); 780 status = NFS_PROTO(inode)->lock(filp, cmd, fl);
772 else 781 else
773 status = do_vfs_lock(filp, fl); 782 status = do_vfs_lock(filp, fl);
@@ -791,6 +800,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
791{ 800{
792 struct inode *inode = filp->f_mapping->host; 801 struct inode *inode = filp->f_mapping->host;
793 int ret = -ENOLCK; 802 int ret = -ENOLCK;
803 int is_local = 0;
794 804
795 dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n", 805 dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n",
796 filp->f_path.dentry->d_parent->d_name.name, 806 filp->f_path.dentry->d_parent->d_name.name,
@@ -804,6 +814,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
804 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) 814 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
805 goto out_err; 815 goto out_err;
806 816
817 if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL)
818 is_local = 1;
819
807 if (NFS_PROTO(inode)->lock_check_bounds != NULL) { 820 if (NFS_PROTO(inode)->lock_check_bounds != NULL) {
808 ret = NFS_PROTO(inode)->lock_check_bounds(fl); 821 ret = NFS_PROTO(inode)->lock_check_bounds(fl);
809 if (ret < 0) 822 if (ret < 0)
@@ -811,11 +824,11 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
811 } 824 }
812 825
813 if (IS_GETLK(cmd)) 826 if (IS_GETLK(cmd))
814 ret = do_getlk(filp, cmd, fl); 827 ret = do_getlk(filp, cmd, fl, is_local);
815 else if (fl->fl_type == F_UNLCK) 828 else if (fl->fl_type == F_UNLCK)
816 ret = do_unlk(filp, cmd, fl); 829 ret = do_unlk(filp, cmd, fl, is_local);
817 else 830 else
818 ret = do_setlk(filp, cmd, fl); 831 ret = do_setlk(filp, cmd, fl, is_local);
819out_err: 832out_err:
820 return ret; 833 return ret;
821} 834}
@@ -825,6 +838,9 @@ out_err:
825 */ 838 */
826static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) 839static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
827{ 840{
841 struct inode *inode = filp->f_mapping->host;
842 int is_local = 0;
843
828 dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n", 844 dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n",
829 filp->f_path.dentry->d_parent->d_name.name, 845 filp->f_path.dentry->d_parent->d_name.name,
830 filp->f_path.dentry->d_name.name, 846 filp->f_path.dentry->d_name.name,
@@ -833,14 +849,17 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
833 if (!(fl->fl_flags & FL_FLOCK)) 849 if (!(fl->fl_flags & FL_FLOCK))
834 return -ENOLCK; 850 return -ENOLCK;
835 851
852 if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK)
853 is_local = 1;
854
836 /* We're simulating flock() locks using posix locks on the server */ 855 /* We're simulating flock() locks using posix locks on the server */
837 fl->fl_owner = (fl_owner_t)filp; 856 fl->fl_owner = (fl_owner_t)filp;
838 fl->fl_start = 0; 857 fl->fl_start = 0;
839 fl->fl_end = OFFSET_MAX; 858 fl->fl_end = OFFSET_MAX;
840 859
841 if (fl->fl_type == F_UNLCK) 860 if (fl->fl_type == F_UNLCK)
842 return do_unlk(filp, cmd, fl); 861 return do_unlk(filp, cmd, fl, is_local);
843 return do_setlk(filp, cmd, fl); 862 return do_setlk(filp, cmd, fl, is_local);
844} 863}
845 864
846/* 865/*
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index ec3966e4706b..2e866d86c220 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -100,6 +100,7 @@ enum {
100 Opt_addr, Opt_mountaddr, Opt_clientaddr, 100 Opt_addr, Opt_mountaddr, Opt_clientaddr,
101 Opt_lookupcache, 101 Opt_lookupcache,
102 Opt_fscache_uniq, 102 Opt_fscache_uniq,
103 Opt_local_lock,
103 104
104 /* Special mount options */ 105 /* Special mount options */
105 Opt_userspace, Opt_deprecated, Opt_sloppy, 106 Opt_userspace, Opt_deprecated, Opt_sloppy,
@@ -171,6 +172,7 @@ static const match_table_t nfs_mount_option_tokens = {
171 172
172 { Opt_lookupcache, "lookupcache=%s" }, 173 { Opt_lookupcache, "lookupcache=%s" },
173 { Opt_fscache_uniq, "fsc=%s" }, 174 { Opt_fscache_uniq, "fsc=%s" },
175 { Opt_local_lock, "local_lock=%s" },
174 176
175 { Opt_err, NULL } 177 { Opt_err, NULL }
176}; 178};
@@ -236,6 +238,22 @@ static match_table_t nfs_lookupcache_tokens = {
236 { Opt_lookupcache_err, NULL } 238 { Opt_lookupcache_err, NULL }
237}; 239};
238 240
241enum {
242 Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix,
243 Opt_local_lock_none,
244
245 Opt_local_lock_err
246};
247
248static match_table_t nfs_local_lock_tokens = {
249 { Opt_local_lock_all, "all" },
250 { Opt_local_lock_flock, "flock" },
251 { Opt_local_lock_posix, "posix" },
252 { Opt_local_lock_none, "none" },
253
254 { Opt_local_lock_err, NULL }
255};
256
239 257
240static void nfs_umount_begin(struct super_block *); 258static void nfs_umount_begin(struct super_block *);
241static int nfs_statfs(struct dentry *, struct kstatfs *); 259static int nfs_statfs(struct dentry *, struct kstatfs *);
@@ -1009,9 +1027,13 @@ static int nfs_parse_mount_options(char *raw,
1009 break; 1027 break;
1010 case Opt_lock: 1028 case Opt_lock:
1011 mnt->flags &= ~NFS_MOUNT_NONLM; 1029 mnt->flags &= ~NFS_MOUNT_NONLM;
1030 mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
1031 NFS_MOUNT_LOCAL_FCNTL);
1012 break; 1032 break;
1013 case Opt_nolock: 1033 case Opt_nolock:
1014 mnt->flags |= NFS_MOUNT_NONLM; 1034 mnt->flags |= NFS_MOUNT_NONLM;
1035 mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
1036 NFS_MOUNT_LOCAL_FCNTL);
1015 break; 1037 break;
1016 case Opt_v2: 1038 case Opt_v2:
1017 mnt->flags &= ~NFS_MOUNT_VER3; 1039 mnt->flags &= ~NFS_MOUNT_VER3;
@@ -1412,6 +1434,34 @@ static int nfs_parse_mount_options(char *raw,
1412 mnt->fscache_uniq = string; 1434 mnt->fscache_uniq = string;
1413 mnt->options |= NFS_OPTION_FSCACHE; 1435 mnt->options |= NFS_OPTION_FSCACHE;
1414 break; 1436 break;
1437 case Opt_local_lock:
1438 string = match_strdup(args);
1439 if (string == NULL)
1440 goto out_nomem;
1441 token = match_token(string, nfs_local_lock_tokens,
1442 args);
1443 kfree(string);
1444 switch (token) {
1445 case Opt_local_lock_all:
1446 mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
1447 NFS_MOUNT_LOCAL_FCNTL);
1448 break;
1449 case Opt_local_lock_flock:
1450 mnt->flags |= NFS_MOUNT_LOCAL_FLOCK;
1451 break;
1452 case Opt_local_lock_posix:
1453 mnt->flags |= NFS_MOUNT_LOCAL_FCNTL;
1454 break;
1455 case Opt_local_lock_none:
1456 mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
1457 NFS_MOUNT_LOCAL_FCNTL);
1458 break;
1459 default:
1460 dfprintk(MOUNT, "NFS: invalid "
1461 "local_lock argument\n");
1462 return 0;
1463 };
1464 break;
1415 1465
1416 /* 1466 /*
1417 * Special options 1467 * Special options
@@ -1817,6 +1867,12 @@ static int nfs_validate_mount_data(void *options,
1817 if (!args->nfs_server.hostname) 1867 if (!args->nfs_server.hostname)
1818 goto out_nomem; 1868 goto out_nomem;
1819 1869
1870 if (!(data->flags & NFS_MOUNT_NONLM))
1871 args->flags &= ~(NFS_MOUNT_LOCAL_FLOCK|
1872 NFS_MOUNT_LOCAL_FCNTL);
1873 else
1874 args->flags |= (NFS_MOUNT_LOCAL_FLOCK|
1875 NFS_MOUNT_LOCAL_FCNTL);
1820 /* 1876 /*
1821 * The legacy version 6 binary mount data from userspace has a 1877 * The legacy version 6 binary mount data from userspace has a
1822 * field used only to transport selinux information into the 1878 * field used only to transport selinux information into the
@@ -2433,7 +2489,8 @@ static void nfs4_fill_super(struct super_block *sb)
2433 2489
2434static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) 2490static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
2435{ 2491{
2436 args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3); 2492 args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3|
2493 NFS_MOUNT_LOCAL_FLOCK|NFS_MOUNT_LOCAL_FCNTL);
2437} 2494}
2438 2495
2439static int nfs4_validate_text_mount_data(void *options, 2496static int nfs4_validate_text_mount_data(void *options,