From b3d47676d474ecd914c72049c87e71e5f0ffe040 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 20 Oct 2008 13:01:59 -0400 Subject: nfsd: update fh_verify description Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index f0da7d9..019a8a2 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -258,14 +258,32 @@ out: return error; } -/* - * Perform sanity checks on the dentry in a client's file handle. +/** + * fh_verify - filehandle lookup and access checking + * @rqstp: pointer to current rpc request + * @fhp: filehandle to be verified + * @type: expected type of object pointed to by filehandle + * @access: type of access needed to object + * + * Look up a dentry from the on-the-wire filehandle, check the client's + * access to the export, and set the current task's credentials. + * + * Regardless of success or failure of fh_verify(), fh_put() should be + * called on @fhp when the caller is finished with the filehandle. + * + * fh_verify() may be called multiple times on a given filehandle, for + * example, when processing an NFSv4 compound. The first call will look + * up a dentry using the on-the-wire filehandle. Subsequent calls will + * skip the lookup and just perform the other checks and possibly change + * the current task's credentials. * - * Note that the file handle dentry may need to be freed even after - * an error return. + * @type specifies the type of object expected using one of the S_IF* + * constants defined in include/linux/stat.h. The caller may use zero + * to indicate that it doesn't care, or a negative integer to indicate + * that it expects something not of the given type. * - * This is only called at the start of an nfsproc call, so fhp points to - * a svc_fh which is all 0 except for the over-the-wire file handle. + * @access is formed from the NFSD_MAY_* constants defined in + * include/linux/nfsd/nfsd.h. */ __be32 fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) -- cgit v0.10.2 From 9346eff0dea1e5855fba25c9fe639d92a4db3135 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Oct 2008 11:44:28 +0530 Subject: nfsd: Minor cleanup of find_stateid Minor cleanup/rewrite of find_stateid. Compile tested. Signed-off-by: Krishna Kumar Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 13e0e07..06b89df 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2435,13 +2435,13 @@ static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags) { - struct nfs4_stateid *local = NULL; + struct nfs4_stateid *local; u32 st_id = stid->si_stateownerid; u32 f_id = stid->si_fileid; unsigned int hashval; dprintk("NFSD: find_stateid flags 0x%x\n",flags); - if ((flags & LOCK_STATE) || (flags & RD_STATE) || (flags & WR_STATE)) { + if (flags & (LOCK_STATE | RD_STATE | WR_STATE)) { hashval = stateid_hashval(st_id, f_id); list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { if ((local->st_stateid.si_stateownerid == st_id) && @@ -2449,7 +2449,8 @@ find_stateid(stateid_t *stid, int flags) return local; } } - if ((flags & OPEN_STATE) || (flags & RD_STATE) || (flags & WR_STATE)) { + + if (flags & (OPEN_STATE | RD_STATE | WR_STATE)) { hashval = stateid_hashval(st_id, f_id); list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { if ((local->st_stateid.si_stateownerid == st_id) && -- cgit v0.10.2 From 2bd9e7b62e6e1da3f881c40c73d93e9a212ce6de Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Oct 2008 11:47:09 +0530 Subject: nfsd: Fix leaked memory in nfs4_make_rec_clidname cksum.data is not freed up in one error case. Compile tested. Signed-off-by: Krishna Kumar Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 0f9d6ef..74f7b67 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -116,9 +116,9 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) md5_to_hex(dname, cksum.data); - kfree(cksum.data); status = nfs_ok; out: + kfree(cksum.data); crypto_free_hash(desc.tfm); out_no_tfm: return status; -- cgit v0.10.2 From 548eaca46b3cf4419b6c2be839a106d8641ffb70 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 20 Oct 2008 17:48:43 -0400 Subject: nfsd: document new filehandle fsid types Descriptions taken from mountd code (in nfs-utils/utils/mountd/cache.c). Signed-off-by: J. Bruce Fields diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index d1941cb..b2e0938 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h @@ -68,6 +68,10 @@ struct nfs_fhbase_old { * 1 - 4 byte user specified identifier * 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED * 3 - 4 byte device id, encoded for user-space, 4 byte inode number + * 4 - 4 byte inode number and 4 byte uuid + * 5 - 8 byte uuid + * 6 - 16 byte uuid + * 7 - 8 byte inode number and 16 byte uuid * * The fileid_type identified how the file within the filesystem is encoded. * This is (will be) passed to, and set by, the underlying filesystem if it supports -- cgit v0.10.2 From c9233eb7b0b11ef176d4bf68da2ce85464b6ec39 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Oct 2008 11:51:57 -0400 Subject: sunrpc: add sv_maxconn field to svc_serv (try #3) svc_check_conn_limits() attempts to prevent denial of service attacks by having the service close old connections once it reaches a threshold. This threshold is based on the number of threads in the service: (serv->sv_nrthreads + 3) * 20 Once we reach this, we drop the oldest connections and a printk pops to warn the admin that they should increase the number of threads. Increasing the number of threads isn't an option however for services like lockd. We don't want to eliminate this check entirely for such services but we need some way to increase this limit. This patch adds a sv_maxconn field to the svc_serv struct. When it's set to 0, we use the current method to calculate the max number of connections. RPC services can then set this on an as-needed basis. Signed-off-by: Jeff Layton Acked-by: Neil Brown Signed-off-by: J. Bruce Fields diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 3afe7fb..3435d24 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -58,10 +58,13 @@ struct svc_serv { struct svc_stat * sv_stats; /* RPC statistics */ spinlock_t sv_lock; unsigned int sv_nrthreads; /* # of server threads */ + unsigned int sv_maxconn; /* max connections allowed or + * '0' causing max to be based + * on number of threads. */ + unsigned int sv_max_payload; /* datagram payload size */ unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */ unsigned int sv_xdrsize; /* XDR buffer size */ - struct list_head sv_permsocks; /* all permanent sockets */ struct list_head sv_tempsocks; /* all temporary sockets */ int sv_tmpcnt; /* count of temporary sockets */ diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index bf5b5cd..3fe4f10 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -515,8 +515,10 @@ int svc_port_is_privileged(struct sockaddr *sin) } /* - * Make sure that we don't have too many active connections. If we - * have, something must be dropped. + * Make sure that we don't have too many active connections. If we have, + * something must be dropped. It's not clear what will happen if we allow + * "too many" connections, but when dealing with network-facing software, + * we have to code defensively. Here we do that by imposing hard limits. * * There's no point in trying to do random drop here for DoS * prevention. The NFS clients does 1 reconnect in 15 seconds. An @@ -525,19 +527,27 @@ int svc_port_is_privileged(struct sockaddr *sin) * The only somewhat efficient mechanism would be if drop old * connections from the same IP first. But right now we don't even * record the client IP in svc_sock. + * + * single-threaded services that expect a lot of clients will probably + * need to set sv_maxconn to override the default value which is based + * on the number of threads */ static void svc_check_conn_limits(struct svc_serv *serv) { - if (serv->sv_tmpcnt > (serv->sv_nrthreads+3)*20) { + unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn : + (serv->sv_nrthreads+3) * 20; + + if (serv->sv_tmpcnt > limit) { struct svc_xprt *xprt = NULL; spin_lock_bh(&serv->sv_lock); if (!list_empty(&serv->sv_tempsocks)) { if (net_ratelimit()) { /* Try to help the admin */ printk(KERN_NOTICE "%s: too many open " - "connections, consider increasing the " - "number of nfsd threads\n", - serv->sv_name); + "connections, consider increasing %s\n", + serv->sv_name, serv->sv_maxconn ? + "the max number of connections." : + "the number of threads."); } /* * Always select the oldest connection. It's not fair, -- cgit v0.10.2 From c72a476b4b7ecadb80185de31236edb303c1a5d0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Oct 2008 11:51:58 -0400 Subject: lockd: set svc_serv->sv_maxconn to a more reasonable value (try #3) The default method for calculating the number of connections allowed per RPC service arbitrarily limits single-threaded services to 80 connections. This is too low for services like lockd and artificially limits the number of TCP clients that it can support. Have lockd set a default sv_maxconn value to 1024 (which is the typical default value for RLIMIT_NOFILE. Also add a module parameter to allow an admin to set this to an arbitrary value. Signed-off-by: Jeff Layton Acked-by: Neil Brown Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 252d801..bc3c3cb 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -62,6 +62,9 @@ static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; static int nlm_udpport, nlm_tcpport; int nsm_use_hostnames = 0; +/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */ +static unsigned int nlm_max_connections = 1024; + /* * Constants needed for the sysctl interface. */ @@ -143,6 +146,9 @@ lockd(void *vrqstp) long timeout = MAX_SCHEDULE_TIMEOUT; RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); + /* update sv_maxconn if it has changed */ + rqstp->rq_server->sv_maxconn = nlm_max_connections; + if (signalled()) { flush_signals(current); if (nlmsvc_ops) { @@ -276,6 +282,7 @@ int lockd_up(void) } svc_sock_update_bufs(serv); + serv->sv_maxconn = nlm_max_connections; nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name); if (IS_ERR(nlmsvc_task)) { @@ -485,6 +492,7 @@ module_param_call(nlm_udpport, param_set_port, param_get_int, module_param_call(nlm_tcpport, param_set_port, param_get_int, &nlm_tcpport, 0644); module_param(nsm_use_hostnames, bool, 0644); +module_param(nlm_max_connections, uint, 0644); /* * Initialising and terminating the module. -- cgit v0.10.2 From 7538ce1eb656a1477bedd5b1c202226e7abf5e7b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:19:45 -0500 Subject: NLM: Use modern style for pointer fields in nlm_host Clean up: I'm about to add another "char *" field to the nlm_host structure. The h_name field, for example, uses an older style of declaring a "char *" field. If I match that style for the new field, checkpatch.pl will complain. So, fix pointer fields to use the new style. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 23da3fa..3dbdd35 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -43,8 +43,8 @@ struct nlm_host { struct sockaddr_storage h_addr; /* peer address */ size_t h_addrlen; struct sockaddr_storage h_srcaddr; /* our address (optional) */ - struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ - char * h_name; /* remote hostname */ + struct rpc_clnt *h_rpcclnt; /* RPC client to talk to peer */ + char *h_name; /* remote hostname */ u32 h_version; /* interface version */ unsigned short h_proto; /* transport proto */ unsigned short h_reclaiming : 1, @@ -64,7 +64,7 @@ struct nlm_host { spinlock_t h_lock; struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ - struct nsm_handle * h_nsmhandle; /* NSM status handle */ + struct nsm_handle *h_nsmhandle; /* NSM status handle */ char h_addrbuf[48], /* address eyecatchers */ h_srcaddrbuf[48]; -- cgit v0.10.2 From 1df40b609ad5a622904eb652109c287fe9c93ec5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:19:53 -0500 Subject: NLM: Remove address eye-catcher buffers from nlm_host The h_name field in struct nlm_host is a just copy of h_nsmhandle->sm_name. Likewise, the contents of the h_addrbuf field should be identical to the sm_addrbuf field. The h_srcaddrbuf field is used only in one place for debugging. We can live without this until we get %pI formatting for printk(). Currently these buffers are 48 bytes, but we need to support scope IDs in IPv6 presentation addresses, which means making the buffers even larger. Instead, let's find ways to eliminate them to save space. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index abdebf7..33bf67a 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -206,6 +206,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) goto out; } host->h_name = nsm->sm_name; + host->h_addrbuf = nsm->sm_addrbuf; memcpy(nlm_addr(host), ni->sap, ni->salen); host->h_addrlen = ni->salen; nlm_clear_port(nlm_addr(host)); @@ -232,11 +233,6 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) nrhosts++; - nlm_display_address((struct sockaddr *)&host->h_addr, - host->h_addrbuf, sizeof(host->h_addrbuf)); - nlm_display_address((struct sockaddr *)&host->h_srcaddr, - host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf)); - dprintk("lockd: nlm_lookup_host created host %s\n", host->h_name); @@ -378,8 +374,8 @@ nlm_bind_host(struct nlm_host *host) { struct rpc_clnt *clnt; - dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n", - host->h_name, host->h_addrbuf, host->h_srcaddrbuf); + dprintk("lockd: nlm_bind_host %s (%s)\n", + host->h_name, host->h_addrbuf); /* Lock host handle */ mutex_lock(&host->h_mutex); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 3dbdd35..dae22cb 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -65,9 +65,7 @@ struct nlm_host { struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ struct nsm_handle *h_nsmhandle; /* NSM status handle */ - - char h_addrbuf[48], /* address eyecatchers */ - h_srcaddrbuf[48]; + char *h_addrbuf; /* address eyecatcher */ }; struct nsm_handle { -- cgit v0.10.2 From 6999fb4016b2604c2f8a65586bba4a62a4b24ce7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:01 -0500 Subject: NLM: Remove AF_UNSPEC arm in nlm_display_address() AF_UNSPEC support is no longer needed in nlm_display_address() now that a presentation address is no longer generated for the h_srcaddr field. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 33bf67a..beb5da8 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -112,9 +112,6 @@ static void nlm_display_address(const struct sockaddr *sap, const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; switch (sap->sa_family) { - case AF_UNSPEC: - snprintf(buf, len, "unspecified"); - break; case AF_INET: snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr); break; -- cgit v0.10.2 From bc995801a09d1fead0bec1356bfd836911c8eed7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:08 -0500 Subject: NLM: Support IPv6 scope IDs in nlm_display_address() Scope ID support is needed since the kernel's NSM implementation is about to use these displayed addresses as a mon_name in some cases. When nsm_use_hostnames is zero, without scope ID support NSM will fail to handle peers that contact us via a link-local address. Link-local addresses do not work without an interface ID, which is stored in the sockaddr's sin6_scope_id field. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index beb5da8..012e49a 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -105,22 +105,31 @@ static void nlm_clear_port(struct sockaddr *sap) } } +static void nlm_display_ipv6_address(const struct sockaddr *sap, char *buf, + const size_t len) +{ + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) + snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]); + else if (sin6->sin6_scope_id != 0) + snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr, + sin6->sin6_scope_id); + else + snprintf(buf, len, "%pI6", &sin6->sin6_addr); +} + static void nlm_display_address(const struct sockaddr *sap, char *buf, const size_t len) { const struct sockaddr_in *sin = (struct sockaddr_in *)sap; - const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; switch (sap->sa_family) { case AF_INET: snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr); break; case AF_INET6: - if (ipv6_addr_v4mapped(&sin6->sin6_addr)) - snprintf(buf, len, "%pI4", - &sin6->sin6_addr.s6_addr32[3]); - else - snprintf(buf, len, "%pI6", &sin6->sin6_addr); + nlm_display_ipv6_address(sap, buf, len); break; default: snprintf(buf, len, "unsupported address family"); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index dae22cb..80a0a2c 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -68,6 +68,14 @@ struct nlm_host { char *h_addrbuf; /* address eyecatcher */ }; +/* + * The largest string sm_addrbuf should hold is a full-size IPv6 address + * (no "::" anywhere) with a scope ID. The buffer size is computed to + * hold eight groups of colon-separated four-hex-digit numbers, a + * percent sign, a scope id (at most 32 bits, in decimal), and NUL. + */ +#define NSM_ADDRBUF ((8 * 4 + 7) + (1 + 10) + 1) + struct nsm_handle { struct list_head sm_link; atomic_t sm_count; @@ -76,7 +84,7 @@ struct nsm_handle { size_t sm_addrlen; unsigned int sm_monitored : 1, sm_sticky : 1; /* don't unmonitor */ - char sm_addrbuf[48]; /* address eyecatcher */ + char sm_addrbuf[NSM_ADDRBUF]; }; /* -- cgit v0.10.2 From afb03699dc0a920aed3322ad0e6895533941fb1e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:16 -0500 Subject: NLM: Add helper to handle IPv4 addresses Clean up: introduce a helper function to generate IPv4 addresses using the same style as the IPv6 helper function we just added. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 012e49a..780918a 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -105,6 +105,13 @@ static void nlm_clear_port(struct sockaddr *sap) } } +static void nlm_display_ipv4_address(const struct sockaddr *sap, char *buf, + const size_t len) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr); +} + static void nlm_display_ipv6_address(const struct sockaddr *sap, char *buf, const size_t len) { @@ -122,11 +129,9 @@ static void nlm_display_ipv6_address(const struct sockaddr *sap, char *buf, static void nlm_display_address(const struct sockaddr *sap, char *buf, const size_t len) { - const struct sockaddr_in *sin = (struct sockaddr_in *)sap; - switch (sap->sa_family) { case AF_INET: - snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr); + nlm_display_ipv4_address(sap, buf, len); break; case AF_INET6: nlm_display_ipv6_address(sap, buf, len); -- cgit v0.10.2 From a4846750f090702e2fb848ac4fe5827bcef34060 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:23 -0500 Subject: NSM: Use C99 structure initializer to initialize nsm_args Clean up: Use a C99 structure initializer instead of open-coding the initialization of nsm_args. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index ffd3461..6f6ff41 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -37,7 +37,13 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) { struct rpc_clnt *clnt; int status; - struct nsm_args args; + struct nsm_args args = { + .addr = nsm_addr_in(nsm)->sin_addr.s_addr, + .prog = NLM_PROGRAM, + .vers = 3, + .proc = NLMPROC_NSM_NOTIFY, + .mon_name = nsm->sm_name, + }; struct rpc_message msg = { .rpc_argp = &args, .rpc_resp = res, @@ -49,12 +55,6 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) goto out; } - memset(&args, 0, sizeof(args)); - args.mon_name = nsm->sm_name; - args.addr = nsm_addr_in(nsm)->sin_addr.s_addr; - args.prog = NLM_PROGRAM; - args.vers = 3; - args.proc = NLMPROC_NSM_NOTIFY; memset(res, 0, sizeof(*res)); msg.rpc_proc = &clnt->cl_procinfo[proc]; -- cgit v0.10.2 From 5acf43155d1bcc412d892c73f64044f9a826cde6 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:31 -0500 Subject: NSM: convert printk(KERN_DEBUG) to a dprintk() Clean up: make the printk(KERN_DEBUG) in nsm_mon_unmon() a dprintk, and add another dprintk to note if creating an RPC client for the upcall failed. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 6f6ff41..497dfea 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -52,6 +52,8 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) clnt = nsm_create(); if (IS_ERR(clnt)) { status = PTR_ERR(clnt); + dprintk("lockd: failed to create NSM upcall transport, " + "status=%d\n", status); goto out; } @@ -60,8 +62,8 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) msg.rpc_proc = &clnt->cl_procinfo[proc]; status = rpc_call_sync(clnt, &msg, 0); if (status < 0) - printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n", - status); + dprintk("lockd: NSM upcall RPC failed, status=%d\n", + status); else status = 0; rpc_shutdown_client(clnt); -- cgit v0.10.2 From f47534f7f0ac7727e05ec4274b764b181df2cf7f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:38 -0500 Subject: NSM: Use modern style for sm_name field in nsm_handle Clean up: I'm about to add another "char *" field to the nsm_handle structure. The sm_name field uses an older style of declaring a "char *" field. If I match that style for the new field, checkpatch.pl will complain. So, fix the sm_name field to use the new style. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 80a0a2c..54dbb45 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -79,7 +79,7 @@ struct nlm_host { struct nsm_handle { struct list_head sm_link; atomic_t sm_count; - char * sm_name; + char *sm_name; struct sockaddr_storage sm_addr; size_t sm_addrlen; unsigned int sm_monitored : 1, -- cgit v0.10.2 From 29ed1407ed81086b778ebf12145b048ac3f7e10e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:46 -0500 Subject: NSM: Support IPv6 version of mon_name The "mon_name" argument of the NSMPROC_MON and NSMPROC_UNMON upcalls is a string that contains the hostname or IP address of the remote peer to be notified when this host has rebooted. The sm-notify command uses this identifier to contact the peer when we reboot, so it must be either a well-qualified DNS hostname or a presentation format IP address string. When the "nsm_use_hostnames" sysctl is set to zero, the kernel's NSM provides a presentation format IP address in the "mon_name" argument. Otherwise, the "caller_name" argument from NLM requests is used, which is usually just the DNS hostname of the peer. To support IPv6 addresses for the mon_name argument, we use the nsm_handle's address eye-catcher, which already contains an appropriate presentation format address string. Using the eye-catcher string obviates the need to use a large buffer on the stack to form the presentation address string for the upcall. This patch also addresses a subtle bug. An NSMPROC_MON request and the subsequent NSMPROC_UNMON request for the same peer are required to use the same value for the "mon_name" argument. Otherwise, rpc.statd's NSMPROC_UNMON processing cannot locate the database entry for that peer and remove it. If the setting of nsm_use_hostnames is changed between the time the kernel sends an NSMPROC_MON request and the time it sends the NSMPROC_UNMON request for the same peer, the "mon_name" argument for these two requests may not be the same. This is because the value of "mon_name" is currently chosen at the moment the call is made based on the setting of nsm_use_hostnames To ensure both requests pass identical contents in the "mon_name" argument, we now select which string to use for the argument in the nsm_monitor() function. A pointer to this string is saved in the nsm_handle so it can be used for a subsequent NSMPROC_UNMON upcall. NB: There are other potential problems, such as how nlm_host_rebooted() might behave if nsm_use_hostnames were changed while hosts are still being monitored. This patch does not attempt to address those problems. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 497dfea..a606fbb 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -18,8 +18,6 @@ #define NLMDBG_FACILITY NLMDBG_MONITOR -#define XDR_ADDRBUF_LEN (20) - static struct rpc_clnt * nsm_create(void); static struct rpc_program nsm_program; @@ -42,7 +40,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) .prog = NLM_PROGRAM, .vers = 3, .proc = NLMPROC_NSM_NOTIFY, - .mon_name = nsm->sm_name, + .mon_name = nsm->sm_mon_name, }; struct rpc_message msg = { .rpc_argp = &args, @@ -87,6 +85,12 @@ nsm_monitor(struct nlm_host *host) if (nsm->sm_monitored) return 0; + /* + * Choose whether to record the caller_name or IP address of + * this peer in the local rpc.statd's database. + */ + nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; + status = nsm_mon_unmon(nsm, SM_MON, &res); if (status < 0 || res.status != 0) @@ -167,25 +171,10 @@ static __be32 *xdr_encode_nsm_string(__be32 *p, char *string) /* * "mon_name" specifies the host to be monitored. - * - * Linux uses a text version of the IP address of the remote - * host as the host identifier (the "mon_name" argument). - * - * Linux statd always looks up the canonical hostname first for - * whatever remote hostname it receives, so this works alright. */ static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp) { - char buffer[XDR_ADDRBUF_LEN + 1]; - char *name = argp->mon_name; - - if (!nsm_use_hostnames) { - snprintf(buffer, XDR_ADDRBUF_LEN, - "%pI4", &argp->addr); - name = buffer; - } - - return xdr_encode_nsm_string(p, name); + return xdr_encode_nsm_string(p, argp->mon_name); } /* diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 54dbb45..d3c7247 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -79,6 +79,7 @@ struct nlm_host { struct nsm_handle { struct list_head sm_link; atomic_t sm_count; + char *sm_mon_name; char *sm_name; struct sockaddr_storage sm_addr; size_t sm_addrlen; -- cgit v0.10.2 From 9fee49024ed19d849413df4ab6ec1a1a60aaae94 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:20:53 -0500 Subject: NSM: Use sm_name instead of h_name in nsm_monitor() and nsm_unmonitor() Clean up: Use the sm_name field for reporting the hostname in nsm_monitor() and nsm_unmonitor(), just as the other functions in fs/lockd/mon.c do. The h_name field is just a copy of the sm_name pointer. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index a606fbb..697bdcd 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -79,7 +79,7 @@ nsm_monitor(struct nlm_host *host) struct nsm_res res; int status; - dprintk("lockd: nsm_monitor(%s)\n", host->h_name); + dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name); BUG_ON(nsm == NULL); if (nsm->sm_monitored) @@ -94,7 +94,7 @@ nsm_monitor(struct nlm_host *host) status = nsm_mon_unmon(nsm, SM_MON, &res); if (status < 0 || res.status != 0) - printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name); + printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name); else nsm->sm_monitored = 1; return status; @@ -116,12 +116,12 @@ nsm_unmonitor(struct nlm_host *host) if (atomic_read(&nsm->sm_count) == 1 && nsm->sm_monitored && !nsm->sm_sticky) { - dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name); + dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); status = nsm_mon_unmon(nsm, SM_UNMON, &res); if (status < 0) printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", - host->h_name); + nsm->sm_name); else nsm->sm_monitored = 0; } -- cgit v0.10.2 From 501c1ed3fb5c2648ba1709282c71617910917f66 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:01 -0500 Subject: NLM: Remove redundant printk() in nlmclnt_lock() The nsm_monitor() function already generates a printk(KERN_NOTICE) if the SM_MON upcall fails, so the similar printk() in the nlmclnt_lock() function is redundant. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 31668b6..5ce42e0 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -518,11 +518,9 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) unsigned char fl_type; int status = -ENOLCK; - if (nsm_monitor(host) < 0) { - printk(KERN_NOTICE "lockd: failed to monitor %s\n", - host->h_name); + if (nsm_monitor(host) < 0) goto out; - } + fl->fl_flags |= FL_ACCESS; status = do_vfs_lock(fl); fl->fl_flags = fl_flags; -- cgit v0.10.2 From 5bc74bef7c9b652f0f2aa9c5a8d5ac86881aba79 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:08 -0500 Subject: NSM: Remove BUG_ON() in nsm_monitor() Clean up: Remove the BUG_ON() invocation in nsm_monitor(). It's not likely that nsm_monitor() is ever called with a NULL host pointer, and the code will die anyway if host is NULL. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 697bdcd..bb5fc1b 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -80,7 +80,6 @@ nsm_monitor(struct nlm_host *host) int status; dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name); - BUG_ON(nsm == NULL); if (nsm->sm_monitored) return 0; -- cgit v0.10.2 From 5d254b119823658cc318f88589c6c426b3d0a153 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:15 -0500 Subject: NSM: Make sure to return an error if the SM_MON call result is not zero The nsm_monitor() function reports an error and does not set sm_monitored if the SM_MON upcall reply has a non-zero result code, but nsm_monitor() does not return an error to its caller in this case. Since sm_monitored is not set, the upcall is retried when the next NLM request invokes nsm_monitor(). However, that may not come for a while. In the meantime, at least one NLM request will potentially proceed without the peer being monitored properly. Have nsm_monitor() return an error if the result code is non-zero. This will cause all NLM requests to fail immediately if the upcall completed successfully but rpc.statd returned an error. This may be inconvenient in some cases (for example if rpc.statd cannot complete a proper DNS reverse lookup of the hostname), but will make the reboot monitoring service more robust by forcing such issues to be corrected by an admin. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index bb5fc1b..07e16b8 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -91,8 +91,9 @@ nsm_monitor(struct nlm_host *host) nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; status = nsm_mon_unmon(nsm, SM_MON, &res); - - if (status < 0 || res.status != 0) + if (res.status != 0) + status = -EIO; + if (status < 0) printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name); else nsm->sm_monitored = 1; -- cgit v0.10.2 From 1e49323c4ab044d05bbc68cf13cadcbd4372468c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:24 -0500 Subject: NLM: Move the public declaration of nsm_monitor() to lockd.h Clean up. Make the nlm_host argument "const," and move the public declaration to lockd.h with other NSM public function (nsm_release, eg) and global variable declarations. Add a documenting comment. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 07e16b8..aaaa08e 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -69,11 +69,18 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) return status; } -/* - * Set up monitoring of a remote host +/** + * nsm_monitor - Notify a peer in case we reboot + * @host: pointer to nlm_host of peer to notify + * + * If this peer is not already monitored, this function sends an + * upcall to the local rpc.statd to record the name/address of + * the peer to notify in case we reboot. + * + * Returns zero if the peer is monitored by the local rpc.statd; + * otherwise a negative errno value is returned. */ -int -nsm_monitor(struct nlm_host *host) +int nsm_monitor(const struct nlm_host *host) { struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_res res; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index d3c7247..f15a4f5 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -242,6 +242,10 @@ extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, unsigned int, u32); void nsm_release(struct nsm_handle *); +/* + * Host monitoring + */ +int nsm_monitor(const struct nlm_host *host); /* * This is used in garbage collection and resource reclaim diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h index 5a5448b..546b6102 100644 --- a/include/linux/lockd/sm_inter.h +++ b/include/linux/lockd/sm_inter.h @@ -41,7 +41,6 @@ struct nsm_res { u32 state; }; -int nsm_monitor(struct nlm_host *); int nsm_unmonitor(struct nlm_host *); extern int nsm_local_state; -- cgit v0.10.2 From c8c23c423dec49cb439697d3dc714e1500ff1610 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:31 -0500 Subject: NSM: Release nsmhandle in nlm_destroy_host The nsm_handle's reference count is bumped in nlm_lookup_host(). It should be decremented in nlm_destroy_host() to make it easier to see the balance of these two operations. Move the nsm_release() call to fs/lockd/host.c. The h_nsmhandle pointer is set in nlm_lookup_host(), and never cleared. The nlm_destroy_host() function is never called for the same nlm_host twice, so h_nsmhandle won't ever be NULL when nsm_unmonitor() is called. All references to the nlm_host are gone before it is freed. We can skip making h_nsmhandle NULL just before the nlm_host is deallocated. It's also likely we can remove the h_nsmhandle NULL check in nlmsvc_is_client() as well, but we can do that later when rearchitect- ing the nlm_host cache. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 780918a..1d523c1 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -37,6 +37,7 @@ static struct nsm_handle *nsm_find(const struct sockaddr *sap, const char *hostname, const size_t hostname_len, const int create); +static void nsm_release(struct nsm_handle *nsm); struct nlm_lookup_host_info { const int server; /* search for server|client */ @@ -263,10 +264,8 @@ nlm_destroy_host(struct nlm_host *host) BUG_ON(!list_empty(&host->h_lockowners)); BUG_ON(atomic_read(&host->h_count)); - /* - * Release NSM handle and unmonitor host. - */ nsm_unmonitor(host); + nsm_release(host->h_nsmhandle); clnt = host->h_rpcclnt; if (clnt != NULL) @@ -711,8 +710,7 @@ found: /* * Release an NSM handle */ -void -nsm_release(struct nsm_handle *nsm) +static void nsm_release(struct nsm_handle *nsm) { if (!nsm) return; diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index aaaa08e..15fab22 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -117,10 +117,6 @@ nsm_unmonitor(struct nlm_host *host) struct nsm_res res; int status = 0; - if (nsm == NULL) - return 0; - host->h_nsmhandle = NULL; - if (atomic_read(&nsm->sm_count) == 1 && nsm->sm_monitored && !nsm->sm_sticky) { dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); @@ -132,7 +128,6 @@ nsm_unmonitor(struct nlm_host *host) else nsm->sm_monitored = 0; } - nsm_release(nsm); return status; } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index f15a4f5..30a6a9c 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -240,7 +240,6 @@ void nlm_release_host(struct nlm_host *); void nlm_shutdown_hosts(void); extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, unsigned int, u32); -void nsm_release(struct nsm_handle *); /* * Host monitoring -- cgit v0.10.2 From 356c3eb466fd1a12afd6448d90fba3922836e5f1 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:38 -0500 Subject: NLM: Move the public declaration of nsm_unmonitor() to lockd.h Clean up. Make the nlm_host argument "const," and move the public declaration to lockd.h. Add a documenting comment. Bruce observed that nsm_unmonitor()'s only caller doesn't care about its return code, so make nsm_unmonitor() return void. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 15fab22..d61cdc6 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -107,15 +107,19 @@ int nsm_monitor(const struct nlm_host *host) return status; } -/* - * Cease to monitor remote host +/** + * nsm_unmonitor - Unregister peer notification + * @host: pointer to nlm_host of peer to stop monitoring + * + * If this peer is monitored, this function sends an upcall to + * tell the local rpc.statd not to send this peer a notification + * when we reboot. */ -int -nsm_unmonitor(struct nlm_host *host) +void nsm_unmonitor(const struct nlm_host *host) { struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_res res; - int status = 0; + int status; if (atomic_read(&nsm->sm_count) == 1 && nsm->sm_monitored && !nsm->sm_sticky) { @@ -128,7 +132,6 @@ nsm_unmonitor(struct nlm_host *host) else nsm->sm_monitored = 0; } - return status; } /* diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 30a6a9c..38344bf 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -245,6 +245,7 @@ extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, * Host monitoring */ int nsm_monitor(const struct nlm_host *host); +void nsm_unmonitor(const struct nlm_host *host); /* * This is used in garbage collection and resource reclaim diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h index 546b6102..896a5e3 100644 --- a/include/linux/lockd/sm_inter.h +++ b/include/linux/lockd/sm_inter.h @@ -41,7 +41,6 @@ struct nsm_res { u32 state; }; -int nsm_unmonitor(struct nlm_host *); extern int nsm_local_state; #endif /* LINUX_LOCKD_SM_INTER_H */ -- cgit v0.10.2 From 0c7aef4569f8680951b7dee01dddffb9d2f809ff Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Dec 2008 14:21:46 -0500 Subject: NSM: Check result of SM_UNMON upcall Make sure any error returned by rpc.statd during an SM_UNMON call is reported rather than ignored completely. There isn't much to do with such an error, but we should log it in any case. Similar to a recent change to nsm_monitor(). Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index d61cdc6..3bb71e1 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -126,6 +126,8 @@ void nsm_unmonitor(const struct nlm_host *host) dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); status = nsm_mon_unmon(nsm, SM_UNMON, &res); + if (res.status != 0) + status = -EIO; if (status < 0) printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", nsm->sm_name); -- cgit v0.10.2 From 9c1bfd037f7ff8badaecb47418f109148d88bf45 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:01:59 -0500 Subject: NSM: Move NSM-related XDR data structures to lockd's xdr.h Clean up: NSM's XDR data structures are used only in fs/lockd/mon.c, so move them there. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 3bb71e1..8130883 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -18,6 +18,20 @@ #define NLMDBG_FACILITY NLMDBG_MONITOR +struct nsm_args { + __be32 addr; /* remote address */ + u32 prog; /* RPC callback info */ + u32 vers; + u32 proc; + + char *mon_name; +}; + +struct nsm_res { + u32 status; + u32 state; +}; + static struct rpc_clnt * nsm_create(void); static struct rpc_program nsm_program; diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h index 896a5e3..dd9d8a5 100644 --- a/include/linux/lockd/sm_inter.h +++ b/include/linux/lockd/sm_inter.h @@ -21,26 +21,6 @@ #define SM_MAXSTRLEN 1024 #define SM_PRIV_SIZE 16 -/* - * Arguments for all calls to statd - */ -struct nsm_args { - __be32 addr; /* remote address */ - u32 prog; /* RPC callback info */ - u32 vers; - u32 proc; - - char * mon_name; -}; - -/* - * Result returned by statd - */ -struct nsm_res { - u32 status; - u32 state; -}; - extern int nsm_local_state; #endif /* LINUX_LOCKD_SM_INTER_H */ -- cgit v0.10.2 From 36e8e668d3e6a61848a8921ddeb663b417299fa5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:02:07 -0500 Subject: NSM: Move NSM program and procedure numbers to fs/lockd/mon.c Clean up: Move the RPC program and procedure numbers for NSM into the one source file that needs them: fs/lockd/mon.c. And, as with NLM, NFS, and rpcbind calls, use NSMPROC_FOO instead of SM_FOO for NSM procedure numbers. Finally, make a couple of comments more precise: what is referred to here as SM_NOTIFY is really the NLM (lockd) NLMPROC_SM_NOTIFY downcall, not NSMPROC_NOTIFY. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 8130883..0fc9836 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -17,6 +17,18 @@ #define NLMDBG_FACILITY NLMDBG_MONITOR +#define NSM_PROGRAM 100024 +#define NSM_VERSION 1 + +enum { + NSMPROC_NULL, + NSMPROC_STAT, + NSMPROC_MON, + NSMPROC_UNMON, + NSMPROC_UNMON_ALL, + NSMPROC_SIMU_CRASH, + NSMPROC_NOTIFY, +}; struct nsm_args { __be32 addr; /* remote address */ @@ -42,7 +54,7 @@ static struct rpc_program nsm_program; int nsm_local_state; /* - * Common procedure for SM_MON/SM_UNMON calls + * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls */ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) @@ -111,7 +123,7 @@ int nsm_monitor(const struct nlm_host *host) */ nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; - status = nsm_mon_unmon(nsm, SM_MON, &res); + status = nsm_mon_unmon(nsm, NSMPROC_MON, &res); if (res.status != 0) status = -EIO; if (status < 0) @@ -139,7 +151,7 @@ void nsm_unmonitor(const struct nlm_host *host) && nsm->sm_monitored && !nsm->sm_sticky) { dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); - status = nsm_mon_unmon(nsm, SM_UNMON, &res); + status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res); if (res.status != 0) status = -EIO; if (status < 0) @@ -167,7 +179,7 @@ nsm_create(void) .addrsize = sizeof(sin), .servername = "localhost", .program = &nsm_program, - .version = SM_VERSION, + .version = NSM_VERSION, .authflavor = RPC_AUTH_NULL, }; @@ -201,7 +213,7 @@ static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp) /* * The "my_id" argument specifies the hostname and RPC procedure * to be called when the status manager receives notification - * (via the SM_NOTIFY call) that the state of host "mon_name" + * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name" * has changed. */ static __be32 *xdr_encode_my_id(__be32 *p, struct nsm_args *argp) @@ -219,7 +231,7 @@ static __be32 *xdr_encode_my_id(__be32 *p, struct nsm_args *argp) /* * The "mon_id" argument specifies the non-private arguments - * of an SM_MON or SM_UNMON call. + * of an NSMPROC_MON or NSMPROC_UNMON call. */ static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp) { @@ -232,8 +244,8 @@ static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp) /* * The "priv" argument may contain private information required - * by the SM_MON call. This information will be supplied in the - * SM_NOTIFY call. + * by the NSMPROC_MON call. This information will be supplied in the + * NLMPROC_SM_NOTIFY call. * * Linux provides the raw IP address of the monitored host, * left in network byte order. @@ -300,22 +312,22 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) #define SM_unmonres_sz 1 static struct rpc_procinfo nsm_procedures[] = { -[SM_MON] = { - .p_proc = SM_MON, +[NSMPROC_MON] = { + .p_proc = NSMPROC_MON, .p_encode = (kxdrproc_t) xdr_encode_mon, .p_decode = (kxdrproc_t) xdr_decode_stat_res, .p_arglen = SM_mon_sz, .p_replen = SM_monres_sz, - .p_statidx = SM_MON, + .p_statidx = NSMPROC_MON, .p_name = "MONITOR", }, -[SM_UNMON] = { - .p_proc = SM_UNMON, +[NSMPROC_UNMON] = { + .p_proc = NSMPROC_UNMON, .p_encode = (kxdrproc_t) xdr_encode_unmon, .p_decode = (kxdrproc_t) xdr_decode_stat, .p_arglen = SM_mon_id_sz, .p_replen = SM_unmonres_sz, - .p_statidx = SM_UNMON, + .p_statidx = NSMPROC_UNMON, .p_name = "UNMONITOR", }, }; @@ -334,7 +346,7 @@ static struct rpc_stat nsm_stats; static struct rpc_program nsm_program = { .name = "statd", - .number = SM_PROGRAM, + .number = NSM_PROGRAM, .nrvers = ARRAY_SIZE(nsm_version), .version = nsm_version, .stats = &nsm_stats diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h index dd9d8a5..116bf38 100644 --- a/include/linux/lockd/sm_inter.h +++ b/include/linux/lockd/sm_inter.h @@ -9,15 +9,6 @@ #ifndef LINUX_LOCKD_SM_INTER_H #define LINUX_LOCKD_SM_INTER_H -#define SM_PROGRAM 100024 -#define SM_VERSION 1 -#define SM_STAT 1 -#define SM_MON 2 -#define SM_UNMON 3 -#define SM_UNMON_ALL 4 -#define SM_SIMU_CRASH 5 -#define SM_NOTIFY 6 - #define SM_MAXSTRLEN 1024 #define SM_PRIV_SIZE 16 -- cgit v0.10.2 From 03eb1dcbb799304b58730f4dba65812f49fb305e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:02:15 -0500 Subject: NSM: move to xdr_stream-based XDR encoders and decoders Introduce xdr_stream-based XDR encoder and decoder functions, which are more careful about preventing RPC buffer overflows. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 0fc9836..81e1cc1 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -193,21 +193,26 @@ nsm_create(void) * Status Monitor wire protocol. */ -static __be32 *xdr_encode_nsm_string(__be32 *p, char *string) +static int encode_nsm_string(struct xdr_stream *xdr, const char *string) { - size_t len = strlen(string); - - if (len > SM_MAXSTRLEN) - len = SM_MAXSTRLEN; - return xdr_encode_opaque(p, string, len); + const u32 len = strlen(string); + __be32 *p; + + if (unlikely(len > SM_MAXSTRLEN)) + return -EIO; + p = xdr_reserve_space(xdr, sizeof(u32) + len); + if (unlikely(p == NULL)) + return -EIO; + xdr_encode_opaque(p, string, len); + return 0; } /* * "mon_name" specifies the host to be monitored. */ -static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp) +static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp) { - return xdr_encode_nsm_string(p, argp->mon_name); + return encode_nsm_string(xdr, argp->mon_name); } /* @@ -216,30 +221,35 @@ static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp) * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name" * has changed. */ -static __be32 *xdr_encode_my_id(__be32 *p, struct nsm_args *argp) +static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp) { - p = xdr_encode_nsm_string(p, utsname()->nodename); - if (!p) - return ERR_PTR(-EIO); - + int status; + __be32 *p; + + status = encode_nsm_string(xdr, utsname()->nodename); + if (unlikely(status != 0)) + return status; + p = xdr_reserve_space(xdr, 3 * sizeof(u32)); + if (unlikely(p == NULL)) + return -EIO; *p++ = htonl(argp->prog); *p++ = htonl(argp->vers); *p++ = htonl(argp->proc); - - return p; + return 0; } /* * The "mon_id" argument specifies the non-private arguments * of an NSMPROC_MON or NSMPROC_UNMON call. */ -static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp) +static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp) { - p = xdr_encode_mon_name(p, argp); - if (!p) - return ERR_PTR(-EIO); + int status; - return xdr_encode_my_id(p, argp); + status = encode_mon_name(xdr, argp); + if (unlikely(status != 0)) + return status; + return encode_my_id(xdr, argp); } /* @@ -250,55 +260,71 @@ static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp) * Linux provides the raw IP address of the monitored host, * left in network byte order. */ -static __be32 *xdr_encode_priv(__be32 *p, struct nsm_args *argp) +static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp) { + __be32 *p; + + p = xdr_reserve_space(xdr, SM_PRIV_SIZE); + if (unlikely(p == NULL)) + return -EIO; *p++ = argp->addr; *p++ = 0; *p++ = 0; *p++ = 0; - - return p; + return 0; } -static int -xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp) +static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p, + const struct nsm_args *argp) { - p = xdr_encode_mon_id(p, argp); - if (IS_ERR(p)) - return PTR_ERR(p); - - p = xdr_encode_priv(p, argp); - if (IS_ERR(p)) - return PTR_ERR(p); + struct xdr_stream xdr; + int status; - rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); - return 0; + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + status = encode_mon_id(&xdr, argp); + if (unlikely(status)) + return status; + return encode_priv(&xdr, argp); } -static int -xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp) +static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p, + const struct nsm_args *argp) { - p = xdr_encode_mon_id(p, argp); - if (IS_ERR(p)) - return PTR_ERR(p); - rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); - return 0; + struct xdr_stream xdr; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + return encode_mon_id(&xdr, argp); } -static int -xdr_decode_stat_res(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) +static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p, + struct nsm_res *resp) { + struct xdr_stream xdr; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + p = xdr_inline_decode(&xdr, 2 * sizeof(u32)); + if (unlikely(p == NULL)) + return -EIO; resp->status = ntohl(*p++); - resp->state = ntohl(*p++); - dprintk("nsm: xdr_decode_stat_res status %d state %d\n", + resp->state = ntohl(*p); + + dprintk("lockd: xdr_dec_stat_res status %d state %d\n", resp->status, resp->state); return 0; } -static int -xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) +static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p, + struct nsm_res *resp) { - resp->state = ntohl(*p++); + struct xdr_stream xdr; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + p = xdr_inline_decode(&xdr, sizeof(u32)); + if (unlikely(p == NULL)) + return -EIO; + resp->state = ntohl(*p); + + dprintk("lockd: xdr_dec_stat state %d\n", resp->state); return 0; } @@ -314,8 +340,8 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) static struct rpc_procinfo nsm_procedures[] = { [NSMPROC_MON] = { .p_proc = NSMPROC_MON, - .p_encode = (kxdrproc_t) xdr_encode_mon, - .p_decode = (kxdrproc_t) xdr_decode_stat_res, + .p_encode = (kxdrproc_t)xdr_enc_mon, + .p_decode = (kxdrproc_t)xdr_dec_stat_res, .p_arglen = SM_mon_sz, .p_replen = SM_monres_sz, .p_statidx = NSMPROC_MON, @@ -323,8 +349,8 @@ static struct rpc_procinfo nsm_procedures[] = { }, [NSMPROC_UNMON] = { .p_proc = NSMPROC_UNMON, - .p_encode = (kxdrproc_t) xdr_encode_unmon, - .p_decode = (kxdrproc_t) xdr_decode_stat, + .p_encode = (kxdrproc_t)xdr_enc_unmon, + .p_decode = (kxdrproc_t)xdr_dec_stat, .p_arglen = SM_mon_id_sz, .p_replen = SM_unmonres_sz, .p_statidx = NSMPROC_UNMON, -- cgit v0.10.2 From 67c6d107a689243979a2b5f15244b5261634a924 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:02:45 -0500 Subject: NSM: Move nsm_find() to fs/lockd/mon.c The nsm_find() function sets up fresh nsm_handle entries. This is where we will store the "priv" cookie used to lookup nsm_handles during reboot recovery. The cookie will be constructed when nsm_find() creates a new nsm_handle. As much as possible, I would like to keep everything that handles a "priv" cookie in fs/lockd/mon.c so that all the smarts are in one source file. That organization should make it pretty simple to see how all this works. To me, it makes more sense than the current arrangement to keep nsm_find() with nsm_monitor() and nsm_unmonitor(). So, start reorganizing by moving nsm_find() into fs/lockd/mon.c. The nsm_release() function comes along too, since it shares the nsm_lock global variable. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 1d523c1..dbdeaa8 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -32,12 +32,6 @@ static int nrhosts; static DEFINE_MUTEX(nlm_host_mutex); static void nlm_gc_hosts(void); -static struct nsm_handle *nsm_find(const struct sockaddr *sap, - const size_t salen, - const char *hostname, - const size_t hostname_len, - const int create); -static void nsm_release(struct nsm_handle *nsm); struct nlm_lookup_host_info { const int server; /* search for server|client */ @@ -106,43 +100,6 @@ static void nlm_clear_port(struct sockaddr *sap) } } -static void nlm_display_ipv4_address(const struct sockaddr *sap, char *buf, - const size_t len) -{ - const struct sockaddr_in *sin = (struct sockaddr_in *)sap; - snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr); -} - -static void nlm_display_ipv6_address(const struct sockaddr *sap, char *buf, - const size_t len) -{ - const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; - - if (ipv6_addr_v4mapped(&sin6->sin6_addr)) - snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]); - else if (sin6->sin6_scope_id != 0) - snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr, - sin6->sin6_scope_id); - else - snprintf(buf, len, "%pI6", &sin6->sin6_addr); -} - -static void nlm_display_address(const struct sockaddr *sap, - char *buf, const size_t len) -{ - switch (sap->sa_family) { - case AF_INET: - nlm_display_ipv4_address(sap, buf, len); - break; - case AF_INET6: - nlm_display_ipv6_address(sap, buf, len); - break; - default: - snprintf(buf, len, "unsupported address family"); - break; - } -} - /* * Common host lookup routine for server & client */ @@ -635,88 +592,3 @@ nlm_gc_hosts(void) next_gc = jiffies + NLM_HOST_COLLECT; } - - -/* - * Manage NSM handles - */ -static LIST_HEAD(nsm_handles); -static DEFINE_SPINLOCK(nsm_lock); - -static struct nsm_handle *nsm_find(const struct sockaddr *sap, - const size_t salen, - const char *hostname, - const size_t hostname_len, - const int create) -{ - struct nsm_handle *nsm = NULL; - struct nsm_handle *pos; - - if (!sap) - return NULL; - - if (hostname && memchr(hostname, '/', hostname_len) != NULL) { - if (printk_ratelimit()) { - printk(KERN_WARNING "Invalid hostname \"%.*s\" " - "in NFS lock request\n", - (int)hostname_len, hostname); - } - return NULL; - } - -retry: - spin_lock(&nsm_lock); - list_for_each_entry(pos, &nsm_handles, sm_link) { - - if (hostname && nsm_use_hostnames) { - if (strlen(pos->sm_name) != hostname_len - || memcmp(pos->sm_name, hostname, hostname_len)) - continue; - } else if (!nlm_cmp_addr(nsm_addr(pos), sap)) - continue; - atomic_inc(&pos->sm_count); - kfree(nsm); - nsm = pos; - goto found; - } - if (nsm) { - list_add(&nsm->sm_link, &nsm_handles); - goto found; - } - spin_unlock(&nsm_lock); - - if (!create) - return NULL; - - nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); - if (nsm == NULL) - return NULL; - - memcpy(nsm_addr(nsm), sap, salen); - nsm->sm_addrlen = salen; - nsm->sm_name = (char *) (nsm + 1); - memcpy(nsm->sm_name, hostname, hostname_len); - nsm->sm_name[hostname_len] = '\0'; - nlm_display_address((struct sockaddr *)&nsm->sm_addr, - nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf)); - atomic_set(&nsm->sm_count, 1); - goto retry; - -found: - spin_unlock(&nsm_lock); - return nsm; -} - -/* - * Release an NSM handle - */ -static void nsm_release(struct nsm_handle *nsm) -{ - if (!nsm) - return; - if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) { - list_del(&nsm->sm_link); - spin_unlock(&nsm_lock); - kfree(nsm); - } -} diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 81e1cc1..8e68e799 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -47,12 +47,51 @@ struct nsm_res { static struct rpc_clnt * nsm_create(void); static struct rpc_program nsm_program; +static LIST_HEAD(nsm_handles); +static DEFINE_SPINLOCK(nsm_lock); /* * Local NSM state */ int nsm_local_state; +static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf, + const size_t len) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr); +} + +static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf, + const size_t len) +{ + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) + snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]); + else if (sin6->sin6_scope_id != 0) + snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr, + sin6->sin6_scope_id); + else + snprintf(buf, len, "%pI6", &sin6->sin6_addr); +} + +static void nsm_display_address(const struct sockaddr *sap, + char *buf, const size_t len) +{ + switch (sap->sa_family) { + case AF_INET: + nsm_display_ipv4_address(sap, buf, len); + break; + case AF_INET6: + nsm_display_ipv6_address(sap, buf, len); + break; + default: + snprintf(buf, len, "unsupported address family"); + break; + } +} + /* * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls */ @@ -162,6 +201,100 @@ void nsm_unmonitor(const struct nlm_host *host) } } +/** + * nsm_find - Find or create a cached nsm_handle + * @sap: pointer to socket address of handle to find + * @salen: length of socket address + * @hostname: pointer to C string containing hostname to find + * @hostname_len: length of C string + * @create: one means create new handle if not found in cache + * + * Behavior is modulated by the global nsm_use_hostnames variable + * and by the @create argument. + * + * Returns a cached nsm_handle after bumping its ref count, or if + * @create is set, returns a fresh nsm_handle if a handle that + * matches @sap and/or @hostname cannot be found in the handle cache. + * Returns NULL if an error occurs. + */ +struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen, + const char *hostname, const size_t hostname_len, + const int create) +{ + struct nsm_handle *nsm = NULL; + struct nsm_handle *pos; + + if (!sap) + return NULL; + + if (hostname && memchr(hostname, '/', hostname_len) != NULL) { + if (printk_ratelimit()) { + printk(KERN_WARNING "Invalid hostname \"%.*s\" " + "in NFS lock request\n", + (int)hostname_len, hostname); + } + return NULL; + } + +retry: + spin_lock(&nsm_lock); + list_for_each_entry(pos, &nsm_handles, sm_link) { + + if (hostname && nsm_use_hostnames) { + if (strlen(pos->sm_name) != hostname_len + || memcmp(pos->sm_name, hostname, hostname_len)) + continue; + } else if (!nlm_cmp_addr(nsm_addr(pos), sap)) + continue; + atomic_inc(&pos->sm_count); + kfree(nsm); + nsm = pos; + goto found; + } + if (nsm) { + list_add(&nsm->sm_link, &nsm_handles); + goto found; + } + spin_unlock(&nsm_lock); + + if (!create) + return NULL; + + nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); + if (nsm == NULL) + return NULL; + + memcpy(nsm_addr(nsm), sap, salen); + nsm->sm_addrlen = salen; + nsm->sm_name = (char *) (nsm + 1); + memcpy(nsm->sm_name, hostname, hostname_len); + nsm->sm_name[hostname_len] = '\0'; + nsm_display_address((struct sockaddr *)&nsm->sm_addr, + nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf)); + atomic_set(&nsm->sm_count, 1); + goto retry; + +found: + spin_unlock(&nsm_lock); + return nsm; +} + +/** + * nsm_release - Release an NSM handle + * @nsm: pointer to handle to be released + * + */ +void nsm_release(struct nsm_handle *nsm) +{ + if (!nsm) + return; + if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) { + list_del(&nsm->sm_link); + spin_unlock(&nsm_lock); + kfree(nsm); + } +} + /* * Create NSM client for the local host */ diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 38344bf..8d71536 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -247,6 +247,12 @@ extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, int nsm_monitor(const struct nlm_host *host); void nsm_unmonitor(const struct nlm_host *host); +struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen, + const char *hostname, + const size_t hostname_len, + const int create); +void nsm_release(struct nsm_handle *nsm); + /* * This is used in garbage collection and resource reclaim * A return value != 0 means destroy the lock/block/share -- cgit v0.10.2 From 5cf1c4b19db99d21d44c2ab457cfd44eb86b4439 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:02:53 -0500 Subject: NSM: Add dprintk() calls in nsm_find and nsm_release Introduce some dprintk() calls in fs/lockd/mon.c that are enabled by the NLMDBG_MONITOR flag. These report when we find, create, and release nsm_handles. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 8e68e799..3825545 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -249,10 +249,15 @@ retry: atomic_inc(&pos->sm_count); kfree(nsm); nsm = pos; + dprintk("lockd: found nsm_handle for %s (%s), cnt %d\n", + pos->sm_name, pos->sm_addrbuf, + atomic_read(&pos->sm_count)); goto found; } if (nsm) { list_add(&nsm->sm_link, &nsm_handles); + dprintk("lockd: created nsm_handle for %s (%s)\n", + nsm->sm_name, nsm->sm_addrbuf); goto found; } spin_unlock(&nsm_lock); @@ -291,6 +296,8 @@ void nsm_release(struct nsm_handle *nsm) if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) { list_del(&nsm->sm_link); spin_unlock(&nsm_lock); + dprintk("lockd: destroyed nsm_handle for %s (%s)\n", + nsm->sm_name, nsm->sm_addrbuf); kfree(nsm); } } -- cgit v0.10.2 From bc1cc6c4e476b60df48227165990c87a22db6bb7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:01 -0500 Subject: NSM: Remove NULL pointer check from nsm_find() The nsm_find() function should never be called with a NULL IP address pointer. If it is, that's a bug. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 3825545..0a066a1 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -224,9 +224,6 @@ struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen, struct nsm_handle *nsm = NULL; struct nsm_handle *pos; - if (!sap) - return NULL; - if (hostname && memchr(hostname, '/', hostname_len) != NULL) { if (printk_ratelimit()) { printk(KERN_WARNING "Invalid hostname \"%.*s\" " -- cgit v0.10.2 From 05f3a9af58180d24a9decedd71d4587935782d70 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:09 -0500 Subject: NSM: Remove !nsm check from nsm_release() The nsm_release() function should never be called with a NULL handle point. If it is, that's a bug. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 0a066a1..0792900 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -288,8 +288,6 @@ found: */ void nsm_release(struct nsm_handle *nsm) { - if (!nsm) - return; if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) { list_del(&nsm->sm_link); spin_unlock(&nsm_lock); -- cgit v0.10.2 From 7e44d3bea21fbb9494930d1cd35ca92a9a4a3279 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:16 -0500 Subject: NSM: Generate NSMPROC_MON's "priv" argument when nsm_handle is created Introduce a new data type, used by both the in-kernel NLM and NSM implementations, that is used to manage the opaque "priv" argument for the NSMPROC_MON and NLMPROC_SM_NOTIFY calls. Construct the "priv" cookie when the nsm_handle is created. The nsm_init_private() function may look a little strange, but it is roughly equivalent to how the XDR encoder formed the "priv" argument. It's going to go away soon. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 0792900..c8d18cd 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -201,6 +201,21 @@ void nsm_unmonitor(const struct nlm_host *host) } } +/* + * Construct a unique cookie to match this nsm_handle to this monitored + * host. It is passed to the local rpc.statd via NSMPROC_MON, and + * returned via NLMPROC_SM_NOTIFY, in the "priv" field of these + * requests. + * + * Linux provides the raw IP address of the monitored host, + * left in network byte order. + */ +static void nsm_init_private(struct nsm_handle *nsm) +{ + __be32 *p = (__be32 *)&nsm->sm_priv.data; + *p = nsm_addr_in(nsm)->sin_addr.s_addr; +} + /** * nsm_find - Find or create a cached nsm_handle * @sap: pointer to socket address of handle to find @@ -271,6 +286,7 @@ retry: nsm->sm_name = (char *) (nsm + 1); memcpy(nsm->sm_name, hostname, hostname_len); nsm->sm_name[hostname_len] = '\0'; + nsm_init_private(nsm); nsm_display_address((struct sockaddr *)&nsm->sm_addr, nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf)); atomic_set(&nsm->sm_count, 1); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 8d71536..194fa8a 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -85,6 +85,7 @@ struct nsm_handle { size_t sm_addrlen; unsigned int sm_monitored : 1, sm_sticky : 1; /* don't unmonitor */ + struct nsm_private sm_priv; char sm_addrbuf[NSM_ADDRBUF]; }; diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h index 116bf38..5cef5a7 100644 --- a/include/linux/lockd/sm_inter.h +++ b/include/linux/lockd/sm_inter.h @@ -10,7 +10,6 @@ #define LINUX_LOCKD_SM_INTER_H #define SM_MAXSTRLEN 1024 -#define SM_PRIV_SIZE 16 extern int nsm_local_state; diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index d6b3a80..6b51992 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -13,6 +13,12 @@ #include #include +#define SM_PRIV_SIZE 16 + +struct nsm_private { + unsigned char data[SM_PRIV_SIZE]; +}; + struct svc_rqst; #define NLM_MAXCOOKIELEN 32 -- cgit v0.10.2 From cab2d3c99165abbba2943f1b269003b17fd3b1cb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:24 -0500 Subject: NSM: Encode the new "priv" cookie for NSMPROC_MON requests Pass the new "priv" cookie to NSMPROC_MON's XDR encoder, instead of creating the "priv" argument in the encoder at call time. This patch should not cause a behavioral change: the contents of the cookie remain the same for the time being. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index c8d18cd..4424b0a 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -31,7 +31,7 @@ enum { }; struct nsm_args { - __be32 addr; /* remote address */ + struct nsm_private *priv; u32 prog; /* RPC callback info */ u32 vers; u32 proc; @@ -101,7 +101,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) struct rpc_clnt *clnt; int status; struct nsm_args args = { - .addr = nsm_addr_in(nsm)->sin_addr.s_addr, + .priv = &nsm->sm_priv, .prog = NLM_PROGRAM, .vers = 3, .proc = NLMPROC_NSM_NOTIFY, @@ -407,9 +407,6 @@ static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp) * The "priv" argument may contain private information required * by the NSMPROC_MON call. This information will be supplied in the * NLMPROC_SM_NOTIFY call. - * - * Linux provides the raw IP address of the monitored host, - * left in network byte order. */ static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp) { @@ -418,10 +415,7 @@ static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp) p = xdr_reserve_space(xdr, SM_PRIV_SIZE); if (unlikely(p == NULL)) return -EIO; - *p++ = argp->addr; - *p++ = 0; - *p++ = 0; - *p++ = 0; + xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE); return 0; } -- cgit v0.10.2 From 7fefc9cb9d5f129c238d93166f705c96ca2e7e51 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:31 -0500 Subject: NLM: Change nlm_host_rebooted() to take a single nlm_reboot argument Pass the nlm_reboot data structure directly from the NLMPROC_SM_NOTIFY XDR decoders to nlm_host_rebooted(). This eliminates some packing and unpacking of the NLMPROC_SM_NOTIFY results, and prepares for passing these results, including the "priv" cookie, directly to a lookup routine in fs/lockd/mon.c. This patch changes code organization but should not cause any behavioral change. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index dbdeaa8..ed10338 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -444,31 +444,34 @@ void nlm_release_host(struct nlm_host *host) } } -/* - * We were notified that the host indicated by address &sin - * has rebooted. - * Release all resources held by that peer. +/** + * nlm_host_rebooted - Release all resources held by rebooted host + * @info: pointer to decoded results of NLM_SM_NOTIFY call + * + * We were notified that the specified host has rebooted. Release + * all resources held by that peer. */ -void nlm_host_rebooted(const struct sockaddr_in *sin, - const char *hostname, - unsigned int hostname_len, - u32 new_state) +void nlm_host_rebooted(const struct nlm_reboot *info) { + const struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = info->addr, + }; struct hlist_head *chain; struct hlist_node *pos; struct nsm_handle *nsm; struct nlm_host *host; - nsm = nsm_find((struct sockaddr *)sin, sizeof(*sin), - hostname, hostname_len, 0); + nsm = nsm_find((struct sockaddr *)&sin, sizeof(sin), + info->mon, info->len, 0); if (nsm == NULL) { dprintk("lockd: never saw rebooted peer '%.*s' before\n", - hostname_len, hostname); + info->len, info->mon); return; } dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n", - hostname_len, hostname, nsm->sm_addrbuf); + info->len, info->mon, nsm->sm_addrbuf); /* When reclaiming locks on this peer, make sure that * we set up a new notification */ @@ -483,8 +486,8 @@ again: mutex_lock(&nlm_host_mutex); for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { hlist_for_each_entry(host, pos, chain, h_hash) { if (host->h_nsmhandle == nsm - && host->h_nsmstate != new_state) { - host->h_nsmstate = new_state; + && host->h_nsmstate != info->state) { + host->h_nsmstate = info->state; host->h_state++; nlm_get_host(host); diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 4dfdcbc..bb79a53 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -419,8 +419,6 @@ static __be32 nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { - struct sockaddr_in saddr; - dprintk("lockd: SM_NOTIFY called\n"); if (!nlm_privileged_requester(rqstp)) { @@ -430,14 +428,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, return rpc_system_err; } - /* Obtain the host pointer for this NFS server and try to - * reclaim all locks we hold on this server. - */ - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = argp->addr; - nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state); - + nlm_host_rebooted(argp); return rpc_success; } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 3ca89e2..e44310c 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -451,8 +451,6 @@ static __be32 nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { - struct sockaddr_in saddr; - dprintk("lockd: SM_NOTIFY called\n"); if (!nlm_privileged_requester(rqstp)) { @@ -462,14 +460,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, return rpc_system_err; } - /* Obtain the host pointer for this NFS server and try to - * reclaim all locks we hold on this server. - */ - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = argp->addr; - nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state); - + nlm_host_rebooted(argp); return rpc_success; } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 194fa8a..2a3533e 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -239,8 +239,7 @@ void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *); void nlm_release_host(struct nlm_host *); void nlm_shutdown_hosts(void); -extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, - unsigned int, u32); +void nlm_host_rebooted(const struct nlm_reboot *); /* * Host monitoring -- cgit v0.10.2 From 576df4634e37e46b441fefb91915184edb13bb94 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:39 -0500 Subject: NLM: Decode "priv" argument of NLMPROC_SM_NOTIFY as an opaque The NLM XDR decoders for the NLMPROC_SM_NOTIFY procedure should treat their "priv" argument truly as an opaque, as defined by the protocol, and let the upper layers figure out what is in it. This will make it easier to modify the contents and interpretation of the "priv" argument, and keep knowledge about what's in "priv" local to fs/lockd/mon.c. For now, the NLM and NSM implementations should behave exactly as they did before. The formation of the address of the rebooted host in nlm_host_rebooted() may look a little strange, but it is the inverse of how nsm_init_private() forms the private cookie. Plus, it's going away soon anyway. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index ed10338..dc41e46 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -453,9 +453,10 @@ void nlm_release_host(struct nlm_host *host) */ void nlm_host_rebooted(const struct nlm_reboot *info) { + __be32 *p = (__be32 *)&info->priv.data; const struct sockaddr_in sin = { .sin_family = AF_INET, - .sin_addr.s_addr = info->addr, + .sin_addr.s_addr = *p, }; struct hlist_head *chain; struct hlist_node *pos; diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 1f22629..4cc7d01 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -349,8 +349,8 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp) if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) return 0; argp->state = ntohl(*p++); - /* Preserve the address in network byte order */ - argp->addr = *p++; + memcpy(&argp->priv.data, p, sizeof(argp->priv.data)); + p += XDR_QUADLEN(SM_PRIV_SIZE); return xdr_argsize_check(rqstp, p); } diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 50c493a..61d1714 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -356,8 +356,8 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) return 0; argp->state = ntohl(*p++); - /* Preserve the address in network byte order */ - argp->addr = *p++; + memcpy(&argp->priv.data, p, sizeof(argp->priv.data)); + p += XDR_QUADLEN(SM_PRIV_SIZE); return xdr_argsize_check(rqstp, p); } diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index 6b51992..6338866 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -83,10 +83,10 @@ struct nlm_res { * statd callback when client has rebooted */ struct nlm_reboot { - char * mon; - unsigned int len; - u32 state; - __be32 addr; + char *mon; + unsigned int len; + u32 state; + struct nsm_private priv; }; /* -- cgit v0.10.2 From 3420a8c4359a189f7d854ed7075d151257415447 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:46 -0500 Subject: NSM: Add nsm_lookup() function Introduce a new API to fs/lockd/mon.c that allows nlm_host_rebooted() to lookup up nsm_handles via the contents of an nlm_reboot struct. The new function is equivalent to calling nsm_find() with @create set to zero, but it takes a struct nlm_reboot instead of separate arguments. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 4424b0a..e469039 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -201,6 +201,29 @@ void nsm_unmonitor(const struct nlm_host *host) } } +static struct nsm_handle *nsm_lookup_hostname(const char *hostname, + const size_t len) +{ + struct nsm_handle *nsm; + + list_for_each_entry(nsm, &nsm_handles, sm_link) + if (strlen(nsm->sm_name) == len && + memcmp(nsm->sm_name, hostname, len) == 0) + return nsm; + return NULL; +} + +static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv) +{ + struct nsm_handle *nsm; + + list_for_each_entry(nsm, &nsm_handles, sm_link) + if (memcmp(nsm->sm_priv.data, priv->data, + sizeof(priv->data)) == 0) + return nsm; + return NULL; +} + /* * Construct a unique cookie to match this nsm_handle to this monitored * host. It is passed to the local rpc.statd via NSMPROC_MON, and @@ -298,6 +321,47 @@ found: } /** + * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle + * @info: pointer to NLMPROC_SM_NOTIFY arguments + * + * Returns a matching nsm_handle if found in the nsm cache; the returned + * nsm_handle's reference count is bumped and sm_monitored is cleared. + * Otherwise returns NULL if some error occurred. + */ +struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info) +{ + struct nsm_handle *cached; + + spin_lock(&nsm_lock); + + if (nsm_use_hostnames && info->mon != NULL) + cached = nsm_lookup_hostname(info->mon, info->len); + else + cached = nsm_lookup_priv(&info->priv); + + if (unlikely(cached == NULL)) { + spin_unlock(&nsm_lock); + dprintk("lockd: never saw rebooted peer '%.*s' before\n", + info->len, info->mon); + return cached; + } + + atomic_inc(&cached->sm_count); + spin_unlock(&nsm_lock); + + /* + * During subsequent lock activity, force a fresh + * notification to be set up for this host. + */ + cached->sm_monitored = 0; + + dprintk("lockd: host %s (%s) rebooted, cnt %d\n", + cached->sm_name, cached->sm_addrbuf, + atomic_read(&cached->sm_count)); + return cached; +} + +/** * nsm_release - Release an NSM handle * @nsm: pointer to handle to be released * diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 2a3533e..5e3ad92 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -251,6 +251,7 @@ struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen, const char *hostname, const size_t hostname_len, const int create); +struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info); void nsm_release(struct nsm_handle *nsm); /* -- cgit v0.10.2 From 8c7378fd2a5f22016542931b887a2ae98d146eaf Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:03:54 -0500 Subject: NLM: Call nsm_reboot_lookup() instead of nsm_find() Invoke the newly introduced nsm_reboot_lookup() function in nlm_host_rebooted() instead of nsm_find(). This introduces just one behavioral change: debugging messages produced during reboot notification will now appear when the NLMDBG_MONITOR flag is set, but not when the NLMDBG_HOSTCACHE flag is set. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index dc41e46..230de93 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -453,30 +453,14 @@ void nlm_release_host(struct nlm_host *host) */ void nlm_host_rebooted(const struct nlm_reboot *info) { - __be32 *p = (__be32 *)&info->priv.data; - const struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_addr.s_addr = *p, - }; struct hlist_head *chain; struct hlist_node *pos; struct nsm_handle *nsm; struct nlm_host *host; - nsm = nsm_find((struct sockaddr *)&sin, sizeof(sin), - info->mon, info->len, 0); - if (nsm == NULL) { - dprintk("lockd: never saw rebooted peer '%.*s' before\n", - info->len, info->mon); + nsm = nsm_reboot_lookup(info); + if (unlikely(nsm == NULL)) return; - } - - dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n", - info->len, info->mon, nsm->sm_addrbuf); - - /* When reclaiming locks on this peer, make sure that - * we set up a new notification */ - nsm->sm_monitored = 0; /* Mark all hosts tied to this NSM state as having rebooted. * We run the loop repeatedly, because we drop the host table -- cgit v0.10.2 From 92fd91b998a5216a6d6606704e71d541a180216c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Dec 2008 19:04:01 -0500 Subject: NLM: Remove "create" argument from nsm_find() Clean up: nsm_find() now has only one caller, and that caller unconditionally sets the @create argument. Thus the @create argument is no longer needed. Since nsm_find() now has a more specific purpose, pick a more appropriate name for it. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 230de93..e5a65df 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -159,8 +159,8 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) atomic_inc(&nsm->sm_count); else { host = NULL; - nsm = nsm_find(ni->sap, ni->salen, - ni->hostname, ni->hostname_len, 1); + nsm = nsm_get_handle(ni->sap, ni->salen, + ni->hostname, ni->hostname_len); if (!nsm) { dprintk("lockd: nlm_lookup_host failed; " "no nsm handle\n"); diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index e469039..7407022 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -240,24 +240,22 @@ static void nsm_init_private(struct nsm_handle *nsm) } /** - * nsm_find - Find or create a cached nsm_handle + * nsm_get_handle - Find or create a cached nsm_handle * @sap: pointer to socket address of handle to find * @salen: length of socket address * @hostname: pointer to C string containing hostname to find * @hostname_len: length of C string - * @create: one means create new handle if not found in cache * - * Behavior is modulated by the global nsm_use_hostnames variable - * and by the @create argument. + * Behavior is modulated by the global nsm_use_hostnames variable. * - * Returns a cached nsm_handle after bumping its ref count, or if - * @create is set, returns a fresh nsm_handle if a handle that - * matches @sap and/or @hostname cannot be found in the handle cache. - * Returns NULL if an error occurs. + * Returns a cached nsm_handle after bumping its ref count, or + * returns a fresh nsm_handle if a handle that matches @sap and/or + * @hostname cannot be found in the handle cache. Returns NULL if + * an error occurs. */ -struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen, - const char *hostname, const size_t hostname_len, - const int create) +struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, + const size_t salen, const char *hostname, + const size_t hostname_len) { struct nsm_handle *nsm = NULL; struct nsm_handle *pos; @@ -297,9 +295,6 @@ retry: } spin_unlock(&nsm_lock); - if (!create) - return NULL; - nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); if (nsm == NULL) return NULL; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 5e3ad92..1ccd49e 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -247,10 +247,10 @@ void nlm_host_rebooted(const struct nlm_reboot *); int nsm_monitor(const struct nlm_host *host); void nsm_unmonitor(const struct nlm_host *host); -struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen, +struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, + const size_t salen, const char *hostname, - const size_t hostname_len, - const int create); + const size_t hostname_len); struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info); void nsm_release(struct nsm_handle *nsm); -- cgit v0.10.2 From b39b897c259fc1fd1998505f2b1d4ec1f115bce1 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:55:52 -0500 Subject: NSM: Refactor nsm_handle creation into a helper function Clean up. Refactor the creation of nsm_handles into a helper. Fields are initialized in increasing address order to make efficient use of CPU caches. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 7407022..315ca07 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -239,6 +239,30 @@ static void nsm_init_private(struct nsm_handle *nsm) *p = nsm_addr_in(nsm)->sin_addr.s_addr; } +static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap, + const size_t salen, + const char *hostname, + const size_t hostname_len) +{ + struct nsm_handle *new; + + new = kzalloc(sizeof(*new) + hostname_len + 1, GFP_KERNEL); + if (unlikely(new == NULL)) + return NULL; + + atomic_set(&new->sm_count, 1); + new->sm_name = (char *)(new + 1); + memcpy(nsm_addr(new), sap, salen); + new->sm_addrlen = salen; + nsm_init_private(new); + nsm_display_address((const struct sockaddr *)&new->sm_addr, + new->sm_addrbuf, sizeof(new->sm_addrbuf)); + memcpy(new->sm_name, hostname, hostname_len); + new->sm_name[hostname_len] = '\0'; + + return new; +} + /** * nsm_get_handle - Find or create a cached nsm_handle * @sap: pointer to socket address of handle to find @@ -295,19 +319,9 @@ retry: } spin_unlock(&nsm_lock); - nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); - if (nsm == NULL) + nsm = nsm_create_handle(sap, salen, hostname, hostname_len); + if (unlikely(nsm == NULL)) return NULL; - - memcpy(nsm_addr(nsm), sap, salen); - nsm->sm_addrlen = salen; - nsm->sm_name = (char *) (nsm + 1); - memcpy(nsm->sm_name, hostname, hostname_len); - nsm->sm_name[hostname_len] = '\0'; - nsm_init_private(nsm); - nsm_display_address((struct sockaddr *)&nsm->sm_addr, - nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf)); - atomic_set(&nsm->sm_count, 1); goto retry; found: -- cgit v0.10.2 From 77a3ef33e2de6fc8aabd7cb1700bfef81757c28a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:55:59 -0500 Subject: NSM: More clean up of nsm_get_handle() Clean up: refactor nsm_get_handle() so it is organized the same way that nsm_reboot_lookup() is. There is an additional micro-optimization here. This change moves the "hostname & nsm_use_hostnames" test out of the list_for_each_entry() clause in nsm_get_handle(), since it is loop-invariant. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 315ca07..99aec74 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -213,6 +213,16 @@ static struct nsm_handle *nsm_lookup_hostname(const char *hostname, return NULL; } +static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap) +{ + struct nsm_handle *nsm; + + list_for_each_entry(nsm, &nsm_handles, sm_link) + if (nlm_cmp_addr(nsm_addr(nsm), sap)) + return nsm; + return NULL; +} + static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv) { struct nsm_handle *nsm; @@ -281,8 +291,7 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, const size_t salen, const char *hostname, const size_t hostname_len) { - struct nsm_handle *nsm = NULL; - struct nsm_handle *pos; + struct nsm_handle *cached, *new = NULL; if (hostname && memchr(hostname, '/', hostname_len) != NULL) { if (printk_ratelimit()) { @@ -295,38 +304,37 @@ struct nsm_handle *nsm_get_handle(const struct sockaddr *sap, retry: spin_lock(&nsm_lock); - list_for_each_entry(pos, &nsm_handles, sm_link) { - - if (hostname && nsm_use_hostnames) { - if (strlen(pos->sm_name) != hostname_len - || memcmp(pos->sm_name, hostname, hostname_len)) - continue; - } else if (!nlm_cmp_addr(nsm_addr(pos), sap)) - continue; - atomic_inc(&pos->sm_count); - kfree(nsm); - nsm = pos; - dprintk("lockd: found nsm_handle for %s (%s), cnt %d\n", - pos->sm_name, pos->sm_addrbuf, - atomic_read(&pos->sm_count)); - goto found; + + if (nsm_use_hostnames && hostname != NULL) + cached = nsm_lookup_hostname(hostname, hostname_len); + else + cached = nsm_lookup_addr(sap); + + if (cached != NULL) { + atomic_inc(&cached->sm_count); + spin_unlock(&nsm_lock); + kfree(new); + dprintk("lockd: found nsm_handle for %s (%s), " + "cnt %d\n", cached->sm_name, + cached->sm_addrbuf, + atomic_read(&cached->sm_count)); + return cached; } - if (nsm) { - list_add(&nsm->sm_link, &nsm_handles); + + if (new != NULL) { + list_add(&new->sm_link, &nsm_handles); + spin_unlock(&nsm_lock); dprintk("lockd: created nsm_handle for %s (%s)\n", - nsm->sm_name, nsm->sm_addrbuf); - goto found; + new->sm_name, new->sm_addrbuf); + return new; } + spin_unlock(&nsm_lock); - nsm = nsm_create_handle(sap, salen, hostname, hostname_len); - if (unlikely(nsm == NULL)) + new = nsm_create_handle(sap, salen, hostname, hostname_len); + if (unlikely(new == NULL)) return NULL; goto retry; - -found: - spin_unlock(&nsm_lock); - return nsm; } /** -- cgit v0.10.2 From 94da7663db26530a8377f7219f8be8bd4d4822c2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:07 -0500 Subject: NSM: Replace IP address as our nlm_reboot lookup key NLM provides file locking services for NFS files. Part of this service includes a second protocol, known as NSM, which is a reboot notification service. NLM uses this service to determine when to reclaim locks or enter a grace period after a client or server reboots. The NLM service (implemented by lockd in the Linux kernel) contacts the local NSM service (implemented by rpc.statd in Linux user space) via NSM protocol upcalls to register a callback when a particular remote peer reboots. To match the callback to the correct remote peer, the NLM service constructs a cookie that it passes in the request. The NSM service passes that cookie back to the NLM service when it is notified that the given remote peer has indeed rebooted. Currently on Linux, the cookie is the raw 32-bit IPv4 address of the remote peer. To support IPv6 addresses, which are larger, we could use all 16 bytes of the cookie to represent a full IPv6 address, although we still can't represent an IPv6 address with a scope ID in just 16 bytes. Instead, to avoid the need for future changes to support additional address types, we'll use a manufactured value for the cookie, and use that to find the corresponding nsm_handle struct in the kernel during the NLMPROC_SM_NOTIFY callback. This should provide complete support in the kernel's NSM implementation for IPv6 hosts, while remaining backwards compatible with older rpc.statd implementations. Note we also deal with another case where nsm_use_hostnames can change while there are outstanding notifications, possibly resulting in the loss of reboot notifications. After this patch, the priv cookie is always used to lookup rebooted hosts in the kernel. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 99aec74..8ae4c02 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -9,6 +9,8 @@ #include #include #include +#include + #include #include #include @@ -240,13 +242,25 @@ static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv) * returned via NLMPROC_SM_NOTIFY, in the "priv" field of these * requests. * - * Linux provides the raw IP address of the monitored host, - * left in network byte order. + * The NSM protocol requires that these cookies be unique while the + * system is running. We prefer a stronger requirement of making them + * unique across reboots. If user space bugs cause a stale cookie to + * be sent to the kernel, it could cause the wrong host to lose its + * lock state if cookies were not unique across reboots. + * + * The cookies are exposed only to local user space via loopback. They + * do not appear on the physical network. If we want greater security + * for some reason, nsm_init_private() could perform a one-way hash to + * obscure the contents of the cookie. */ static void nsm_init_private(struct nsm_handle *nsm) { - __be32 *p = (__be32 *)&nsm->sm_priv.data; - *p = nsm_addr_in(nsm)->sin_addr.s_addr; + u64 *p = (u64 *)&nsm->sm_priv.data; + struct timespec ts; + + ktime_get_ts(&ts); + *p++ = timespec_to_ns(&ts); + *p = (unsigned long)nsm; } static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap, @@ -351,11 +365,7 @@ struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info) spin_lock(&nsm_lock); - if (nsm_use_hostnames && info->mon != NULL) - cached = nsm_lookup_hostname(info->mon, info->len); - else - cached = nsm_lookup_priv(&info->priv); - + cached = nsm_lookup_priv(&info->priv); if (unlikely(cached == NULL)) { spin_unlock(&nsm_lock); dprintk("lockd: never saw rebooted peer '%.*s' before\n", -- cgit v0.10.2 From e6765b83977f07983c7a10e6bbb19d6c7bbfc3a4 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:14 -0500 Subject: NSM: Remove include/linux/lockd/sm_inter.h Clean up: The include/linux/lockd/sm_inter.h header is nearly empty now. Remove it. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 5ce42e0..dd79570 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -16,7 +16,6 @@ #include #include #include -#include #define NLMDBG_FACILITY NLMDBG_CLIENT #define NLMCLNT_GRACE_WAIT (5*HZ) diff --git a/fs/lockd/host.c b/fs/lockd/host.c index e5a65df..99d737b 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 8ae4c02..dfa9d80 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -15,8 +15,6 @@ #include #include #include -#include - #define NLMDBG_FACILITY NLMDBG_MONITOR #define NSM_PROGRAM 100024 diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index bc3c3cb..0b13392 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #define NLMDBG_FACILITY NLMDBG_SVC diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index bb79a53..17250373 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -16,8 +16,6 @@ #include #include #include -#include - #define NLMDBG_FACILITY NLMDBG_CLIENT diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index e44310c..3688e55 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -16,8 +16,6 @@ #include #include #include -#include - #define NLMDBG_FACILITY NLMDBG_CLIENT diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 34c2766..9e4d6aab 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 4cc7d01..0336f2b 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -16,7 +16,6 @@ #include #include #include -#include #define NLMDBG_FACILITY NLMDBG_XDR diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 61d1714..e1d5286 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -17,7 +17,6 @@ #include #include #include -#include #define NLMDBG_FACILITY NLMDBG_XDR diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 1ccd49e..8b57467 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -205,6 +205,7 @@ extern struct svc_procedure nlmsvc_procedures4[]; extern int nlmsvc_grace_period; extern unsigned long nlmsvc_timeout; extern int nsm_use_hostnames; +extern int nsm_local_state; /* * Lockd client functions diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h deleted file mode 100644 index 5cef5a7..0000000 --- a/include/linux/lockd/sm_inter.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/include/linux/lockd/sm_inter.h - * - * Declarations for the kernel statd client. - * - * Copyright (C) 1996, Olaf Kirch - */ - -#ifndef LINUX_LOCKD_SM_INTER_H -#define LINUX_LOCKD_SM_INTER_H - -#define SM_MAXSTRLEN 1024 - -extern int nsm_local_state; - -#endif /* LINUX_LOCKD_SM_INTER_H */ diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index 6338866..7dc5b6c 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -13,6 +13,7 @@ #include #include +#define SM_MAXSTRLEN 1024 #define SM_PRIV_SIZE 16 struct nsm_private { -- cgit v0.10.2 From 8529bc51d30b8f001734b29b21a51b579c260f5b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:22 -0500 Subject: NSM: Move nsm_addr() to fs/lockd/mon.c Clean up: nsm_addr_in() is no longer used, and nsm_addr() is used only in fs/lockd/mon.c, so move it there. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index dfa9d80..43be31c 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -55,6 +55,11 @@ static DEFINE_SPINLOCK(nsm_lock); */ int nsm_local_state; +static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm) +{ + return (struct sockaddr *)&nsm->sm_addr; +} + static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf, const size_t len) { diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 8b57467..6ab0449 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -112,16 +112,6 @@ static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host) return (struct sockaddr *)&host->h_srcaddr; } -static inline struct sockaddr_in *nsm_addr_in(const struct nsm_handle *handle) -{ - return (struct sockaddr_in *)&handle->sm_addr; -} - -static inline struct sockaddr *nsm_addr(const struct nsm_handle *handle) -{ - return (struct sockaddr *)&handle->sm_addr; -} - /* * Map an fl_owner_t into a unique 32-bit "pid" */ -- cgit v0.10.2 From b7ba597fb964dfa44284904b3b3d74d44b8e1c42 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:29 -0500 Subject: NSM: Move nsm_use_hostnames to mon.c Clean up. Treat the nsm_use_hostnames global variable like nsm_local_state. Note that the default value of nsm_use_hostnames is still zero. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 43be31c..fafa0ea 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -53,7 +53,8 @@ static DEFINE_SPINLOCK(nsm_lock); /* * Local NSM state */ -int nsm_local_state; +int __read_mostly nsm_local_state; +int __read_mostly nsm_use_hostnames; static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm) { diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 0b13392..7ac7d72 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -59,7 +59,6 @@ unsigned long nlmsvc_timeout; static unsigned long nlm_grace_period; static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; static int nlm_udpport, nlm_tcpport; -int nsm_use_hostnames = 0; /* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */ static unsigned int nlm_max_connections = 1024; -- cgit v0.10.2 From 49b5699b3fc22b363534c509c1b7dba06bc677bf Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:37 -0500 Subject: NSM: Move nsm_create() Clean up: one last thing... relocate nsm_create() to eliminate the forward declaration and group it near the only function that actually uses it. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index fafa0ea..5e2c4d5 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -44,8 +44,6 @@ struct nsm_res { u32 state; }; -static struct rpc_clnt * nsm_create(void); - static struct rpc_program nsm_program; static LIST_HEAD(nsm_handles); static DEFINE_SPINLOCK(nsm_lock); @@ -98,11 +96,26 @@ static void nsm_display_address(const struct sockaddr *sap, } } -/* - * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls - */ -static int -nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) +static struct rpc_clnt *nsm_create(void) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + struct rpc_create_args args = { + .protocol = XPRT_TRANSPORT_UDP, + .address = (struct sockaddr *)&sin, + .addrsize = sizeof(sin), + .servername = "rpc.statd", + .program = &nsm_program, + .version = NSM_VERSION, + .authflavor = RPC_AUTH_NULL, + }; + + return rpc_create(&args); +} + +static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) { struct rpc_clnt *clnt; int status; @@ -409,30 +422,6 @@ void nsm_release(struct nsm_handle *nsm) } /* - * Create NSM client for the local host - */ -static struct rpc_clnt * -nsm_create(void) -{ - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(INADDR_LOOPBACK), - .sin_port = 0, - }; - struct rpc_create_args args = { - .protocol = XPRT_TRANSPORT_UDP, - .address = (struct sockaddr *)&sin, - .addrsize = sizeof(sin), - .servername = "localhost", - .program = &nsm_program, - .version = NSM_VERSION, - .authflavor = RPC_AUTH_NULL, - }; - - return rpc_create(&args); -} - -/* * XDR functions for NSM. * * See http://www.opengroup.org/ for details on the Network -- cgit v0.10.2 From d1208f70738c91f13b4eadb1b7a694082e439da2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:44 -0500 Subject: NLM: nlm_privileged_requester() doesn't recognize mapped loopback address Commit b85e4676 added the nlm_privileged_requester() helper to check whether an RPC request was sent from a local privileged caller. It recognizes IPv4 privileged callers (from "127.0.0.1"), and IPv6 privileged callers (from "::1"). However, IPV6_ADDR_LOOPBACK is not set for the mapped IPv4 loopback address (::ffff:7f00:0001), so the test breaks when the kernel's RPC service is IPv6-enabled but user space is calling via the IPv4 loopback address. This is actually the most common case for IPv6- enabled RPC services on Linux. Rewrite the IPv6 check to handle the mapped IPv4 loopback address as well as a normal IPv6 loopback address. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 6ab0449..80d7e8a 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -299,8 +299,14 @@ static inline int __nlm_privileged_request4(const struct sockaddr *sap) static inline int __nlm_privileged_request6(const struct sockaddr *sap) { const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; - return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) && - (ntohs(sin6->sin6_port) < 1024); + + if (ntohs(sin6->sin6_port) > 1023) + return 0; + + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_MAPPED) + return ipv4_is_loopback(sin6->sin6_addr.s6_addr32[3]); + + return ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK; } #else /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ static inline int __nlm_privileged_request6(const struct sockaddr *sap) -- cgit v0.10.2 From 57ef692588bc225853ca3267ca5b7cea2b07e058 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:52 -0500 Subject: NLM: Rewrite IPv4 privileged requester's check Clean up. For consistency, rewrite the IPv4 check to match the same style as the new IPv6 check. Note that ipv4_is_loopback() is somewhat broader in its interpretation of what is a loopback address than simply "127.0.0.1". Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 80d7e8a..aa6fe70 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -291,8 +291,11 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) static inline int __nlm_privileged_request4(const struct sockaddr *sap) { const struct sockaddr_in *sin = (struct sockaddr_in *)sap; - return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) && - (ntohs(sin->sin_port) < 1024); + + if (ntohs(sin->sin_port) > 1023) + return 0; + + return ipv4_is_loopback(sin->sin_addr.s_addr); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -- cgit v0.10.2 From b064ec038a6180b13e5f89b6a30b42cb5ce8febc Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 11 Dec 2008 17:56:59 -0500 Subject: lockd: Enable NLM use of AF_INET6 If the kernel is configured to support IPv6 and the RPC server can register services via rpcbindv4, we are all set to enable IPv6 support for lockd. Signed-off-by: Chuck Lever Cc: Aime Le Rouzic Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 7ac7d72..3e5f9f0 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -53,6 +53,17 @@ static struct svc_rqst *nlmsvc_rqst; unsigned long nlmsvc_timeout; /* + * If the kernel has IPv6 support available, always listen for + * both AF_INET and AF_INET6 requests. + */ +#if (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) && \ + defined(CONFIG_SUNRPC_REGISTER_V4) +static const sa_family_t nlmsvc_family = AF_INET6; +#else /* (CONFIG_IPV6 || CONFIG_IPV6_MODULE) && CONFIG_SUNRPC_REGISTER_V4 */ +static const sa_family_t nlmsvc_family = AF_INET; +#endif /* (CONFIG_IPV6 || CONFIG_IPV6_MODULE) && CONFIG_SUNRPC_REGISTER_V4 */ + +/* * These can be set at insmod time (useful for NFS as root filesystem), * and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003 */ @@ -256,7 +267,7 @@ int lockd_up(void) "lockd_up: no pid, %d users??\n", nlmsvc_users); error = -ENOMEM; - serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, AF_INET, NULL); + serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, nlmsvc_family, NULL); if (!serv) { printk(KERN_WARNING "lockd_up: create service failed\n"); goto out; -- cgit v0.10.2 From b046ccdc1f8171f6d0129dcc2a28d49187b4bf69 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 12 Dec 2008 16:57:13 -0500 Subject: NFSD: clean up failover sysctl function naming Clean up: Rename recently-added failover functions to match the naming convention in fs/nfsd/nfsctl.c. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 77d7b8c..173c4dd 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -84,6 +84,8 @@ static ssize_t write_unexport(struct file *file, char *buf, size_t size); static ssize_t write_getfd(struct file *file, char *buf, size_t size); static ssize_t write_getfs(struct file *file, char *buf, size_t size); static ssize_t write_filehandle(struct file *file, char *buf, size_t size); +static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size); +static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size); static ssize_t write_threads(struct file *file, char *buf, size_t size); static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); static ssize_t write_versions(struct file *file, char *buf, size_t size); @@ -94,9 +96,6 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); #endif -static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size); -static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size); - static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Svc] = write_svc, [NFSD_Add] = write_add, @@ -106,8 +105,8 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Getfd] = write_getfd, [NFSD_Getfs] = write_getfs, [NFSD_Fh] = write_filehandle, - [NFSD_FO_UnlockIP] = failover_unlock_ip, - [NFSD_FO_UnlockFS] = failover_unlock_fs, + [NFSD_FO_UnlockIP] = write_unlock_ip, + [NFSD_FO_UnlockFS] = write_unlock_fs, [NFSD_Threads] = write_threads, [NFSD_Pool_Threads] = write_pool_threads, [NFSD_Versions] = write_versions, @@ -309,7 +308,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) return err; } -static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) +static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) { struct sockaddr_in sin = { .sin_family = AF_INET, @@ -339,7 +338,7 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin); } -static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) +static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) { struct path path; char *fo_path; -- cgit v0.10.2 From 54224f04ae95d86b27c0673cd773ebb120d86876 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 12 Dec 2008 16:57:20 -0500 Subject: NFSD: Fix a handful of coding style issues in write_filehandle() Clean up: follow kernel coding style. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 173c4dd..498d763 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -390,11 +390,13 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) dname = mesg; len = qword_get(&mesg, dname, size); - if (len <= 0) return -EINVAL; + if (len <= 0) + return -EINVAL; path = dname+len+1; len = qword_get(&mesg, path, size); - if (len <= 0) return -EINVAL; + if (len <= 0) + return -EINVAL; len = get_int(&mesg, &maxsize); if (len) @@ -418,7 +420,8 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) if (len) return len; - mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; + mesg = buf; + len = SIMPLE_TRANSACTION_LIMIT; qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); mesg[-1] = '\n'; return mesg - buf; -- cgit v0.10.2 From 9e074856caf13ba83363f73759f5e395f74ccf41 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 12 Dec 2008 16:57:27 -0500 Subject: NFSD: Replace open-coded integer with macro Clean up: Instead of open-coding 2049, use the NFS_PORT macro. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 498d763..856b864 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -439,9 +439,9 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) rv = get_int(&mesg, &newthreads); if (rv) return rv; - if (newthreads <0) + if (newthreads < 0) return -EINVAL; - rv = nfsd_svc(2049, newthreads); + rv = nfsd_svc(NFS_PORT, newthreads); if (rv) return rv; } -- cgit v0.10.2 From 262a09823bb07c6aafb6c1d312cde613d0b90c85 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 12 Dec 2008 16:57:35 -0500 Subject: NFSD: Add documenting comments for nfsctl interface Document the NFSD sysctl interface laid out in fs/nfsd/nfsctl.c. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 856b864..3d93b20 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -175,10 +175,24 @@ static const struct file_operations exports_operations = { /*----------------------------------------------------------------------------*/ /* * payload - write methods - * If the method has a response, the response should be put in buf, - * and the length returned. Otherwise return 0 or and -error. */ +/** + * write_svc - Start kernel's NFSD server + * + * Deprecated. /proc/fs/nfsd/threads is preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_svc + * svc_port: port number of this + * server's listener + * svc_nthreads: number of threads to start + * size: size in bytes of passed in nfsctl_svc + * Output: + * On success: returns zero + * On error: return code is negative errno value + */ static ssize_t write_svc(struct file *file, char *buf, size_t size) { struct nfsctl_svc *data; @@ -188,6 +202,30 @@ static ssize_t write_svc(struct file *file, char *buf, size_t size) return nfsd_svc(data->svc_port, data->svc_nthreads); } +/** + * write_add - Add or modify client entry in auth unix cache + * + * Deprecated. /proc/net/rpc/auth.unix.ip is preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_client + * cl_ident: '\0'-terminated C string + * containing domain name + * of client + * cl_naddr: no. of items in cl_addrlist + * cl_addrlist: array of client addresses + * cl_fhkeytype: ignored + * cl_fhkeylen: ignored + * cl_fhkey: ignored + * size: size in bytes of passed in nfsctl_client + * Output: + * On success: returns zero + * On error: return code is negative errno value + * + * Note: Only AF_INET client addresses are passed in, since + * nfsctl_client.cl_addrlist contains only in_addr fields for addresses. + */ static ssize_t write_add(struct file *file, char *buf, size_t size) { struct nfsctl_client *data; @@ -197,6 +235,30 @@ static ssize_t write_add(struct file *file, char *buf, size_t size) return exp_addclient(data); } +/** + * write_del - Remove client from auth unix cache + * + * Deprecated. /proc/net/rpc/auth.unix.ip is preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_client + * cl_ident: '\0'-terminated C string + * containing domain name + * of client + * cl_naddr: ignored + * cl_addrlist: ignored + * cl_fhkeytype: ignored + * cl_fhkeylen: ignored + * cl_fhkey: ignored + * size: size in bytes of passed in nfsctl_client + * Output: + * On success: returns zero + * On error: return code is negative errno value + * + * Note: Only AF_INET client addresses are passed in, since + * nfsctl_client.cl_addrlist contains only in_addr fields for addresses. + */ static ssize_t write_del(struct file *file, char *buf, size_t size) { struct nfsctl_client *data; @@ -206,6 +268,33 @@ static ssize_t write_del(struct file *file, char *buf, size_t size) return exp_delclient(data); } +/** + * write_export - Export part or all of a local file system + * + * Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_export + * ex_client: '\0'-terminated C string + * containing domain name + * of client allowed to access + * this export + * ex_path: '\0'-terminated C string + * containing pathname of + * directory in local file system + * ex_dev: fsid to use for this export + * ex_ino: ignored + * ex_flags: export flags for this export + * ex_anon_uid: UID to use for anonymous + * requests + * ex_anon_gid: GID to use for anonymous + * requests + * size: size in bytes of passed in nfsctl_export + * Output: + * On success: returns zero + * On error: return code is negative errno value + */ static ssize_t write_export(struct file *file, char *buf, size_t size) { struct nfsctl_export *data; @@ -215,6 +304,31 @@ static ssize_t write_export(struct file *file, char *buf, size_t size) return exp_export(data); } +/** + * write_unexport - Unexport a previously exported file system + * + * Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_export + * ex_client: '\0'-terminated C string + * containing domain name + * of client no longer allowed + * to access this export + * ex_path: '\0'-terminated C string + * containing pathname of + * directory in local file system + * ex_dev: ignored + * ex_ino: ignored + * ex_flags: ignored + * ex_anon_uid: ignored + * ex_anon_gid: ignored + * size: size in bytes of passed in nfsctl_export + * Output: + * On success: returns zero + * On error: return code is negative errno value + */ static ssize_t write_unexport(struct file *file, char *buf, size_t size) { struct nfsctl_export *data; @@ -225,6 +339,30 @@ static ssize_t write_unexport(struct file *file, char *buf, size_t size) return exp_unexport(data); } +/** + * write_getfs - Get a variable-length NFS file handle by path + * + * Deprecated. /proc/fs/nfsd/filehandle is preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_fsparm + * gd_addr: socket address of client + * gd_path: '\0'-terminated C string + * containing pathname of + * directory in local file system + * gd_maxlen: maximum size of returned file + * handle + * size: size in bytes of passed in nfsctl_fsparm + * Output: + * On success: passed-in buffer filled with a knfsd_fh structure + * (a variable-length raw NFS file handle); + * return code is the size in bytes of the file handle + * On error: return code is negative errno value + * + * Note: Only AF_INET client addresses are passed in, since gd_addr + * is the same size as a struct sockaddr_in. + */ static ssize_t write_getfs(struct file *file, char *buf, size_t size) { struct nfsctl_fsparm *data; @@ -264,6 +402,29 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size) return err; } +/** + * write_getfd - Get a fixed-length NFS file handle by path (used by mountd) + * + * Deprecated. /proc/fs/nfsd/filehandle is preferred. + * Function remains to support old versions of nfs-utils. + * + * Input: + * buf: struct nfsctl_fdparm + * gd_addr: socket address of client + * gd_path: '\0'-terminated C string + * containing pathname of + * directory in local file system + * gd_version: fdparm structure version + * size: size in bytes of passed in nfsctl_fdparm + * Output: + * On success: passed-in buffer filled with nfsctl_res + * (a fixed-length raw NFS file handle); + * return code is the size in bytes of the file handle + * On error: return code is negative errno value + * + * Note: Only AF_INET client addresses are passed in, since gd_addr + * is the same size as a struct sockaddr_in. + */ static ssize_t write_getfd(struct file *file, char *buf, size_t size) { struct nfsctl_fdparm *data; @@ -308,6 +469,22 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) return err; } +/** + * write_unlock_ip - Release all locks used by a client + * + * Experimental. + * + * Input: + * buf: '\n'-terminated C string containing a + * presentation format IPv4 address + * size: length of C string in @buf + * Output: + * On success: returns zero if all specified locks were released; + * returns one if one or more locks were not released + * On error: return code is negative errno value + * + * Note: Only AF_INET client addresses are passed in + */ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) { struct sockaddr_in sin = { @@ -338,6 +515,20 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin); } +/** + * write_unlock_fs - Release all locks on a local file system + * + * Experimental. + * + * Input: + * buf: '\n'-terminated C string containing the + * absolute pathname of a local file system + * size: length of C string in @buf + * Output: + * On success: returns zero if all specified locks were released; + * returns one if one or more locks were not released + * On error: return code is negative errno value + */ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) { struct path path; @@ -359,21 +550,44 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) if (error) return error; + /* + * XXX: Needs better sanity checking. Otherwise we could end up + * releasing locks on the wrong file system. + * + * For example: + * 1. Does the path refer to a directory? + * 2. Is that directory a mount point, or + * 3. Is that directory the root of an exported file system? + */ error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb); path_put(&path); return error; } +/** + * write_filehandle - Get a variable-length NFS file handle by path + * + * On input, the buffer contains a '\n'-terminated C string comprised of + * three alphanumeric words separated by whitespace. The string may + * contain escape sequences. + * + * Input: + * buf: + * domain: client domain name + * path: export pathname + * maxsize: numeric maximum size of + * @buf + * size: length of C string in @buf + * Output: + * On success: passed-in buffer filled with '\n'-terminated C + * string containing a ASCII hex text version + * of the NFS file handle; + * return code is the size in bytes of the string + * On error: return code is negative errno value + */ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) { - /* request is: - * domain path maxsize - * response is - * filehandle - * - * qword quoting is used, so filehandle will be \x.... - */ char *dname, *path; int uninitialized_var(maxsize); char *mesg = buf; @@ -427,11 +641,36 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) return mesg - buf; } +/** + * write_threads - Start NFSD, or report the current number of running threads + * + * Input: + * buf: ignored + * size: zero + * Output: + * On success: passed-in buffer filled with '\n'-terminated C + * string numeric value representing the number of + * running NFSD threads; + * return code is the size in bytes of the string + * On error: return code is zero + * + * OR + * + * Input: + * buf: C string containing an unsigned + * integer value representing the + * number of NFSD threads to start + * size: non-zero length of C string in @buf + * Output: + * On success: NFS service is started; + * passed-in buffer filled with '\n'-terminated C + * string numeric value representing the number of + * running NFSD threads; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + */ static ssize_t write_threads(struct file *file, char *buf, size_t size) { - /* if size > 0, look for a number of threads and call nfsd_svc - * then write out number of threads as reply - */ char *mesg = buf; int rv; if (size > 0) { @@ -449,6 +688,28 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) return strlen(buf); } +/** + * write_pool_threads - Set or report the current number of threads per pool + * + * Input: + * buf: ignored + * size: zero + * + * OR + * + * Input: + * buf: C string containing whitespace- + * separated unsigned integer values + * representing the number of NFSD + * threads to start in each pool + * size: non-zero length of C string in @buf + * Output: + * On success: passed-in buffer filled with '\n'-terminated C + * string containing integer values representing the + * number of NFSD threads in each pool; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + */ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) { /* if size > 0, look for an array of number of threads per node @@ -519,10 +780,6 @@ out_free: static ssize_t __write_versions(struct file *file, char *buf, size_t size) { - /* - * Format: - * [-/+]vers [-/+]vers ... - */ char *mesg = buf; char *vers, sign; int len, num; @@ -580,6 +837,38 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) return len; } +/** + * write_versions - Set or report the available NFS protocol versions + * + * Input: + * buf: ignored + * size: zero + * Output: + * On success: passed-in buffer filled with '\n'-terminated C + * string containing positive or negative integer + * values representing the current status of each + * protocol version; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + * + * OR + * + * Input: + * buf: C string containing whitespace- + * separated positive or negative + * integer values representing NFS + * protocol versions to enable ("+n") + * or disable ("-n") + * size: non-zero length of C string in @buf + * Output: + * On success: status of zero or more protocol versions has + * been updated; passed-in buffer filled with + * '\n'-terminated C string containing positive + * or negative integer values representing the + * current status of each protocol version; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + */ static ssize_t write_versions(struct file *file, char *buf, size_t size) { ssize_t rv; @@ -689,6 +978,75 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) return -EINVAL; } +/** + * write_ports - Pass a socket file descriptor or transport name to listen on + * + * Input: + * buf: ignored + * size: zero + * Output: + * On success: passed-in buffer filled with a '\n'-terminated C + * string containing a whitespace-separated list of + * named NFSD listeners; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + * + * OR + * + * Input: + * buf: C string containing an unsigned + * integer value representing a bound + * but unconnected socket that is to be + * used as an NFSD listener + * size: non-zero length of C string in @buf + * Output: + * On success: NFS service is started; + * passed-in buffer filled with a '\n'-terminated C + * string containing a unique alphanumeric name of + * the listener; + * return code is the size in bytes of the string + * On error: return code is a negative errno value + * + * OR + * + * Input: + * buf: C string containing a "-" followed + * by an integer value representing a + * previously passed in socket file + * descriptor + * size: non-zero length of C string in @buf + * Output: + * On success: NFS service no longer listens on that socket; + * passed-in buffer filled with a '\n'-terminated C + * string containing a unique name of the listener; + * return code is the size in bytes of the string + * On error: return code is a negative errno value + * + * OR + * + * Input: + * buf: C string containing a transport + * name and an unsigned integer value + * representing the port to listen on, + * separated by whitespace + * size: non-zero length of C string in @buf + * Output: + * On success: returns zero; NFS service is started + * On error: return code is a negative errno value + * + * OR + * + * Input: + * buf: C string containing a "-" followed + * by a transport name and an unsigned + * integer value representing the port + * to listen on, separated by whitespace + * size: non-zero length of C string in @buf + * Output: + * On success: returns zero; NFS service no longer listens + * on that transport + * On error: return code is a negative errno value + */ static ssize_t write_ports(struct file *file, char *buf, size_t size) { ssize_t rv; @@ -702,6 +1060,27 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) int nfsd_max_blksize; +/** + * write_maxblksize - Set or report the current NFS blksize + * + * Input: + * buf: ignored + * size: zero + * + * OR + * + * Input: + * buf: C string containing an unsigned + * integer value representing the new + * NFS blksize + * size: non-zero length of C string in @buf + * Output: + * On success: passed-in buffer filled with '\n'-terminated C string + * containing numeric value of the current NFS blksize + * setting; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + */ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) { char *mesg = buf; @@ -754,6 +1133,27 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) return strlen(buf); } +/** + * write_leasetime - Set or report the current NFSv4 lease time + * + * Input: + * buf: ignored + * size: zero + * + * OR + * + * Input: + * buf: C string containing an unsigned + * integer value representing the new + * NFSv4 lease expiry time + * size: non-zero length of C string in @buf + * Output: + * On success: passed-in buffer filled with '\n'-terminated C + * string containing unsigned integer value of the + * current lease expiry time; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + */ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) { ssize_t rv; @@ -790,6 +1190,27 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size) return strlen(buf); } +/** + * write_recoverydir - Set or report the pathname of the recovery directory + * + * Input: + * buf: ignored + * size: zero + * + * OR + * + * Input: + * buf: C string containing the pathname + * of the directory on a local file + * system containing permanent NFSv4 + * recovery data + * size: non-zero length of C string in @buf + * Output: + * On success: passed-in buffer filled with '\n'-terminated C string + * containing the current recovery pathname setting; + * return code is the size in bytes of the string + * On error: return code is zero or a negative errno value + */ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) { ssize_t rv; -- cgit v0.10.2 From 69b6ba3712b796a66595cfaf0a5ab4dfe1cf964a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 23 Dec 2008 16:30:11 -0500 Subject: SUNRPC: Ensure the server closes sockets in a timely fashion We want to ensure that connected sockets close down the connection when we set XPT_CLOSE, so that we don't keep it hanging while cleaning up all the stuff that is keeping a reference to the socket. Signed-off-by: Trond Myklebust Signed-off-by: J. Bruce Fields diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index ef3238d..cccfa7d 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -59,6 +59,7 @@ static void svc_udp_data_ready(struct sock *, int); static int svc_udp_recvfrom(struct svc_rqst *); static int svc_udp_sendto(struct svc_rqst *); static void svc_sock_detach(struct svc_xprt *); +static void svc_tcp_sock_detach(struct svc_xprt *); static void svc_sock_free(struct svc_xprt *); static struct svc_xprt *svc_create_socket(struct svc_serv *, int, @@ -1017,7 +1018,7 @@ static struct svc_xprt_ops svc_tcp_ops = { .xpo_recvfrom = svc_tcp_recvfrom, .xpo_sendto = svc_tcp_sendto, .xpo_release_rqst = svc_release_skb, - .xpo_detach = svc_sock_detach, + .xpo_detach = svc_tcp_sock_detach, .xpo_free = svc_sock_free, .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, .xpo_has_wspace = svc_tcp_has_wspace, @@ -1287,6 +1288,24 @@ static void svc_sock_detach(struct svc_xprt *xprt) sk->sk_state_change = svsk->sk_ostate; sk->sk_data_ready = svsk->sk_odata; sk->sk_write_space = svsk->sk_owspace; + + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible(sk->sk_sleep); +} + +/* + * Disconnect the socket, and reset the callbacks + */ +static void svc_tcp_sock_detach(struct svc_xprt *xprt) +{ + struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); + + dprintk("svc: svc_tcp_sock_detach(%p)\n", svsk); + + svc_sock_detach(xprt); + + if (!test_bit(XPT_LISTENER, &xprt->xpt_flags)) + kernel_sock_shutdown(svsk->sk_sock, SHUT_RDWR); } /* -- cgit v0.10.2 From 55ef1274dddd4de387c54d110e354ffbb6cdc706 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 20 Dec 2008 11:58:38 -0800 Subject: nfsd: Ensure nfsv4 calls the underlying filesystem on LOCKT Since nfsv4 allows LOCKT without an open, but the ->lock() method is a file method, we fake up a struct file in the nfsv4 code with just the fields we need initialized. But we forgot to initialize the file operations, with the result that LOCKT never results in a call to the filesystem's ->lock() method (if it exists). We could just add that one more initialization. But this hack of faking up a struct file with only some fields initialized seems the kind of thing that might cause more problems in the future. We should either do an open and get a real struct file, or make lock-testing an inode (not a file) method. This patch does the former. Reported-by: Marc Eshel Tested-by: Marc Eshel Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 06b89df..e62d0e3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2782,6 +2782,25 @@ out: } /* + * The NFSv4 spec allows a client to do a LOCKT without holding an OPEN, + * so we do a temporary open here just to get an open file to pass to + * vfs_test_lock. (Arguably perhaps test_lock should be done with an + * inode operation.) + */ +static int nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock) +{ + struct file *file; + int err; + + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); + if (err) + return err; + err = vfs_test_lock(file, lock); + nfsd_close(file); + return err; +} + +/* * LOCKT operation */ __be32 @@ -2789,7 +2808,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_lockt *lockt) { struct inode *inode; - struct file file; struct file_lock file_lock; int error; __be32 status; @@ -2847,16 +2865,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_transform_lock_offset(&file_lock); - /* vfs_test_lock uses the struct file _only_ to resolve the inode. - * since LOCKT doesn't require an OPEN, and therefore a struct - * file may not exist, pass vfs_test_lock a struct file with - * only the dentry:inode set. - */ - memset(&file, 0, sizeof (struct file)); - file.f_path.dentry = cstate->current_fh.fh_dentry; - status = nfs_ok; - error = vfs_test_lock(&file, &file_lock); + error = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock); if (error) { status = nfserrno(error); goto out; -- cgit v0.10.2 From d3fe5ea7cf815c037c90b1f1464ffc1ab5e8601b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 31 Dec 2008 16:06:04 -0500 Subject: NLM: Refactor make_socks() function Clean up: extract common logic in NLM's make_socks() function into a helper. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 3e5f9f0..cf3899a 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -204,6 +204,19 @@ lockd(void *vrqstp) return 0; } +static int create_lockd_listener(struct svc_serv *serv, char *name, + unsigned short port) +{ + struct svc_xprt *xprt; + + xprt = svc_find_xprt(serv, name, 0, 0); + if (xprt == NULL) + return svc_create_xprt(serv, name, port, SVC_SOCK_DEFAULTS); + + svc_xprt_put(xprt); + return 0; +} + /* * Ensure there are active UDP and TCP listeners for lockd. * @@ -217,23 +230,11 @@ lockd(void *vrqstp) static int make_socks(struct svc_serv *serv) { static int warned; - struct svc_xprt *xprt; int err = 0; - xprt = svc_find_xprt(serv, "udp", 0, 0); - if (!xprt) - err = svc_create_xprt(serv, "udp", nlm_udpport, - SVC_SOCK_DEFAULTS); - else - svc_xprt_put(xprt); - if (err >= 0) { - xprt = svc_find_xprt(serv, "tcp", 0, 0); - if (!xprt) - err = svc_create_xprt(serv, "tcp", nlm_tcpport, - SVC_SOCK_DEFAULTS); - else - svc_xprt_put(xprt); - } + err = create_lockd_listener(serv, "udp", nlm_udpport); + if (err >= 0) + err = create_lockd_listener(serv, "tcp", nlm_tcpport); if (err >= 0) { warned = 0; err = 0; -- cgit v0.10.2 From 0dba7c2a9ed3d4a1e58f5d94fffa9f44dbe012e6 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 31 Dec 2008 16:06:11 -0500 Subject: NLM: Clean up flow of control in make_socks() function Clean up: Use Bruce's preferred control flow style in make_socks(). Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index cf3899a..64f1c31 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -230,17 +230,23 @@ static int create_lockd_listener(struct svc_serv *serv, char *name, static int make_socks(struct svc_serv *serv) { static int warned; - int err = 0; + int err; err = create_lockd_listener(serv, "udp", nlm_udpport); - if (err >= 0) - err = create_lockd_listener(serv, "tcp", nlm_tcpport); - if (err >= 0) { - warned = 0; - err = 0; - } else if (warned++ == 0) + if (err < 0) + goto out_err; + + err = create_lockd_listener(serv, "tcp", nlm_tcpport); + if (err < 0) + goto out_err; + + warned = 0; + return 0; + +out_err: + if (warned++ == 0) printk(KERN_WARNING - "lockd_up: makesock failed, error=%d\n", err); + "lockd_up: makesock failed, error=%d\n", err); return err; } -- cgit v0.10.2 From f05ef8db1abe68e3f6fc272efee51bc54ce528c5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 5 Jan 2009 17:19:37 +0000 Subject: CRED: Fix NFSD regression Fix a regression in NFSD's permission checking introduced by the credentials patches. There are two parts to the problem, both in nfsd_setuser(): (1) The return value of set_groups() is -ve if in error, not 0, and should be checked appropriately. 0 indicates success. (2) The UID to use for fs accesses is in new->fsuid, not new->uid (which is 0). This causes CAP_DAC_OVERRIDE to always be set, rather than being cleared if the UID is anything other than 0 after squashing. Reported-by: J. Bruce Fields Signed-off-by: David Howells Acked-by: James Morris Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 0184fe9..c903e04 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -76,10 +76,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) ret = set_groups(new, gi); put_group_info(gi); - if (!ret) + if (ret < 0) goto error; - if (new->uid) + if (new->fsuid) new->cap_effective = cap_drop_nfsd_set(new->cap_effective); else new->cap_effective = cap_raise_nfsd_set(new->cap_effective, -- cgit v0.10.2 From 2779e3ae39645515cb6c1126634f47c28c9e7190 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Mon, 5 Jan 2009 11:12:52 -0600 Subject: svc: Move kfree of deferral record to common code The rqstp structure has a pointer to a svc_deferred_req record that is allocated when requests are deferred. This record is common to all transports and can be freed in common code. Move the kfree of the rq_deferred to the common svc_xprt_release function. This also fixes a memory leak in the RDMA transport which does not kfree the dr structure in it's version of the xpo_release_rqst callback. Signed-off-by: Tom Tucker Signed-off-by: J. Bruce Fields diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 3fe4f10..2961961 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -448,6 +448,9 @@ static void svc_xprt_release(struct svc_rqst *rqstp) rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp); + kfree(rqstp->rq_deferred); + rqstp->rq_deferred = NULL; + svc_free_res_pages(rqstp); rqstp->rq_res.page_len = 0; rqstp->rq_res.page_base = 0; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index cccfa7d..3c10340 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -103,7 +103,6 @@ static void svc_reclassify_socket(struct socket *sock) static void svc_release_skb(struct svc_rqst *rqstp) { struct sk_buff *skb = rqstp->rq_xprt_ctxt; - struct svc_deferred_req *dr = rqstp->rq_deferred; if (skb) { struct svc_sock *svsk = @@ -113,10 +112,6 @@ static void svc_release_skb(struct svc_rqst *rqstp) dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); skb_free_datagram(svsk->sk_sk, skb); } - if (dr) { - rqstp->rq_deferred = NULL; - kfree(dr); - } } union svc_pktinfo_u { -- cgit v0.10.2 From 9a8d248e2d2e9c880ac4561f27fea5dc200655bd Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 6 Jan 2009 13:37:03 -0500 Subject: nfsd: fix double-locks of directory mutex A number of nfsd operations depend on the i_mutex to cover more code than just the fsync, so the approach of 4c728ef583b3d8 "add a vfs_fsync helper" doesn't work for nfsd. Revert the parts of those patches that touch nfsd. Note: we can't, however, remove the logic from vfs_fsync that was needed only for the special case of nfsd, because a vfs_fsync(NULL,...) call can still result indirectly from a stackable filesystem that was called by nfsd. (Thanks to Christoph Hellwig for pointing this out.) Reported-by: Eric Sesterhenn Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 44aa92a..6e50aaa 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -744,16 +744,44 @@ nfsd_close(struct file *filp) fput(filp); } +/* + * Sync a file + * As this calls fsync (not fdatasync) there is no need for a write_inode + * after it. + */ +static inline int nfsd_dosync(struct file *filp, struct dentry *dp, + const struct file_operations *fop) +{ + struct inode *inode = dp->d_inode; + int (*fsync) (struct file *, struct dentry *, int); + int err; + + err = filemap_fdatawrite(inode->i_mapping); + if (err == 0 && fop && (fsync = fop->fsync)) + err = fsync(filp, dp, 0); + if (err == 0) + err = filemap_fdatawait(inode->i_mapping); + + return err; +} + static int nfsd_sync(struct file *filp) { - return vfs_fsync(filp, filp->f_path.dentry, 0); + int err; + struct inode *inode = filp->f_path.dentry->d_inode; + dprintk("nfsd: sync file %s\n", filp->f_path.dentry->d_name.name); + mutex_lock(&inode->i_mutex); + err=nfsd_dosync(filp, filp->f_path.dentry, filp->f_op); + mutex_unlock(&inode->i_mutex); + + return err; } int -nfsd_sync_dir(struct dentry *dentry) +nfsd_sync_dir(struct dentry *dp) { - return vfs_fsync(NULL, dentry, 0); + return nfsd_dosync(NULL, dp, dp->d_inode->i_fop); } /* -- cgit v0.10.2 From 22945e4a1c7454c97f5d8aee1ef526c83fef3223 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Mon, 5 Jan 2009 15:21:19 -0600 Subject: svc: Clean up deferred requests on transport destruction A race between svc_revisit and svc_delete_xprt can result in deferred requests holding references on a transport that can never be recovered because dead transports are not enqueued for subsequent processing. Check for XPT_DEAD in revisit to clean up completing deferrals on a dead transport and sweep a transport's deferred queue to do the same for queued but unprocessed deferrals. Signed-off-by: Tom Tucker Signed-off-by: J. Bruce Fields diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 2961961..a78b879 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -850,6 +850,11 @@ static void svc_age_temp_xprts(unsigned long closure) void svc_delete_xprt(struct svc_xprt *xprt) { struct svc_serv *serv = xprt->xpt_server; + struct svc_deferred_req *dr; + + /* Only do this once */ + if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) + return; dprintk("svc: svc_delete_xprt(%p)\n", xprt); xprt->xpt_ops->xpo_detach(xprt); @@ -864,12 +869,16 @@ void svc_delete_xprt(struct svc_xprt *xprt) * while still attached to a queue, the queue itself * is about to be destroyed (in svc_destroy). */ - if (!test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) { - BUG_ON(atomic_read(&xprt->xpt_ref.refcount) < 2); - if (test_bit(XPT_TEMP, &xprt->xpt_flags)) - serv->sv_tmpcnt--; + if (test_bit(XPT_TEMP, &xprt->xpt_flags)) + serv->sv_tmpcnt--; + + for (dr = svc_deferred_dequeue(xprt); dr; + dr = svc_deferred_dequeue(xprt)) { svc_xprt_put(xprt); + kfree(dr); } + + svc_xprt_put(xprt); spin_unlock_bh(&serv->sv_lock); } @@ -915,17 +924,19 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many) container_of(dreq, struct svc_deferred_req, handle); struct svc_xprt *xprt = dr->xprt; - if (too_many) { + spin_lock(&xprt->xpt_lock); + set_bit(XPT_DEFERRED, &xprt->xpt_flags); + if (too_many || test_bit(XPT_DEAD, &xprt->xpt_flags)) { + spin_unlock(&xprt->xpt_lock); + dprintk("revisit canceled\n"); svc_xprt_put(xprt); kfree(dr); return; } dprintk("revisit queued\n"); dr->xprt = NULL; - spin_lock(&xprt->xpt_lock); list_add(&dr->handle.recent, &xprt->xpt_deferred); spin_unlock(&xprt->xpt_lock); - set_bit(XPT_DEFERRED, &xprt->xpt_flags); svc_xprt_enqueue(xprt); svc_xprt_put(xprt); } -- cgit v0.10.2 From 24c3767e41a6a59d32bb45abe899eb194e6bf1b8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 23 Dec 2008 16:30:12 -0500 Subject: SUNRPC: The sunrpc server code should not be used by out-of-tree modules Signed-off-by: Trond Myklebust Signed-off-by: J. Bruce Fields diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index c996671..4735caa 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -98,7 +98,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, return new; } -EXPORT_SYMBOL(sunrpc_cache_lookup); +EXPORT_SYMBOL_GPL(sunrpc_cache_lookup); static void queue_loose(struct cache_detail *detail, struct cache_head *ch); @@ -173,7 +173,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, cache_put(old, detail); return tmp; } -EXPORT_SYMBOL(sunrpc_cache_update); +EXPORT_SYMBOL_GPL(sunrpc_cache_update); static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); /* @@ -245,7 +245,7 @@ int cache_check(struct cache_detail *detail, cache_put(h, detail); return rv; } -EXPORT_SYMBOL(cache_check); +EXPORT_SYMBOL_GPL(cache_check); /* * caches need to be periodically cleaned. @@ -373,7 +373,7 @@ int cache_register(struct cache_detail *cd) schedule_delayed_work(&cache_cleaner, 0); return 0; } -EXPORT_SYMBOL(cache_register); +EXPORT_SYMBOL_GPL(cache_register); void cache_unregister(struct cache_detail *cd) { @@ -399,7 +399,7 @@ void cache_unregister(struct cache_detail *cd) out: printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name); } -EXPORT_SYMBOL(cache_unregister); +EXPORT_SYMBOL_GPL(cache_unregister); /* clean cache tries to find something to clean * and cleans it. @@ -514,7 +514,7 @@ void cache_flush(void) while (cache_clean() != -1) cond_resched(); } -EXPORT_SYMBOL(cache_flush); +EXPORT_SYMBOL_GPL(cache_flush); void cache_purge(struct cache_detail *detail) { @@ -523,7 +523,7 @@ void cache_purge(struct cache_detail *detail) cache_flush(); detail->flush_time = 1; } -EXPORT_SYMBOL(cache_purge); +EXPORT_SYMBOL_GPL(cache_purge); /* @@ -988,7 +988,7 @@ void qword_add(char **bpp, int *lp, char *str) *bpp = bp; *lp = len; } -EXPORT_SYMBOL(qword_add); +EXPORT_SYMBOL_GPL(qword_add); void qword_addhex(char **bpp, int *lp, char *buf, int blen) { @@ -1017,7 +1017,7 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen) *bpp = bp; *lp = len; } -EXPORT_SYMBOL(qword_addhex); +EXPORT_SYMBOL_GPL(qword_addhex); static void warn_no_listener(struct cache_detail *detail) { @@ -1140,7 +1140,7 @@ int qword_get(char **bpp, char *dest, int bufsize) *dest = '\0'; return len; } -EXPORT_SYMBOL(qword_get); +EXPORT_SYMBOL_GPL(qword_get); /* diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 50b049c..085372e 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -106,7 +106,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) { seq_putc(seq, '\n'); } } -EXPORT_SYMBOL(svc_seq_show); +EXPORT_SYMBOL_GPL(svc_seq_show); /** * rpc_alloc_iostats - allocate an rpc_iostats structure @@ -249,14 +249,14 @@ svc_proc_register(struct svc_stat *statp, const struct file_operations *fops) { return do_register(statp->program->pg_name, statp, fops); } -EXPORT_SYMBOL(svc_proc_register); +EXPORT_SYMBOL_GPL(svc_proc_register); void svc_proc_unregister(const char *name) { remove_proc_entry(name, proc_net_rpc); } -EXPORT_SYMBOL(svc_proc_unregister); +EXPORT_SYMBOL_GPL(svc_proc_unregister); void rpc_proc_init(void) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 54c98d8..c51fed4 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -431,7 +431,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize, { return __svc_create(prog, bufsize, /*npools*/1, family, shutdown); } -EXPORT_SYMBOL(svc_create); +EXPORT_SYMBOL_GPL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, @@ -450,7 +450,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, return serv; } -EXPORT_SYMBOL(svc_create_pooled); +EXPORT_SYMBOL_GPL(svc_create_pooled); /* * Destroy an RPC service. Should be called with appropriate locking to @@ -492,7 +492,7 @@ svc_destroy(struct svc_serv *serv) kfree(serv->sv_pools); kfree(serv); } -EXPORT_SYMBOL(svc_destroy); +EXPORT_SYMBOL_GPL(svc_destroy); /* * Allocate an RPC server's buffer space. @@ -567,7 +567,7 @@ out_thread: out_enomem: return ERR_PTR(-ENOMEM); } -EXPORT_SYMBOL(svc_prepare_thread); +EXPORT_SYMBOL_GPL(svc_prepare_thread); /* * Choose a pool in which to create a new thread, for svc_set_num_threads @@ -689,7 +689,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) return error; } -EXPORT_SYMBOL(svc_set_num_threads); +EXPORT_SYMBOL_GPL(svc_set_num_threads); /* * Called from a server thread as it's exiting. Caller must hold the BKL or @@ -717,7 +717,7 @@ svc_exit_thread(struct svc_rqst *rqstp) if (serv) svc_destroy(serv); } -EXPORT_SYMBOL(svc_exit_thread); +EXPORT_SYMBOL_GPL(svc_exit_thread); #ifdef CONFIG_SUNRPC_REGISTER_V4 @@ -1231,7 +1231,7 @@ err_bad: svc_putnl(resv, ntohl(rpc_stat)); goto sendit; } -EXPORT_SYMBOL(svc_process); +EXPORT_SYMBOL_GPL(svc_process); /* * Return (transport-specific) limit on the rpc payload. diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index a78b879..e588df5 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -440,7 +440,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space) svc_xprt_enqueue(xprt); } } -EXPORT_SYMBOL(svc_reserve); +EXPORT_SYMBOL_GPL(svc_reserve); static void svc_xprt_release(struct svc_rqst *rqstp) { @@ -501,7 +501,7 @@ void svc_wake_up(struct svc_serv *serv) spin_unlock_bh(&pool->sp_lock); } } -EXPORT_SYMBOL(svc_wake_up); +EXPORT_SYMBOL_GPL(svc_wake_up); int svc_port_is_privileged(struct sockaddr *sin) { @@ -743,7 +743,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) serv->sv_stats->netcnt++; return len; } -EXPORT_SYMBOL(svc_recv); +EXPORT_SYMBOL_GPL(svc_recv); /* * Drop request @@ -753,7 +753,7 @@ void svc_drop(struct svc_rqst *rqstp) dprintk("svc: xprt %p dropped request\n", rqstp->rq_xprt); svc_xprt_release(rqstp); } -EXPORT_SYMBOL(svc_drop); +EXPORT_SYMBOL_GPL(svc_drop); /* * Return reply to client. diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index 8a73cbb..e64109b 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -57,13 +57,13 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp) rqstp->rq_authop = aops; return aops->accept(rqstp, authp); } -EXPORT_SYMBOL(svc_authenticate); +EXPORT_SYMBOL_GPL(svc_authenticate); int svc_set_client(struct svc_rqst *rqstp) { return rqstp->rq_authop->set_client(rqstp); } -EXPORT_SYMBOL(svc_set_client); +EXPORT_SYMBOL_GPL(svc_set_client); /* A request, which was authenticated, has now executed. * Time to finalise the credentials and verifier @@ -95,7 +95,7 @@ svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops) spin_unlock(&authtab_lock); return rv; } -EXPORT_SYMBOL(svc_auth_register); +EXPORT_SYMBOL_GPL(svc_auth_register); void svc_auth_unregister(rpc_authflavor_t flavor) @@ -105,7 +105,7 @@ svc_auth_unregister(rpc_authflavor_t flavor) authtab[flavor] = NULL; spin_unlock(&authtab_lock); } -EXPORT_SYMBOL(svc_auth_unregister); +EXPORT_SYMBOL_GPL(svc_auth_unregister); /************************************************** * 'auth_domains' are stored in a hash table indexed by name. @@ -132,7 +132,7 @@ void auth_domain_put(struct auth_domain *dom) spin_unlock(&auth_domain_lock); } } -EXPORT_SYMBOL(auth_domain_put); +EXPORT_SYMBOL_GPL(auth_domain_put); struct auth_domain * auth_domain_lookup(char *name, struct auth_domain *new) @@ -157,10 +157,10 @@ auth_domain_lookup(char *name, struct auth_domain *new) spin_unlock(&auth_domain_lock); return new; } -EXPORT_SYMBOL(auth_domain_lookup); +EXPORT_SYMBOL_GPL(auth_domain_lookup); struct auth_domain *auth_domain_find(char *name) { return auth_domain_lookup(name, NULL); } -EXPORT_SYMBOL(auth_domain_find); +EXPORT_SYMBOL_GPL(auth_domain_find); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 82240e6..5c865e2 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -64,7 +64,7 @@ struct auth_domain *unix_domain_find(char *name) rv = auth_domain_lookup(name, &new->h); } } -EXPORT_SYMBOL(unix_domain_find); +EXPORT_SYMBOL_GPL(unix_domain_find); static void svcauth_unix_domain_release(struct auth_domain *dom) { @@ -358,7 +358,7 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom) else return -ENOMEM; } -EXPORT_SYMBOL(auth_unix_add_addr); +EXPORT_SYMBOL_GPL(auth_unix_add_addr); int auth_unix_forget_old(struct auth_domain *dom) { @@ -370,7 +370,7 @@ int auth_unix_forget_old(struct auth_domain *dom) udom->addr_changes++; return 0; } -EXPORT_SYMBOL(auth_unix_forget_old); +EXPORT_SYMBOL_GPL(auth_unix_forget_old); struct auth_domain *auth_unix_lookup(struct in6_addr *addr) { @@ -395,13 +395,13 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr) cache_put(&ipm->h, &ip_map_cache); return rv; } -EXPORT_SYMBOL(auth_unix_lookup); +EXPORT_SYMBOL_GPL(auth_unix_lookup); void svcauth_unix_purge(void) { cache_purge(&ip_map_cache); } -EXPORT_SYMBOL(svcauth_unix_purge); +EXPORT_SYMBOL_GPL(svcauth_unix_purge); static inline struct ip_map * ip_map_cached_get(struct svc_rqst *rqstp) @@ -714,7 +714,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) return SVC_OK; } -EXPORT_SYMBOL(svcauth_unix_set_client); +EXPORT_SYMBOL_GPL(svcauth_unix_set_client); static int svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 3c10340..5763e64 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -285,7 +285,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose) return -ENOENT; return len; } -EXPORT_SYMBOL(svc_sock_names); +EXPORT_SYMBOL_GPL(svc_sock_names); /* * Check input queue length @@ -1097,7 +1097,7 @@ void svc_sock_update_bufs(struct svc_serv *serv) } spin_unlock_bh(&serv->sv_lock); } -EXPORT_SYMBOL(svc_sock_update_bufs); +EXPORT_SYMBOL_GPL(svc_sock_update_bufs); /* * Initialize socket for RPC use and create svc_sock struct -- cgit v0.10.2 From 30fa8c0157e4591ee2227aaa0b17cd3b0da5e6cb Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Wed, 7 Jan 2009 16:54:30 -0500 Subject: NFSD: FIDs need to take precedence over UUIDs When determining the fsid_type in fh_compose(), the setting of the FID via fsid= export option needs to take precedence over using the UUID device id. Signed-off-by: Steve Dickson Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 019a8a2..9f1ca17 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -484,6 +484,8 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, goto retry; break; } + } else if (exp->ex_flags & NFSEXP_FSID) { + fsid_type = FSID_NUM; } else if (exp->ex_uuid) { if (fhp->fh_maxsize >= 64) { if (root_export) @@ -496,9 +498,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, else fsid_type = FSID_UUID4_INUM; } - } else if (exp->ex_flags & NFSEXP_FSID) - fsid_type = FSID_NUM; - else if (!old_valid_dev(ex_dev)) + } else if (!old_valid_dev(ex_dev)) /* for newer device numbers, we must use a newer fsid format */ fsid_type = FSID_ENCODE_DEV; else -- cgit v0.10.2 From b7aeda40d3010666d2c024c80557b6aa92a1a1ad Mon Sep 17 00:00:00 2001 From: Dean Hildebrand Date: Mon, 15 Dec 2008 19:40:15 +0200 Subject: nfsd: add etoosmall to nfserrno Signed-off-by: Dean Hildebrand Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 5cffeca..6f7f263 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -622,6 +622,7 @@ nfserrno (int errno) { nfserr_badname, -ESRCH }, { nfserr_io, -ETXTBSY }, { nfserr_notsupp, -EOPNOTSUPP }, + { nfserr_toosmall, -ETOOSMALL }, }; int i; -- cgit v0.10.2 From 0407717d8587f60003f4904bff27650cd836c00c Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Mon, 15 Dec 2008 19:40:49 +0200 Subject: nfsd: dprint each op status in nfsd4_proc_compound Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 669461e..9fa60a3 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -946,6 +946,11 @@ encode_op: nfsd4_encode_operation(resp, op); status = op->status; } + + dprintk("nfsv4 compound op %p opcnt %d #%d: %d: status %d\n", + args->ops, args->opcnt, resp->opcnt, op->opnum, + be32_to_cpu(status)); + if (cstate->replay_owner) { nfs4_put_stateowner(cstate->replay_owner); cstate->replay_owner = NULL; -- cgit v0.10.2 From df96fcf02a5fd2ae4e9b09e079dd6ef12d10ecd7 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Mon, 15 Dec 2008 19:41:10 +0200 Subject: nfsd: git rid of nfs4_cb_null_ops declaration There's no use for nfs4_cb_null_ops's declaration in fs/nfsd/nfs4callback.c Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 6d7d8c0..c464181 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -53,9 +53,6 @@ #define NFSPROC4_CB_NULL 0 #define NFSPROC4_CB_COMPOUND 1 -/* declarations */ -static const struct rpc_call_ops nfs4_cb_null_ops; - /* Index of predefined Linux callback client operations */ enum { -- cgit v0.10.2 From 4e65ebf08951326709817e654c149d0a94982e01 Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Mon, 15 Dec 2008 19:41:31 +0200 Subject: nfsd: delete wrong file comment from nfsd/nfs4xdr.c Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index afcdf4b..f65953b 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1,6 +1,4 @@ /* - * fs/nfs/nfs4xdr.c - * * Server-side XDR for NFSv4 * * Copyright (c) 2002 The Regents of the University of Michigan. -- cgit v0.10.2 From 87df4de8073f922a1f643b9fa6ba0412d5529ecf Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Mon, 15 Dec 2008 19:42:03 +0200 Subject: nfsd: last_byte_offset refactor the nfs4 server lock code to use last_byte_offset to compute the last byte covered by the lock. Check for overflow so that the last byte is set to NFS4_MAX_UINT64 if offset + len wraps around. Also, use NFS4_MAX_UINT64 for ~(u64)0 where appropriate. Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e62d0e3..88db7d3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2416,6 +2416,26 @@ out: #define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS) #define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1) +static inline u64 +end_offset(u64 start, u64 len) +{ + u64 end; + + end = start + len; + return end >= start ? end: NFS4_MAX_UINT64; +} + +/* last octet in a range */ +static inline u64 +last_byte_offset(u64 start, u64 len) +{ + u64 end; + + BUG_ON(!len); + end = start + len; + return end > start ? end - 1: NFS4_MAX_UINT64; +} + #define lockownerid_hashval(id) \ ((id) & LOCK_HASH_MASK) @@ -2519,8 +2539,8 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) deny->ld_clientid.cl_id = 0; } deny->ld_start = fl->fl_start; - deny->ld_length = ~(u64)0; - if (fl->fl_end != ~(u64)0) + deny->ld_length = NFS4_MAX_UINT64; + if (fl->fl_end != NFS4_MAX_UINT64) deny->ld_length = fl->fl_end - fl->fl_start + 1; deny->ld_type = NFS4_READ_LT; if (fl->fl_type != F_RDLCK) @@ -2617,7 +2637,7 @@ out: static int check_lock_length(u64 offset, u64 length) { - return ((length == 0) || ((length != ~(u64)0) && + return ((length == 0) || ((length != NFS4_MAX_UINT64) && LOFF_OVERFLOW(offset, length))); } @@ -2737,11 +2757,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, file_lock.fl_lmops = &nfsd_posix_mng_ops; file_lock.fl_start = lock->lk_offset; - if ((lock->lk_length == ~(u64)0) || - LOFF_OVERFLOW(lock->lk_offset, lock->lk_length)) - file_lock.fl_end = ~(u64)0; - else - file_lock.fl_end = lock->lk_offset + lock->lk_length - 1; + file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length); nfs4_transform_lock_offset(&file_lock); /* @@ -2858,10 +2874,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, file_lock.fl_lmops = &nfsd_posix_mng_ops; file_lock.fl_start = lockt->lt_offset; - if ((lockt->lt_length == ~(u64)0) || LOFF_OVERFLOW(lockt->lt_offset, lockt->lt_length)) - file_lock.fl_end = ~(u64)0; - else - file_lock.fl_end = lockt->lt_offset + lockt->lt_length - 1; + file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length); nfs4_transform_lock_offset(&file_lock); @@ -2917,10 +2930,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, file_lock.fl_lmops = &nfsd_posix_mng_ops; file_lock.fl_start = locku->lu_offset; - if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length)) - file_lock.fl_end = ~(u64)0; - else - file_lock.fl_end = locku->lu_offset + locku->lu_length - 1; + file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length); nfs4_transform_lock_offset(&file_lock); /* diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index ea03667..b912311 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -88,6 +88,8 @@ #define NFS4_ACE_GENERIC_EXECUTE 0x001200A0 #define NFS4_ACE_MASK_ALL 0x001F01FF +#define NFS4_MAX_UINT64 (~(u64)0) + enum nfs4_acl_whotype { NFS4_ACL_WHO_NAMED = 0, NFS4_ACL_WHO_OWNER, -- cgit v0.10.2 From db43910cb42285a99f45f7e0a0a32e32d0b61dcf Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Mon, 15 Dec 2008 19:42:24 +0200 Subject: nfsd: get rid of NFSD_VERSION Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 2126940..e19f459 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -23,7 +23,6 @@ /* * nfsd version */ -#define NFSD_VERSION "0.5" #define NFSD_SUPPORTED_MINOR_VERSION 0 /* -- cgit v0.10.2