aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2009-06-17 21:02:10 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2009-06-17 21:02:10 -0400
commit6c9dc4255108bab4ef5c177d369b99c3c23492a7 (patch)
tree1369d9d804e276c32ab157e64646888b64ca0640
parent18fc31641925867c871bc75270ce642c039188d3 (diff)
lockd: Update NSM state from SM_MON replies
When rpc.statd starts up in user space at boot time, it attempts to write the latest NSM local state number into /proc/sys/fs/nfs/nsm_local_state. If lockd.ko isn't loaded yet (as is the case in most configurations), that file doesn't exist, thus the kernel's NSM state remains set to its initial value of zero during lockd operation. This is a problem because rpc.statd and lockd use the NSM state number to prevent repeated lock recovery on rebooted hosts. If lockd sends a zero NSM state, but then a delayed SM_NOTIFY with a real NSM state number is received, there is no way for lockd or rpc.statd to distinguish that stale SM_NOTIFY from an actual reboot. Thus lock recovery could be performed after the rebooted host has already started reclaiming locks, and those locks will be lost. We could change /etc/init.d/nfslock so it always modprobes lockd.ko before starting rpc.statd. However, if lockd.ko is ever unloaded and reloaded, we are back at square one, since the NSM state is not preserved across an unload/reload cycle. This may happen frequently on clients that use automounter. A period of NFS inactivity causes lockd.ko to be unloaded, and the kernel loses its NSM state setting. Instead, let's use the fact that rpc.statd plants the local system's NSM state in every SM_MON (and SM_UNMON) reply. lockd performs a synchronous SM_MON upcall to the local rpc.statd _before_ sending its first NLM request to a new remote. This would permit rpc.statd to provide the current NSM state to lockd, even after lockd.ko had been unloaded and reloaded. Note that NLMPROC_LOCK arguments are constructed before the nsm_monitor() call, so we have to rearrange argument construction very slightly to make this all work out. And, the kernel appears to treat NSM state as a u32 (see struct nlm_args and nsm_res). Make nsm_local_state a u32 as well, to ensure we don't get bogus comparison results. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/lockd/clntproc.c2
-rw-r--r--fs/lockd/mon.c18
-rw-r--r--include/linux/lockd/lockd.h2
3 files changed, 14 insertions, 8 deletions
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 273e229353f3..f2fdcbce143e 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -126,7 +126,6 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
126 struct nlm_lock *lock = &argp->lock; 126 struct nlm_lock *lock = &argp->lock;
127 127
128 nlmclnt_next_cookie(&argp->cookie); 128 nlmclnt_next_cookie(&argp->cookie);
129 argp->state = nsm_local_state;
130 memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh)); 129 memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh));
131 lock->caller = utsname()->nodename; 130 lock->caller = utsname()->nodename;
132 lock->oh.data = req->a_owner; 131 lock->oh.data = req->a_owner;
@@ -521,6 +520,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
521 520
522 if (nsm_monitor(host) < 0) 521 if (nsm_monitor(host) < 0)
523 goto out; 522 goto out;
523 req->a_args.state = nsm_local_state;
524 524
525 fl->fl_flags |= FL_ACCESS; 525 fl->fl_flags |= FL_ACCESS;
526 status = do_vfs_lock(fl); 526 status = do_vfs_lock(fl);
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 6d5d4a4169e5..38385336614c 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -53,7 +53,7 @@ static DEFINE_SPINLOCK(nsm_lock);
53/* 53/*
54 * Local NSM state 54 * Local NSM state
55 */ 55 */
56int __read_mostly nsm_local_state; 56u32 __read_mostly nsm_local_state;
57int __read_mostly nsm_use_hostnames; 57int __read_mostly nsm_use_hostnames;
58 58
59static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm) 59static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
@@ -184,13 +184,19 @@ int nsm_monitor(const struct nlm_host *host)
184 nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; 184 nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
185 185
186 status = nsm_mon_unmon(nsm, NSMPROC_MON, &res); 186 status = nsm_mon_unmon(nsm, NSMPROC_MON, &res);
187 if (res.status != 0) 187 if (unlikely(res.status != 0))
188 status = -EIO; 188 status = -EIO;
189 if (status < 0) 189 if (unlikely(status < 0)) {
190 printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name); 190 printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
191 else 191 return status;
192 nsm->sm_monitored = 1; 192 }
193 return status; 193
194 nsm->sm_monitored = 1;
195 if (unlikely(nsm_local_state != res.state)) {
196 nsm_local_state = res.state;
197 dprintk("lockd: NSM state changed to %d\n", nsm_local_state);
198 }
199 return 0;
194} 200}
195 201
196/** 202/**
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index 51855dfd8adb..c325b187966b 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -195,7 +195,7 @@ extern struct svc_procedure nlmsvc_procedures4[];
195extern int nlmsvc_grace_period; 195extern int nlmsvc_grace_period;
196extern unsigned long nlmsvc_timeout; 196extern unsigned long nlmsvc_timeout;
197extern int nsm_use_hostnames; 197extern int nsm_use_hostnames;
198extern int nsm_local_state; 198extern u32 nsm_local_state;
199 199
200/* 200/*
201 * Lockd client functions 201 * Lockd client functions