Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Suresh Jayaraman and committed by
Trond Myklebust
5eebde23 63185942

+97 -16
+4 -2
fs/nfs/client.c
··· 635 635 */ 636 636 static 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 640 nlmclnt_done(server->nlm_host); 640 641 } 641 642 ··· 658 657 659 658 if (nlm_init.nfs_version > 3) 660 659 return 0; 661 - if (server->flags & NFS_MOUNT_NONLM) 660 + if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 661 + (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 662 662 return 0; 663 663 664 664 switch (clp->cl_proto) {
+32 -13
fs/nfs/file.c
··· 684 684 return ret; 685 685 } 686 686 687 - static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) 687 + static int 688 + do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) 688 689 { 689 690 struct inode *inode = filp->f_mapping->host; 690 691 int status = 0; ··· 700 699 if (nfs_have_delegation(inode, FMODE_READ)) 701 700 goto out_noconflict; 702 701 703 - if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) 702 + if (is_local) 704 703 goto out_noconflict; 705 704 706 705 status = NFS_PROTO(inode)->lock(filp, cmd, fl); ··· 731 730 return res; 732 731 } 733 732 734 - static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) 733 + static int 734 + do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) 735 735 { 736 736 struct inode *inode = filp->f_mapping->host; 737 737 int status; ··· 747 745 * If we're signalled while cleaning up locks on process exit, we 748 746 * still need to complete the unlock. 749 747 */ 750 - /* Use local locking if mounted with "-onolock" */ 751 - if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) 748 + /* 749 + * Use local locking if mounted with "-onolock" or with appropriate 750 + * "-olocal_lock=" 751 + */ 752 + if (!is_local) 752 753 status = NFS_PROTO(inode)->lock(filp, cmd, fl); 753 754 else 754 755 status = do_vfs_lock(filp, fl); 755 756 return status; 756 757 } 757 758 758 - static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) 759 + static int 760 + do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) 759 761 { 760 762 struct inode *inode = filp->f_mapping->host; 761 763 int status; ··· 772 766 if (status != 0) 773 767 goto out; 774 768 775 - /* Use local locking if mounted with "-onolock" */ 776 - if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) 769 + /* 770 + * Use local locking if mounted with "-onolock" or with appropriate 771 + * "-olocal_lock=" 772 + */ 773 + if (!is_local) 777 774 status = NFS_PROTO(inode)->lock(filp, cmd, fl); 778 775 else 779 776 status = do_vfs_lock(filp, fl); ··· 800 791 { 801 792 struct inode *inode = filp->f_mapping->host; 802 793 int ret = -ENOLCK; 794 + int is_local = 0; 803 795 804 796 dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n", 805 797 filp->f_path.dentry->d_parent->d_name.name, ··· 814 804 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) 815 805 goto out_err; 816 806 807 + if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL) 808 + is_local = 1; 809 + 817 810 if (NFS_PROTO(inode)->lock_check_bounds != NULL) { 818 811 ret = NFS_PROTO(inode)->lock_check_bounds(fl); 819 812 if (ret < 0) ··· 824 811 } 825 812 826 813 if (IS_GETLK(cmd)) 827 - ret = do_getlk(filp, cmd, fl); 814 + ret = do_getlk(filp, cmd, fl, is_local); 828 815 else if (fl->fl_type == F_UNLCK) 829 - ret = do_unlk(filp, cmd, fl); 816 + ret = do_unlk(filp, cmd, fl, is_local); 830 817 else 831 - ret = do_setlk(filp, cmd, fl); 818 + ret = do_setlk(filp, cmd, fl, is_local); 832 819 out_err: 833 820 return ret; 834 821 } ··· 838 825 */ 839 826 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) 840 827 { 828 + struct inode *inode = filp->f_mapping->host; 829 + int is_local = 0; 830 + 841 831 dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n", 842 832 filp->f_path.dentry->d_parent->d_name.name, 843 833 filp->f_path.dentry->d_name.name, ··· 849 833 if (!(fl->fl_flags & FL_FLOCK)) 850 834 return -ENOLCK; 851 835 836 + if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK) 837 + is_local = 1; 838 + 852 839 /* We're simulating flock() locks using posix locks on the server */ 853 840 fl->fl_owner = (fl_owner_t)filp; 854 841 fl->fl_start = 0; 855 842 fl->fl_end = OFFSET_MAX; 856 843 857 844 if (fl->fl_type == F_UNLCK) 858 - return do_unlk(filp, cmd, fl); 859 - return do_setlk(filp, cmd, fl); 845 + return do_unlk(filp, cmd, fl, is_local); 846 + return do_setlk(filp, cmd, fl, is_local); 860 847 } 861 848 862 849 /*
+58 -1
fs/nfs/super.c
··· 100 100 Opt_addr, Opt_mountaddr, Opt_clientaddr, 101 101 Opt_lookupcache, 102 102 Opt_fscache_uniq, 103 + Opt_local_lock, 103 104 104 105 /* Special mount options */ 105 106 Opt_userspace, Opt_deprecated, Opt_sloppy, ··· 172 171 173 172 { Opt_lookupcache, "lookupcache=%s" }, 174 173 { Opt_fscache_uniq, "fsc=%s" }, 174 + { Opt_local_lock, "local_lock=%s" }, 175 175 176 176 { Opt_err, NULL } 177 177 }; ··· 236 234 { Opt_lookupcache_none, "none" }, 237 235 238 236 { Opt_lookupcache_err, NULL } 237 + }; 238 + 239 + enum { 240 + Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix, 241 + Opt_local_lock_none, 242 + 243 + Opt_local_lock_err 244 + }; 245 + 246 + static match_table_t nfs_local_lock_tokens = { 247 + { Opt_local_lock_all, "all" }, 248 + { Opt_local_lock_flock, "flock" }, 249 + { Opt_local_lock_posix, "posix" }, 250 + { Opt_local_lock_none, "none" }, 251 + 252 + { Opt_local_lock_err, NULL } 239 253 }; 240 254 241 255 ··· 1027 1009 break; 1028 1010 case Opt_lock: 1029 1011 mnt->flags &= ~NFS_MOUNT_NONLM; 1012 + mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | 1013 + NFS_MOUNT_LOCAL_FCNTL); 1030 1014 break; 1031 1015 case Opt_nolock: 1032 1016 mnt->flags |= NFS_MOUNT_NONLM; 1017 + mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK | 1018 + NFS_MOUNT_LOCAL_FCNTL); 1033 1019 break; 1034 1020 case Opt_v2: 1035 1021 mnt->flags &= ~NFS_MOUNT_VER3; ··· 1433 1411 kfree(mnt->fscache_uniq); 1434 1412 mnt->fscache_uniq = string; 1435 1413 mnt->options |= NFS_OPTION_FSCACHE; 1414 + break; 1415 + case Opt_local_lock: 1416 + string = match_strdup(args); 1417 + if (string == NULL) 1418 + goto out_nomem; 1419 + token = match_token(string, nfs_local_lock_tokens, 1420 + args); 1421 + kfree(string); 1422 + switch (token) { 1423 + case Opt_local_lock_all: 1424 + mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK | 1425 + NFS_MOUNT_LOCAL_FCNTL); 1426 + break; 1427 + case Opt_local_lock_flock: 1428 + mnt->flags |= NFS_MOUNT_LOCAL_FLOCK; 1429 + break; 1430 + case Opt_local_lock_posix: 1431 + mnt->flags |= NFS_MOUNT_LOCAL_FCNTL; 1432 + break; 1433 + case Opt_local_lock_none: 1434 + mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | 1435 + NFS_MOUNT_LOCAL_FCNTL); 1436 + break; 1437 + default: 1438 + dfprintk(MOUNT, "NFS: invalid " 1439 + "local_lock argument\n"); 1440 + return 0; 1441 + }; 1436 1442 break; 1437 1443 1438 1444 /* ··· 1867 1817 if (!args->nfs_server.hostname) 1868 1818 goto out_nomem; 1869 1819 1820 + if (!(data->flags & NFS_MOUNT_NONLM)) 1821 + args->flags &= ~(NFS_MOUNT_LOCAL_FLOCK| 1822 + NFS_MOUNT_LOCAL_FCNTL); 1823 + else 1824 + args->flags |= (NFS_MOUNT_LOCAL_FLOCK| 1825 + NFS_MOUNT_LOCAL_FCNTL); 1870 1826 /* 1871 1827 * The legacy version 6 binary mount data from userspace has a 1872 1828 * field used only to transport selinux information into the ··· 2489 2433 2490 2434 static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) 2491 2435 { 2492 - args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3); 2436 + args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3| 2437 + NFS_MOUNT_LOCAL_FLOCK|NFS_MOUNT_LOCAL_FCNTL); 2493 2438 } 2494 2439 2495 2440 static int nfs4_validate_text_mount_data(void *options,
+3
include/linux/nfs_mount.h
··· 71 71 #define NFS_MOUNT_NORESVPORT 0x40000 72 72 #define NFS_MOUNT_LEGACY_INTERFACE 0x80000 73 73 74 + #define NFS_MOUNT_LOCAL_FLOCK 0x100000 75 + #define NFS_MOUNT_LOCAL_FCNTL 0x200000 76 + 74 77 #endif