xref: /linux/fs/lockd/host.c (revision 61ea647ed190af8ed5c5adece2fb5ee33eb3cd22)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/lockd/host.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Management for NLM peer hosts. The nlm_host struct is shared
61da177e4SLinus Torvalds  * between client and server implementation. The only reason to
71da177e4SLinus Torvalds  * do so is to reduce code bloat.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/types.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/in.h>
151b333c54SChuck Lever #include <linux/in6.h>
161da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
175976687aSJeff Layton #include <linux/sunrpc/addr.h>
181da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
191da177e4SLinus Torvalds #include <linux/lockd/lockd.h>
20353ab6e9SIngo Molnar #include <linux/mutex.h>
211da177e4SLinus Torvalds 
2266697bfdSStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h>
2366697bfdSStanislav Kinsbursky 
241b333c54SChuck Lever #include <net/ipv6.h>
251da177e4SLinus Torvalds 
263cf7fb07SStanislav Kinsbursky #include "netns.h"
273cf7fb07SStanislav Kinsbursky 
281da177e4SLinus Torvalds #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
291da177e4SLinus Torvalds #define NLM_HOST_NRHASH		32
301da177e4SLinus Torvalds #define NLM_HOST_REBIND		(60 * HZ)
311447d25eSNeilBrown #define NLM_HOST_EXPIRE		(300 * HZ)
321447d25eSNeilBrown #define NLM_HOST_COLLECT	(120 * HZ)
331da177e4SLinus Torvalds 
34d2df0484SChuck Lever static struct hlist_head	nlm_server_hosts[NLM_HOST_NRHASH];
358ea6ecc8SChuck Lever static struct hlist_head	nlm_client_hosts[NLM_HOST_NRHASH];
36b1137468SJ. Bruce Fields 
37b67bfe0dSSasha Levin #define for_each_host(host, chain, table) \
38b1137468SJ. Bruce Fields 	for ((chain) = (table); \
39b1137468SJ. Bruce Fields 	     (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
40b67bfe0dSSasha Levin 		hlist_for_each_entry((host), (chain), h_hash)
41b1137468SJ. Bruce Fields 
42b67bfe0dSSasha Levin #define for_each_host_safe(host, next, chain, table) \
43b1137468SJ. Bruce Fields 	for ((chain) = (table); \
44b1137468SJ. Bruce Fields 	     (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
45b67bfe0dSSasha Levin 		hlist_for_each_entry_safe((host), (next), \
46b1137468SJ. Bruce Fields 						(chain), h_hash)
47b1137468SJ. Bruce Fields 
48fcc072c7SChuck Lever static unsigned long		nrhosts;
49353ab6e9SIngo Molnar static DEFINE_MUTEX(nlm_host_mutex);
501da177e4SLinus Torvalds 
5127adaddcSStanislav Kinsbursky static void			nlm_gc_hosts(struct net *net);
521da177e4SLinus Torvalds 
537f1ed18bSChuck Lever struct nlm_lookup_host_info {
547f1ed18bSChuck Lever 	const int		server;		/* search for server|client */
5588541c84SChuck Lever 	const struct sockaddr	*sap;		/* address to search for */
5688541c84SChuck Lever 	const size_t		salen;		/* it's length */
577f1ed18bSChuck Lever 	const unsigned short	protocol;	/* transport to search for*/
587f1ed18bSChuck Lever 	const u32		version;	/* NLM version to search for */
597f1ed18bSChuck Lever 	const char		*hostname;	/* remote's hostname */
607f1ed18bSChuck Lever 	const size_t		hostname_len;	/* it's length */
610cb2659bSChuck Lever 	const int		noresvport;	/* use non-priv port */
6266697bfdSStanislav Kinsbursky 	struct net		*net;		/* network namespace to bind */
63b422df91STrond Myklebust 	const struct cred	*cred;
647f1ed18bSChuck Lever };
657f1ed18bSChuck Lever 
66ede2fea0SChuck Lever /*
67ede2fea0SChuck Lever  * Hash function must work well on big- and little-endian platforms
68ede2fea0SChuck Lever  */
__nlm_hash32(const __be32 n)69ede2fea0SChuck Lever static unsigned int __nlm_hash32(const __be32 n)
70ede2fea0SChuck Lever {
71ede2fea0SChuck Lever 	unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
72ede2fea0SChuck Lever 	return hash ^ (hash >> 8);
73ede2fea0SChuck Lever }
74ede2fea0SChuck Lever 
__nlm_hash_addr4(const struct sockaddr * sap)75ede2fea0SChuck Lever static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
76ede2fea0SChuck Lever {
77ede2fea0SChuck Lever 	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
78ede2fea0SChuck Lever 	return __nlm_hash32(sin->sin_addr.s_addr);
79ede2fea0SChuck Lever }
80ede2fea0SChuck Lever 
__nlm_hash_addr6(const struct sockaddr * sap)81ede2fea0SChuck Lever static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
82ede2fea0SChuck Lever {
83ede2fea0SChuck Lever 	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
84ede2fea0SChuck Lever 	const struct in6_addr addr = sin6->sin6_addr;
85ede2fea0SChuck Lever 	return __nlm_hash32(addr.s6_addr32[0]) ^
86ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[1]) ^
87ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[2]) ^
88ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[3]);
89ede2fea0SChuck Lever }
90ede2fea0SChuck Lever 
nlm_hash_address(const struct sockaddr * sap)91ede2fea0SChuck Lever static unsigned int nlm_hash_address(const struct sockaddr *sap)
92ede2fea0SChuck Lever {
93ede2fea0SChuck Lever 	unsigned int hash;
94ede2fea0SChuck Lever 
95ede2fea0SChuck Lever 	switch (sap->sa_family) {
96ede2fea0SChuck Lever 	case AF_INET:
97ede2fea0SChuck Lever 		hash = __nlm_hash_addr4(sap);
98ede2fea0SChuck Lever 		break;
99ede2fea0SChuck Lever 	case AF_INET6:
100ede2fea0SChuck Lever 		hash = __nlm_hash_addr6(sap);
101ede2fea0SChuck Lever 		break;
102ede2fea0SChuck Lever 	default:
103ede2fea0SChuck Lever 		hash = 0;
104ede2fea0SChuck Lever 	}
105ede2fea0SChuck Lever 	return hash & (NLM_HOST_NRHASH - 1);
106ede2fea0SChuck Lever }
107ede2fea0SChuck Lever 
1081da177e4SLinus Torvalds /*
109a7952f40SChuck Lever  * Allocate and initialize an nlm_host.  Common to both client and server.
110a7952f40SChuck Lever  */
nlm_alloc_host(struct nlm_lookup_host_info * ni,struct nsm_handle * nsm)111a7952f40SChuck Lever static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
112a7952f40SChuck Lever 				       struct nsm_handle *nsm)
113a7952f40SChuck Lever {
114a7952f40SChuck Lever 	struct nlm_host *host = NULL;
115a7952f40SChuck Lever 	unsigned long now = jiffies;
116a7952f40SChuck Lever 
117a7952f40SChuck Lever 	if (nsm != NULL)
118c751082cSElena Reshetova 		refcount_inc(&nsm->sm_count);
119a7952f40SChuck Lever 	else {
1200ad95472SAndrey Ryabinin 		nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
121a7952f40SChuck Lever 					ni->hostname, ni->hostname_len);
122a7952f40SChuck Lever 		if (unlikely(nsm == NULL)) {
123a7952f40SChuck Lever 			dprintk("lockd: %s failed; no nsm handle\n",
124a7952f40SChuck Lever 				__func__);
125a7952f40SChuck Lever 			goto out;
126a7952f40SChuck Lever 		}
127a7952f40SChuck Lever 	}
128a7952f40SChuck Lever 
129a7952f40SChuck Lever 	host = kmalloc(sizeof(*host), GFP_KERNEL);
130a7952f40SChuck Lever 	if (unlikely(host == NULL)) {
131a7952f40SChuck Lever 		dprintk("lockd: %s failed; no memory\n", __func__);
132a7952f40SChuck Lever 		nsm_release(nsm);
133a7952f40SChuck Lever 		goto out;
134a7952f40SChuck Lever 	}
135a7952f40SChuck Lever 
136a7952f40SChuck Lever 	memcpy(nlm_addr(host), ni->sap, ni->salen);
137a7952f40SChuck Lever 	host->h_addrlen    = ni->salen;
138a7952f40SChuck Lever 	rpc_set_port(nlm_addr(host), 0);
139a7952f40SChuck Lever 	host->h_srcaddrlen = 0;
140a7952f40SChuck Lever 
141a7952f40SChuck Lever 	host->h_rpcclnt    = NULL;
142a7952f40SChuck Lever 	host->h_name	   = nsm->sm_name;
143a7952f40SChuck Lever 	host->h_version    = ni->version;
144a7952f40SChuck Lever 	host->h_proto      = ni->protocol;
145a7952f40SChuck Lever 	host->h_reclaiming = 0;
146a7952f40SChuck Lever 	host->h_server     = ni->server;
147a7952f40SChuck Lever 	host->h_noresvport = ni->noresvport;
148a7952f40SChuck Lever 	host->h_inuse      = 0;
149a7952f40SChuck Lever 	init_waitqueue_head(&host->h_gracewait);
150a7952f40SChuck Lever 	init_rwsem(&host->h_rwsem);
151a7952f40SChuck Lever 	host->h_state      = 0;
152a7952f40SChuck Lever 	host->h_nsmstate   = 0;
153a7952f40SChuck Lever 	host->h_pidcount   = 0;
154fee21fb5SElena Reshetova 	refcount_set(&host->h_count, 1);
155a7952f40SChuck Lever 	mutex_init(&host->h_mutex);
156a7952f40SChuck Lever 	host->h_nextrebind = now + NLM_HOST_REBIND;
157a7952f40SChuck Lever 	host->h_expires    = now + NLM_HOST_EXPIRE;
158a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_lockowners);
159a7952f40SChuck Lever 	spin_lock_init(&host->h_lock);
160a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_granted);
161a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_reclaim);
162a7952f40SChuck Lever 	host->h_nsmhandle  = nsm;
163a7952f40SChuck Lever 	host->h_addrbuf    = nsm->sm_addrbuf;
16466697bfdSStanislav Kinsbursky 	host->net	   = ni->net;
1653316fb80SZheng Yongjun 	host->h_cred	   = get_cred(ni->cred);
16697f8e625SWolfram Sang 	strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
167a7952f40SChuck Lever 
168a7952f40SChuck Lever out:
169a7952f40SChuck Lever 	return host;
170a7952f40SChuck Lever }
171a7952f40SChuck Lever 
172a7952f40SChuck Lever /*
173723bb5b5SChuck Lever  * Destroy an nlm_host and free associated resources
174723bb5b5SChuck Lever  *
175723bb5b5SChuck Lever  * Caller must hold nlm_host_mutex.
176c53c1bb9SOlaf Kirch  */
nlm_destroy_host_locked(struct nlm_host * host)177723bb5b5SChuck Lever static void nlm_destroy_host_locked(struct nlm_host *host)
178c53c1bb9SOlaf Kirch {
179c53c1bb9SOlaf Kirch 	struct rpc_clnt	*clnt;
180caa4e76bSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(host->net, lockd_net_id);
181c53c1bb9SOlaf Kirch 
182723bb5b5SChuck Lever 	dprintk("lockd: destroy host %s\n", host->h_name);
183723bb5b5SChuck Lever 
184723bb5b5SChuck Lever 	hlist_del_init(&host->h_hash);
185723bb5b5SChuck Lever 
186c53c1bb9SOlaf Kirch 	nsm_unmonitor(host);
187c8c23c42SChuck Lever 	nsm_release(host->h_nsmhandle);
188c53c1bb9SOlaf Kirch 
18934f52e35STrond Myklebust 	clnt = host->h_rpcclnt;
19034f52e35STrond Myklebust 	if (clnt != NULL)
19134f52e35STrond Myklebust 		rpc_shutdown_client(clnt);
192b422df91STrond Myklebust 	put_cred(host->h_cred);
193c53c1bb9SOlaf Kirch 	kfree(host);
194723bb5b5SChuck Lever 
195caa4e76bSStanislav Kinsbursky 	ln->nrhosts--;
196723bb5b5SChuck Lever 	nrhosts--;
197c53c1bb9SOlaf Kirch }
198c53c1bb9SOlaf Kirch 
199d7d20440SChuck Lever /**
200d7d20440SChuck Lever  * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
201d7d20440SChuck Lever  * @sap: network address of server
202d7d20440SChuck Lever  * @salen: length of server address
203d7d20440SChuck Lever  * @protocol: transport protocol to use
204d7d20440SChuck Lever  * @version: NLM protocol version
205d7d20440SChuck Lever  * @hostname: '\0'-terminated hostname of server
2060cb2659bSChuck Lever  * @noresvport: 1 if non-privileged port should be used
207b422df91STrond Myklebust  * @net: pointer to net namespace
208b422df91STrond Myklebust  * @cred: pointer to cred
209d7d20440SChuck Lever  *
210d7d20440SChuck Lever  * Returns an nlm_host structure that matches the passed-in
211d7d20440SChuck Lever  * [server address, transport protocol, NLM version, server hostname].
212d7d20440SChuck Lever  * If one doesn't already exist in the host cache, a new handle is
213d7d20440SChuck Lever  * created and returned.
214c585646dSAdrian Bunk  */
nlmclnt_lookup_host(const struct sockaddr * sap,const size_t salen,const unsigned short protocol,const u32 version,const char * hostname,int noresvport,struct net * net,const struct cred * cred)215d7d20440SChuck Lever struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
216d7d20440SChuck Lever 				     const size_t salen,
217d7d20440SChuck Lever 				     const unsigned short protocol,
2180cb2659bSChuck Lever 				     const u32 version,
2190cb2659bSChuck Lever 				     const char *hostname,
22066697bfdSStanislav Kinsbursky 				     int noresvport,
221b422df91STrond Myklebust 				     struct net *net,
222b422df91STrond Myklebust 				     const struct cred *cred)
223c585646dSAdrian Bunk {
2247f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
2257f1ed18bSChuck Lever 		.server		= 0,
226d7d20440SChuck Lever 		.sap		= sap,
227d7d20440SChuck Lever 		.salen		= salen,
228d7d20440SChuck Lever 		.protocol	= protocol,
2297f1ed18bSChuck Lever 		.version	= version,
2307f1ed18bSChuck Lever 		.hostname	= hostname,
231d7d20440SChuck Lever 		.hostname_len	= strlen(hostname),
2320cb2659bSChuck Lever 		.noresvport	= noresvport,
23366697bfdSStanislav Kinsbursky 		.net		= net,
234b422df91STrond Myklebust 		.cred		= cred,
2357f1ed18bSChuck Lever 	};
2368ea6ecc8SChuck Lever 	struct hlist_head *chain;
2378ea6ecc8SChuck Lever 	struct nlm_host	*host;
2388ea6ecc8SChuck Lever 	struct nsm_handle *nsm = NULL;
239caa4e76bSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
240c98451bdSFrank van Maarseveen 
2417f1ed18bSChuck Lever 	dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
2427f1ed18bSChuck Lever 			(hostname ? hostname : "<none>"), version,
243d7d20440SChuck Lever 			(protocol == IPPROTO_UDP ? "udp" : "tcp"));
2447f1ed18bSChuck Lever 
2458ea6ecc8SChuck Lever 	mutex_lock(&nlm_host_mutex);
2468ea6ecc8SChuck Lever 
2478ea6ecc8SChuck Lever 	chain = &nlm_client_hosts[nlm_hash_address(sap)];
248b67bfe0dSSasha Levin 	hlist_for_each_entry(host, chain, h_hash) {
24966697bfdSStanislav Kinsbursky 		if (host->net != net)
25066697bfdSStanislav Kinsbursky 			continue;
2518ea6ecc8SChuck Lever 		if (!rpc_cmp_addr(nlm_addr(host), sap))
2528ea6ecc8SChuck Lever 			continue;
2538ea6ecc8SChuck Lever 
2548ea6ecc8SChuck Lever 		/* Same address. Share an NSM handle if we already have one */
2558ea6ecc8SChuck Lever 		if (nsm == NULL)
2568ea6ecc8SChuck Lever 			nsm = host->h_nsmhandle;
2578ea6ecc8SChuck Lever 
2588ea6ecc8SChuck Lever 		if (host->h_proto != protocol)
2598ea6ecc8SChuck Lever 			continue;
2608ea6ecc8SChuck Lever 		if (host->h_version != version)
2618ea6ecc8SChuck Lever 			continue;
2628ea6ecc8SChuck Lever 
2638ea6ecc8SChuck Lever 		nlm_get_host(host);
2648ea6ecc8SChuck Lever 		dprintk("lockd: %s found host %s (%s)\n", __func__,
2658ea6ecc8SChuck Lever 			host->h_name, host->h_addrbuf);
2668ea6ecc8SChuck Lever 		goto out;
2678ea6ecc8SChuck Lever 	}
2688ea6ecc8SChuck Lever 
2698ea6ecc8SChuck Lever 	host = nlm_alloc_host(&ni, nsm);
2708ea6ecc8SChuck Lever 	if (unlikely(host == NULL))
2718ea6ecc8SChuck Lever 		goto out;
2728ea6ecc8SChuck Lever 
2738ea6ecc8SChuck Lever 	hlist_add_head(&host->h_hash, chain);
274caa4e76bSStanislav Kinsbursky 	ln->nrhosts++;
2758ea6ecc8SChuck Lever 	nrhosts++;
2768ea6ecc8SChuck Lever 
2778ea6ecc8SChuck Lever 	dprintk("lockd: %s created host %s (%s)\n", __func__,
2788ea6ecc8SChuck Lever 		host->h_name, host->h_addrbuf);
2798ea6ecc8SChuck Lever 
2808ea6ecc8SChuck Lever out:
2818ea6ecc8SChuck Lever 	mutex_unlock(&nlm_host_mutex);
2828ea6ecc8SChuck Lever 	return host;
2838ea6ecc8SChuck Lever }
2848ea6ecc8SChuck Lever 
2858ea6ecc8SChuck Lever /**
2868ea6ecc8SChuck Lever  * nlmclnt_release_host - release client nlm_host
2878ea6ecc8SChuck Lever  * @host: nlm_host to release
2888ea6ecc8SChuck Lever  *
2898ea6ecc8SChuck Lever  */
nlmclnt_release_host(struct nlm_host * host)2908ea6ecc8SChuck Lever void nlmclnt_release_host(struct nlm_host *host)
2918ea6ecc8SChuck Lever {
2928ea6ecc8SChuck Lever 	if (host == NULL)
2938ea6ecc8SChuck Lever 		return;
2948ea6ecc8SChuck Lever 
2958ea6ecc8SChuck Lever 	dprintk("lockd: release client host %s\n", host->h_name);
2968ea6ecc8SChuck Lever 
297a2d30a54STrond Myklebust 	WARN_ON_ONCE(host->h_server);
2988ea6ecc8SChuck Lever 
2994a9be28cSNeilBrown 	if (refcount_dec_and_mutex_lock(&host->h_count, &nlm_host_mutex)) {
300a2d30a54STrond Myklebust 		WARN_ON_ONCE(!list_empty(&host->h_lockowners));
301a2d30a54STrond Myklebust 		WARN_ON_ONCE(!list_empty(&host->h_granted));
302a2d30a54STrond Myklebust 		WARN_ON_ONCE(!list_empty(&host->h_reclaim));
3038ea6ecc8SChuck Lever 
3048ea6ecc8SChuck Lever 		nlm_destroy_host_locked(host);
3058ea6ecc8SChuck Lever 		mutex_unlock(&nlm_host_mutex);
3068ea6ecc8SChuck Lever 	}
307c585646dSAdrian Bunk }
308c585646dSAdrian Bunk 
3096bfbe8afSChuck Lever /**
3106bfbe8afSChuck Lever  * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
3116bfbe8afSChuck Lever  * @rqstp: incoming NLM request
3126bfbe8afSChuck Lever  * @hostname: name of client host
3136bfbe8afSChuck Lever  * @hostname_len: length of client hostname
3146bfbe8afSChuck Lever  *
3156bfbe8afSChuck Lever  * Returns an nlm_host structure that matches the [client address,
3166bfbe8afSChuck Lever  * transport protocol, NLM version, client hostname] of the passed-in
3176bfbe8afSChuck Lever  * NLM request.  If one doesn't already exist in the host cache, a
3186bfbe8afSChuck Lever  * new handle is created and returned.
3196bfbe8afSChuck Lever  *
3206bfbe8afSChuck Lever  * Before possibly creating a new nlm_host, construct a sockaddr
3216bfbe8afSChuck Lever  * for a specific source address in case the local system has
3226bfbe8afSChuck Lever  * multiple network addresses.  The family of the address in
3236bfbe8afSChuck Lever  * rq_daddr is guaranteed to be the same as the family of the
3246bfbe8afSChuck Lever  * address in rq_addr, so it's safe to use the same family for
3256bfbe8afSChuck Lever  * the source address.
326c585646dSAdrian Bunk  */
nlmsvc_lookup_host(const struct svc_rqst * rqstp,const char * hostname,const size_t hostname_len)3276bfbe8afSChuck Lever struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
3286bfbe8afSChuck Lever 				    const char *hostname,
3296bfbe8afSChuck Lever 				    const size_t hostname_len)
330c585646dSAdrian Bunk {
33167216b94SChuck Lever 	struct hlist_head *chain;
33267216b94SChuck Lever 	struct nlm_host	*host = NULL;
33367216b94SChuck Lever 	struct nsm_handle *nsm = NULL;
334849a1cf1SMi Jinlong 	struct sockaddr *src_sap = svc_daddr(rqstp);
335849a1cf1SMi Jinlong 	size_t src_len = rqstp->rq_daddrlen;
3369695c705SStanislav Kinsbursky 	struct net *net = SVC_NET(rqstp);
3377f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
3387f1ed18bSChuck Lever 		.server		= 1,
33988541c84SChuck Lever 		.sap		= svc_addr(rqstp),
34088541c84SChuck Lever 		.salen		= rqstp->rq_addrlen,
3417f1ed18bSChuck Lever 		.protocol	= rqstp->rq_prot,
3427f1ed18bSChuck Lever 		.version	= rqstp->rq_vers,
3437f1ed18bSChuck Lever 		.hostname	= hostname,
3447f1ed18bSChuck Lever 		.hostname_len	= hostname_len,
34566697bfdSStanislav Kinsbursky 		.net		= net,
3467f1ed18bSChuck Lever 	};
3473cf7fb07SStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
348c98451bdSFrank van Maarseveen 
34993f38b6fSAmir Goldstein 	dprintk("lockd: %s(host='%.*s', vers=%u, proto=%s)\n", __func__,
3507f1ed18bSChuck Lever 			(int)hostname_len, hostname, rqstp->rq_vers,
3517f1ed18bSChuck Lever 			(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
3527f1ed18bSChuck Lever 
35367216b94SChuck Lever 	mutex_lock(&nlm_host_mutex);
35467216b94SChuck Lever 
3553cf7fb07SStanislav Kinsbursky 	if (time_after_eq(jiffies, ln->next_gc))
35627adaddcSStanislav Kinsbursky 		nlm_gc_hosts(net);
35767216b94SChuck Lever 
358d2df0484SChuck Lever 	chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
359b67bfe0dSSasha Levin 	hlist_for_each_entry(host, chain, h_hash) {
36066697bfdSStanislav Kinsbursky 		if (host->net != net)
36166697bfdSStanislav Kinsbursky 			continue;
36267216b94SChuck Lever 		if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
36367216b94SChuck Lever 			continue;
36467216b94SChuck Lever 
36567216b94SChuck Lever 		/* Same address. Share an NSM handle if we already have one */
36667216b94SChuck Lever 		if (nsm == NULL)
36767216b94SChuck Lever 			nsm = host->h_nsmhandle;
36867216b94SChuck Lever 
36967216b94SChuck Lever 		if (host->h_proto != ni.protocol)
37067216b94SChuck Lever 			continue;
37167216b94SChuck Lever 		if (host->h_version != ni.version)
37267216b94SChuck Lever 			continue;
37379691836SChuck Lever 		if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
37467216b94SChuck Lever 			continue;
37567216b94SChuck Lever 
37667216b94SChuck Lever 		/* Move to head of hash chain. */
37767216b94SChuck Lever 		hlist_del(&host->h_hash);
37867216b94SChuck Lever 		hlist_add_head(&host->h_hash, chain);
37967216b94SChuck Lever 
38067216b94SChuck Lever 		nlm_get_host(host);
38167216b94SChuck Lever 		dprintk("lockd: %s found host %s (%s)\n",
38267216b94SChuck Lever 			__func__, host->h_name, host->h_addrbuf);
38367216b94SChuck Lever 		goto out;
38467216b94SChuck Lever 	}
38567216b94SChuck Lever 
38667216b94SChuck Lever 	host = nlm_alloc_host(&ni, nsm);
38767216b94SChuck Lever 	if (unlikely(host == NULL))
38867216b94SChuck Lever 		goto out;
38967216b94SChuck Lever 
39079691836SChuck Lever 	memcpy(nlm_srcaddr(host), src_sap, src_len);
39179691836SChuck Lever 	host->h_srcaddrlen = src_len;
39267216b94SChuck Lever 	hlist_add_head(&host->h_hash, chain);
393caa4e76bSStanislav Kinsbursky 	ln->nrhosts++;
39467216b94SChuck Lever 	nrhosts++;
39567216b94SChuck Lever 
396535cb8f3STrond Myklebust 	refcount_inc(&host->h_count);
397535cb8f3STrond Myklebust 
39867216b94SChuck Lever 	dprintk("lockd: %s created host %s (%s)\n",
39967216b94SChuck Lever 		__func__, host->h_name, host->h_addrbuf);
40067216b94SChuck Lever 
40167216b94SChuck Lever out:
40267216b94SChuck Lever 	mutex_unlock(&nlm_host_mutex);
40367216b94SChuck Lever 	return host;
40467216b94SChuck Lever }
40567216b94SChuck Lever 
40667216b94SChuck Lever /**
40767216b94SChuck Lever  * nlmsvc_release_host - release server nlm_host
40867216b94SChuck Lever  * @host: nlm_host to release
40967216b94SChuck Lever  *
41067216b94SChuck Lever  * Host is destroyed later in nlm_gc_host().
41167216b94SChuck Lever  */
nlmsvc_release_host(struct nlm_host * host)41267216b94SChuck Lever void nlmsvc_release_host(struct nlm_host *host)
41367216b94SChuck Lever {
41467216b94SChuck Lever 	if (host == NULL)
41567216b94SChuck Lever 		return;
41667216b94SChuck Lever 
41767216b94SChuck Lever 	dprintk("lockd: release server host %s\n", host->h_name);
41867216b94SChuck Lever 
419a2d30a54STrond Myklebust 	WARN_ON_ONCE(!host->h_server);
420fee21fb5SElena Reshetova 	refcount_dec(&host->h_count);
421c585646dSAdrian Bunk }
422c585646dSAdrian Bunk 
423c585646dSAdrian Bunk /*
4241da177e4SLinus Torvalds  * Create the NLM RPC client for an NLM peer
4251da177e4SLinus Torvalds  */
4261da177e4SLinus Torvalds struct rpc_clnt *
nlm_bind_host(struct nlm_host * host)4271da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
4301da177e4SLinus Torvalds 
4311df40b60SChuck Lever 	dprintk("lockd: nlm_bind_host %s (%s)\n",
4321df40b60SChuck Lever 			host->h_name, host->h_addrbuf);
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	/* Lock host handle */
43550467914STrond Myklebust 	mutex_lock(&host->h_mutex);
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	/* If we've already created an RPC client, check whether
4381da177e4SLinus Torvalds 	 * RPC rebind is required
4391da177e4SLinus Torvalds 	 */
4401da177e4SLinus Torvalds 	if ((clnt = host->h_rpcclnt) != NULL) {
4419b82d88dSCalum Mackay 		nlm_rebind_host(host);
4421da177e4SLinus Torvalds 	} else {
44321051ba6STrond Myklebust 		unsigned long increment = nlmsvc_timeout;
444e1ec7892SChuck Lever 		struct rpc_timeout timeparms = {
445e1ec7892SChuck Lever 			.to_initval	= increment,
446e1ec7892SChuck Lever 			.to_increment	= increment,
447e1ec7892SChuck Lever 			.to_maxval	= increment * 6UL,
448e1ec7892SChuck Lever 			.to_retries	= 5U,
449e1ec7892SChuck Lever 		};
450e1ec7892SChuck Lever 		struct rpc_create_args args = {
45166697bfdSStanislav Kinsbursky 			.net		= host->net,
452e1ec7892SChuck Lever 			.protocol	= host->h_proto,
453b4ed58fdSChuck Lever 			.address	= nlm_addr(host),
454b4ed58fdSChuck Lever 			.addrsize	= host->h_addrlen,
455e1ec7892SChuck Lever 			.timeout	= &timeparms,
456e1ec7892SChuck Lever 			.servername	= host->h_name,
457e1ec7892SChuck Lever 			.program	= &nlm_program,
458e1ec7892SChuck Lever 			.version	= host->h_version,
459e1ec7892SChuck Lever 			.authflavor	= RPC_AUTH_UNIX,
46090bd17c8SJeff Layton 			.flags		= (RPC_CLNT_CREATE_NOPING |
461e6237b6fSTrond Myklebust 					   RPC_CLNT_CREATE_AUTOBIND |
462e6237b6fSTrond Myklebust 					   RPC_CLNT_CREATE_REUSEPORT),
463b422df91STrond Myklebust 			.cred		= host->h_cred,
464e1ec7892SChuck Lever 		};
4651da177e4SLinus Torvalds 
46690bd17c8SJeff Layton 		/*
46790bd17c8SJeff Layton 		 * lockd retries server side blocks automatically so we want
46890bd17c8SJeff Layton 		 * those to be soft RPC calls. Client side calls need to be
46990bd17c8SJeff Layton 		 * hard RPC tasks.
47090bd17c8SJeff Layton 		 */
47190bd17c8SJeff Layton 		if (!host->h_server)
47290bd17c8SJeff Layton 			args.flags |= RPC_CLNT_CREATE_HARDRTRY;
4730cb2659bSChuck Lever 		if (host->h_noresvport)
4740cb2659bSChuck Lever 			args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
4758e35f8e7STrond Myklebust 		if (host->h_srcaddrlen)
4768e35f8e7STrond Myklebust 			args.saddress = nlm_srcaddr(host);
47790bd17c8SJeff Layton 
478e1ec7892SChuck Lever 		clnt = rpc_create(&args);
479e1ec7892SChuck Lever 		if (!IS_ERR(clnt))
4801da177e4SLinus Torvalds 			host->h_rpcclnt = clnt;
481e1ec7892SChuck Lever 		else {
482e1ec7892SChuck Lever 			printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
483e1ec7892SChuck Lever 			clnt = NULL;
484e1ec7892SChuck Lever 		}
4851da177e4SLinus Torvalds 	}
4861da177e4SLinus Torvalds 
48750467914STrond Myklebust 	mutex_unlock(&host->h_mutex);
4881da177e4SLinus Torvalds 	return clnt;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds 
4919b82d88dSCalum Mackay /**
4929b82d88dSCalum Mackay  * nlm_rebind_host - If needed, force a portmap lookup of the peer's lockd port
4939b82d88dSCalum Mackay  * @host: NLM host handle for peer
4949b82d88dSCalum Mackay  *
4959b82d88dSCalum Mackay  * This is not needed when using a connection-oriented protocol, such as TCP.
4969b82d88dSCalum Mackay  * The existing autobind mechanism is sufficient to force a rebind when
4979b82d88dSCalum Mackay  * required, e.g. on connection state transitions.
4981da177e4SLinus Torvalds  */
4991da177e4SLinus Torvalds void
nlm_rebind_host(struct nlm_host * host)5001da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host)
5011da177e4SLinus Torvalds {
5029b82d88dSCalum Mackay 	if (host->h_proto != IPPROTO_UDP)
5039b82d88dSCalum Mackay 		return;
5049b82d88dSCalum Mackay 
5051da177e4SLinus Torvalds 	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
50635f5a422SChuck Lever 		rpc_force_rebind(host->h_rpcclnt);
5071da177e4SLinus Torvalds 		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
5081da177e4SLinus Torvalds 	}
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds /*
5121da177e4SLinus Torvalds  * Increment NLM host count
5131da177e4SLinus Torvalds  */
nlm_get_host(struct nlm_host * host)5141da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host)
5151da177e4SLinus Torvalds {
5161da177e4SLinus Torvalds 	if (host) {
5171da177e4SLinus Torvalds 		dprintk("lockd: get host %s\n", host->h_name);
518fee21fb5SElena Reshetova 		refcount_inc(&host->h_count);
5191da177e4SLinus Torvalds 		host->h_expires = jiffies + NLM_HOST_EXPIRE;
5201da177e4SLinus Torvalds 	}
5211da177e4SLinus Torvalds 	return host;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds 
next_host_state(struct hlist_head * cache,struct nsm_handle * nsm,const struct nlm_reboot * info)524b10e30f6SJ. Bruce Fields static struct nlm_host *next_host_state(struct hlist_head *cache,
525b10e30f6SJ. Bruce Fields 					struct nsm_handle *nsm,
526b10e30f6SJ. Bruce Fields 					const struct nlm_reboot *info)
527b10e30f6SJ. Bruce Fields {
52880c30e8dSChuck Lever 	struct nlm_host *host;
529b10e30f6SJ. Bruce Fields 	struct hlist_head *chain;
530b10e30f6SJ. Bruce Fields 
531b10e30f6SJ. Bruce Fields 	mutex_lock(&nlm_host_mutex);
532b67bfe0dSSasha Levin 	for_each_host(host, chain, cache) {
533b10e30f6SJ. Bruce Fields 		if (host->h_nsmhandle == nsm
534b10e30f6SJ. Bruce Fields 		    && host->h_nsmstate != info->state) {
535b10e30f6SJ. Bruce Fields 			host->h_nsmstate = info->state;
536b10e30f6SJ. Bruce Fields 			host->h_state++;
537b10e30f6SJ. Bruce Fields 
538b10e30f6SJ. Bruce Fields 			nlm_get_host(host);
539b10e30f6SJ. Bruce Fields 			mutex_unlock(&nlm_host_mutex);
540b10e30f6SJ. Bruce Fields 			return host;
541b10e30f6SJ. Bruce Fields 		}
54280c30e8dSChuck Lever 	}
54380c30e8dSChuck Lever 
54480c30e8dSChuck Lever 	mutex_unlock(&nlm_host_mutex);
54580c30e8dSChuck Lever 	return NULL;
54680c30e8dSChuck Lever }
547b10e30f6SJ. Bruce Fields 
5487fefc9cbSChuck Lever /**
5497fefc9cbSChuck Lever  * nlm_host_rebooted - Release all resources held by rebooted host
5500ad95472SAndrey Ryabinin  * @net:  network namespace
5517fefc9cbSChuck Lever  * @info: pointer to decoded results of NLM_SM_NOTIFY call
5527fefc9cbSChuck Lever  *
5537fefc9cbSChuck Lever  * We were notified that the specified host has rebooted.  Release
5547fefc9cbSChuck Lever  * all resources held by that peer.
555cf712c24SOlaf Kirch  */
nlm_host_rebooted(const struct net * net,const struct nlm_reboot * info)5560ad95472SAndrey Ryabinin void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
557cf712c24SOlaf Kirch {
5585c8dd29cSOlaf Kirch 	struct nsm_handle *nsm;
5590cea3276SOlaf Kirch 	struct nlm_host	*host;
560cf712c24SOlaf Kirch 
5610ad95472SAndrey Ryabinin 	nsm = nsm_reboot_lookup(net, info);
5628c7378fdSChuck Lever 	if (unlikely(nsm == NULL))
563cf712c24SOlaf Kirch 		return;
5645c8dd29cSOlaf Kirch 
5655c8dd29cSOlaf Kirch 	/* Mark all hosts tied to this NSM state as having rebooted.
5665c8dd29cSOlaf Kirch 	 * We run the loop repeatedly, because we drop the host table
5675c8dd29cSOlaf Kirch 	 * lock for this.
5685c8dd29cSOlaf Kirch 	 * To avoid processing a host several times, we match the nsmstate.
5695c8dd29cSOlaf Kirch 	 */
570d2df0484SChuck Lever 	while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
571cf712c24SOlaf Kirch 		nlmsvc_free_host_resources(host);
57267216b94SChuck Lever 		nlmsvc_release_host(host);
5735c8dd29cSOlaf Kirch 	}
5748ea6ecc8SChuck Lever 	while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
5758ea6ecc8SChuck Lever 		nlmclnt_recovery(host);
5768ea6ecc8SChuck Lever 		nlmclnt_release_host(host);
5778ea6ecc8SChuck Lever 	}
5788ea6ecc8SChuck Lever 
579cdd30fa1SJeff Layton 	nsm_release(nsm);
580cf712c24SOlaf Kirch }
581cf712c24SOlaf Kirch 
nlm_complain_hosts(struct net * net)582d5850ff9SStanislav Kinsbursky static void nlm_complain_hosts(struct net *net)
583d5850ff9SStanislav Kinsbursky {
584d5850ff9SStanislav Kinsbursky 	struct hlist_head *chain;
585d5850ff9SStanislav Kinsbursky 	struct nlm_host	*host;
586d5850ff9SStanislav Kinsbursky 
587d5850ff9SStanislav Kinsbursky 	if (net) {
588d5850ff9SStanislav Kinsbursky 		struct lockd_net *ln = net_generic(net, lockd_net_id);
589d5850ff9SStanislav Kinsbursky 
590d5850ff9SStanislav Kinsbursky 		if (ln->nrhosts == 0)
591d5850ff9SStanislav Kinsbursky 			return;
592e919b076SVasily Averin 		pr_warn("lockd: couldn't shutdown host module for net %x!\n",
593e919b076SVasily Averin 			net->ns.inum);
594e919b076SVasily Averin 		dprintk("lockd: %lu hosts left in net %x:\n", ln->nrhosts,
595e919b076SVasily Averin 			net->ns.inum);
596d5850ff9SStanislav Kinsbursky 	} else {
597d5850ff9SStanislav Kinsbursky 		if (nrhosts == 0)
598d5850ff9SStanislav Kinsbursky 			return;
599d5850ff9SStanislav Kinsbursky 		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
600d5850ff9SStanislav Kinsbursky 		dprintk("lockd: %lu hosts left:\n", nrhosts);
601d5850ff9SStanislav Kinsbursky 	}
602d5850ff9SStanislav Kinsbursky 
603b67bfe0dSSasha Levin 	for_each_host(host, chain, nlm_server_hosts) {
604d5850ff9SStanislav Kinsbursky 		if (net && host->net != net)
605d5850ff9SStanislav Kinsbursky 			continue;
606e919b076SVasily Averin 		dprintk("       %s (cnt %d use %d exp %ld net %x)\n",
607fee21fb5SElena Reshetova 			host->h_name, refcount_read(&host->h_count),
608e919b076SVasily Averin 			host->h_inuse, host->h_expires, host->net->ns.inum);
609d5850ff9SStanislav Kinsbursky 	}
610d5850ff9SStanislav Kinsbursky }
611d5850ff9SStanislav Kinsbursky 
6121da177e4SLinus Torvalds void
nlm_shutdown_hosts_net(struct net * net)6133b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(struct net *net)
6141da177e4SLinus Torvalds {
6150cea3276SOlaf Kirch 	struct hlist_head *chain;
6161da177e4SLinus Torvalds 	struct nlm_host	*host;
6171da177e4SLinus Torvalds 
618353ab6e9SIngo Molnar 	mutex_lock(&nlm_host_mutex);
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds 	/* First, make all hosts eligible for gc */
621e919b076SVasily Averin 	dprintk("lockd: nuking all hosts in net %x...\n",
622e919b076SVasily Averin 		net ? net->ns.inum : 0);
623b67bfe0dSSasha Levin 	for_each_host(host, chain, nlm_server_hosts) {
6243b64739fSStanislav Kinsbursky 		if (net && host->net != net)
6253b64739fSStanislav Kinsbursky 			continue;
6261da177e4SLinus Torvalds 		host->h_expires = jiffies - 1;
627d801b861SJeff Layton 		if (host->h_rpcclnt) {
628d801b861SJeff Layton 			rpc_shutdown_client(host->h_rpcclnt);
629d801b861SJeff Layton 			host->h_rpcclnt = NULL;
630d801b861SJeff Layton 		}
631*bfca7a6fSJeff Layton 		nlmsvc_free_host_resources(host);
632d801b861SJeff Layton 	}
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	/* Then, perform a garbage collection pass */
63527adaddcSStanislav Kinsbursky 	nlm_gc_hosts(net);
636d5850ff9SStanislav Kinsbursky 	nlm_complain_hosts(net);
6379e137ed5SVasily Averin 	mutex_unlock(&nlm_host_mutex);
6383b64739fSStanislav Kinsbursky }
6393b64739fSStanislav Kinsbursky 
6403b64739fSStanislav Kinsbursky /*
6413b64739fSStanislav Kinsbursky  * Shut down the hosts module.
6423b64739fSStanislav Kinsbursky  * Note that this routine is called only at server shutdown time.
6433b64739fSStanislav Kinsbursky  */
6443b64739fSStanislav Kinsbursky void
nlm_shutdown_hosts(void)6453b64739fSStanislav Kinsbursky nlm_shutdown_hosts(void)
6463b64739fSStanislav Kinsbursky {
647e2edaa98SStanislav Kinsbursky 	dprintk("lockd: shutting down host module\n");
6483b64739fSStanislav Kinsbursky 	nlm_shutdown_hosts_net(NULL);
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
6511da177e4SLinus Torvalds /*
6521da177e4SLinus Torvalds  * Garbage collect any unused NLM hosts.
6531da177e4SLinus Torvalds  * This GC combines reference counting for async operations with
6541da177e4SLinus Torvalds  * mark & sweep for resources held by remote clients.
6551da177e4SLinus Torvalds  */
6561da177e4SLinus Torvalds static void
nlm_gc_hosts(struct net * net)65727adaddcSStanislav Kinsbursky nlm_gc_hosts(struct net *net)
6581da177e4SLinus Torvalds {
6590cea3276SOlaf Kirch 	struct hlist_head *chain;
660b67bfe0dSSasha Levin 	struct hlist_node *next;
6610cea3276SOlaf Kirch 	struct nlm_host	*host;
6621da177e4SLinus Torvalds 
663e919b076SVasily Averin 	dprintk("lockd: host garbage collection for net %x\n",
664e919b076SVasily Averin 		net ? net->ns.inum : 0);
665b67bfe0dSSasha Levin 	for_each_host(host, chain, nlm_server_hosts) {
66627adaddcSStanislav Kinsbursky 		if (net && host->net != net)
66727adaddcSStanislav Kinsbursky 			continue;
6681da177e4SLinus Torvalds 		host->h_inuse = 0;
66927adaddcSStanislav Kinsbursky 	}
6701da177e4SLinus Torvalds 
6711da177e4SLinus Torvalds 	/* Mark all hosts that hold locks, blocks or shares */
672b26411f8SStanislav Kinsbursky 	nlmsvc_mark_resources(net);
6731da177e4SLinus Torvalds 
674b67bfe0dSSasha Levin 	for_each_host_safe(host, next, chain, nlm_server_hosts) {
67527adaddcSStanislav Kinsbursky 		if (net && host->net != net)
67627adaddcSStanislav Kinsbursky 			continue;
677535cb8f3STrond Myklebust 		if (host->h_inuse || time_before(jiffies, host->h_expires)) {
678b1137468SJ. Bruce Fields 			dprintk("nlm_gc_hosts skipping %s "
679e919b076SVasily Averin 				"(cnt %d use %d exp %ld net %x)\n",
680fee21fb5SElena Reshetova 				host->h_name, refcount_read(&host->h_count),
681e919b076SVasily Averin 				host->h_inuse, host->h_expires,
682e919b076SVasily Averin 				host->net->ns.inum);
6831da177e4SLinus Torvalds 			continue;
6841da177e4SLinus Torvalds 		}
685535cb8f3STrond Myklebust 		if (refcount_dec_if_one(&host->h_count))
686723bb5b5SChuck Lever 			nlm_destroy_host_locked(host);
6871da177e4SLinus Torvalds 	}
6881da177e4SLinus Torvalds 
6893cf7fb07SStanislav Kinsbursky 	if (net) {
6903cf7fb07SStanislav Kinsbursky 		struct lockd_net *ln = net_generic(net, lockd_net_id);
6913cf7fb07SStanislav Kinsbursky 
6923cf7fb07SStanislav Kinsbursky 		ln->next_gc = jiffies + NLM_HOST_COLLECT;
6933cf7fb07SStanislav Kinsbursky 	}
6941da177e4SLinus Torvalds }
695