xref: /openbmc/linux/fs/lockd/host.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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 {
120a7952f40SChuck Lever 		host = NULL;
1210ad95472SAndrey Ryabinin 		nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
122a7952f40SChuck Lever 					ni->hostname, ni->hostname_len);
123a7952f40SChuck Lever 		if (unlikely(nsm == NULL)) {
124a7952f40SChuck Lever 			dprintk("lockd: %s failed; no nsm handle\n",
125a7952f40SChuck Lever 				__func__);
126a7952f40SChuck Lever 			goto out;
127a7952f40SChuck Lever 		}
128a7952f40SChuck Lever 	}
129a7952f40SChuck Lever 
130a7952f40SChuck Lever 	host = kmalloc(sizeof(*host), GFP_KERNEL);
131a7952f40SChuck Lever 	if (unlikely(host == NULL)) {
132a7952f40SChuck Lever 		dprintk("lockd: %s failed; no memory\n", __func__);
133a7952f40SChuck Lever 		nsm_release(nsm);
134a7952f40SChuck Lever 		goto out;
135a7952f40SChuck Lever 	}
136a7952f40SChuck Lever 
137a7952f40SChuck Lever 	memcpy(nlm_addr(host), ni->sap, ni->salen);
138a7952f40SChuck Lever 	host->h_addrlen    = ni->salen;
139a7952f40SChuck Lever 	rpc_set_port(nlm_addr(host), 0);
140a7952f40SChuck Lever 	host->h_srcaddrlen = 0;
141a7952f40SChuck Lever 
142a7952f40SChuck Lever 	host->h_rpcclnt    = NULL;
143a7952f40SChuck Lever 	host->h_name	   = nsm->sm_name;
144a7952f40SChuck Lever 	host->h_version    = ni->version;
145a7952f40SChuck Lever 	host->h_proto      = ni->protocol;
146a7952f40SChuck Lever 	host->h_reclaiming = 0;
147a7952f40SChuck Lever 	host->h_server     = ni->server;
148a7952f40SChuck Lever 	host->h_noresvport = ni->noresvport;
149a7952f40SChuck Lever 	host->h_inuse      = 0;
150a7952f40SChuck Lever 	init_waitqueue_head(&host->h_gracewait);
151a7952f40SChuck Lever 	init_rwsem(&host->h_rwsem);
152a7952f40SChuck Lever 	host->h_state      = 0;
153a7952f40SChuck Lever 	host->h_nsmstate   = 0;
154a7952f40SChuck Lever 	host->h_pidcount   = 0;
155fee21fb5SElena Reshetova 	refcount_set(&host->h_count, 1);
156a7952f40SChuck Lever 	mutex_init(&host->h_mutex);
157a7952f40SChuck Lever 	host->h_nextrebind = now + NLM_HOST_REBIND;
158a7952f40SChuck Lever 	host->h_expires    = now + NLM_HOST_EXPIRE;
159a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_lockowners);
160a7952f40SChuck Lever 	spin_lock_init(&host->h_lock);
161a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_granted);
162a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_reclaim);
163a7952f40SChuck Lever 	host->h_nsmhandle  = nsm;
164a7952f40SChuck Lever 	host->h_addrbuf    = nsm->sm_addrbuf;
16566697bfdSStanislav Kinsbursky 	host->net	   = ni->net;
1663316fb80SZheng Yongjun 	host->h_cred	   = get_cred(ni->cred);
16797f8e625SWolfram Sang 	strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
168a7952f40SChuck Lever 
169a7952f40SChuck Lever out:
170a7952f40SChuck Lever 	return host;
171a7952f40SChuck Lever }
172a7952f40SChuck Lever 
173a7952f40SChuck Lever /*
174723bb5b5SChuck Lever  * Destroy an nlm_host and free associated resources
175723bb5b5SChuck Lever  *
176723bb5b5SChuck Lever  * Caller must hold nlm_host_mutex.
177c53c1bb9SOlaf Kirch  */
nlm_destroy_host_locked(struct nlm_host * host)178723bb5b5SChuck Lever static void nlm_destroy_host_locked(struct nlm_host *host)
179c53c1bb9SOlaf Kirch {
180c53c1bb9SOlaf Kirch 	struct rpc_clnt	*clnt;
181caa4e76bSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(host->net, lockd_net_id);
182c53c1bb9SOlaf Kirch 
183723bb5b5SChuck Lever 	dprintk("lockd: destroy host %s\n", host->h_name);
184723bb5b5SChuck Lever 
185723bb5b5SChuck Lever 	hlist_del_init(&host->h_hash);
186723bb5b5SChuck Lever 
187c53c1bb9SOlaf Kirch 	nsm_unmonitor(host);
188c8c23c42SChuck Lever 	nsm_release(host->h_nsmhandle);
189c53c1bb9SOlaf Kirch 
19034f52e35STrond Myklebust 	clnt = host->h_rpcclnt;
19134f52e35STrond Myklebust 	if (clnt != NULL)
19234f52e35STrond Myklebust 		rpc_shutdown_client(clnt);
193b422df91STrond Myklebust 	put_cred(host->h_cred);
194c53c1bb9SOlaf Kirch 	kfree(host);
195723bb5b5SChuck Lever 
196caa4e76bSStanislav Kinsbursky 	ln->nrhosts--;
197723bb5b5SChuck Lever 	nrhosts--;
198c53c1bb9SOlaf Kirch }
199c53c1bb9SOlaf Kirch 
200d7d20440SChuck Lever /**
201d7d20440SChuck Lever  * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
202d7d20440SChuck Lever  * @sap: network address of server
203d7d20440SChuck Lever  * @salen: length of server address
204d7d20440SChuck Lever  * @protocol: transport protocol to use
205d7d20440SChuck Lever  * @version: NLM protocol version
206d7d20440SChuck Lever  * @hostname: '\0'-terminated hostname of server
2070cb2659bSChuck Lever  * @noresvport: 1 if non-privileged port should be used
208b422df91STrond Myklebust  * @net: pointer to net namespace
209b422df91STrond Myklebust  * @cred: pointer to cred
210d7d20440SChuck Lever  *
211d7d20440SChuck Lever  * Returns an nlm_host structure that matches the passed-in
212d7d20440SChuck Lever  * [server address, transport protocol, NLM version, server hostname].
213d7d20440SChuck Lever  * If one doesn't already exist in the host cache, a new handle is
214d7d20440SChuck Lever  * created and returned.
215c585646dSAdrian 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)216d7d20440SChuck Lever struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
217d7d20440SChuck Lever 				     const size_t salen,
218d7d20440SChuck Lever 				     const unsigned short protocol,
2190cb2659bSChuck Lever 				     const u32 version,
2200cb2659bSChuck Lever 				     const char *hostname,
22166697bfdSStanislav Kinsbursky 				     int noresvport,
222b422df91STrond Myklebust 				     struct net *net,
223b422df91STrond Myklebust 				     const struct cred *cred)
224c585646dSAdrian Bunk {
2257f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
2267f1ed18bSChuck Lever 		.server		= 0,
227d7d20440SChuck Lever 		.sap		= sap,
228d7d20440SChuck Lever 		.salen		= salen,
229d7d20440SChuck Lever 		.protocol	= protocol,
2307f1ed18bSChuck Lever 		.version	= version,
2317f1ed18bSChuck Lever 		.hostname	= hostname,
232d7d20440SChuck Lever 		.hostname_len	= strlen(hostname),
2330cb2659bSChuck Lever 		.noresvport	= noresvport,
23466697bfdSStanislav Kinsbursky 		.net		= net,
235b422df91STrond Myklebust 		.cred		= cred,
2367f1ed18bSChuck Lever 	};
2378ea6ecc8SChuck Lever 	struct hlist_head *chain;
2388ea6ecc8SChuck Lever 	struct nlm_host	*host;
2398ea6ecc8SChuck Lever 	struct nsm_handle *nsm = NULL;
240caa4e76bSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
241c98451bdSFrank van Maarseveen 
2427f1ed18bSChuck Lever 	dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
2437f1ed18bSChuck Lever 			(hostname ? hostname : "<none>"), version,
244d7d20440SChuck Lever 			(protocol == IPPROTO_UDP ? "udp" : "tcp"));
2457f1ed18bSChuck Lever 
2468ea6ecc8SChuck Lever 	mutex_lock(&nlm_host_mutex);
2478ea6ecc8SChuck Lever 
2488ea6ecc8SChuck Lever 	chain = &nlm_client_hosts[nlm_hash_address(sap)];
249b67bfe0dSSasha Levin 	hlist_for_each_entry(host, chain, h_hash) {
25066697bfdSStanislav Kinsbursky 		if (host->net != net)
25166697bfdSStanislav Kinsbursky 			continue;
2528ea6ecc8SChuck Lever 		if (!rpc_cmp_addr(nlm_addr(host), sap))
2538ea6ecc8SChuck Lever 			continue;
2548ea6ecc8SChuck Lever 
2558ea6ecc8SChuck Lever 		/* Same address. Share an NSM handle if we already have one */
2568ea6ecc8SChuck Lever 		if (nsm == NULL)
2578ea6ecc8SChuck Lever 			nsm = host->h_nsmhandle;
2588ea6ecc8SChuck Lever 
2598ea6ecc8SChuck Lever 		if (host->h_proto != protocol)
2608ea6ecc8SChuck Lever 			continue;
2618ea6ecc8SChuck Lever 		if (host->h_version != version)
2628ea6ecc8SChuck Lever 			continue;
2638ea6ecc8SChuck Lever 
2648ea6ecc8SChuck Lever 		nlm_get_host(host);
2658ea6ecc8SChuck Lever 		dprintk("lockd: %s found host %s (%s)\n", __func__,
2668ea6ecc8SChuck Lever 			host->h_name, host->h_addrbuf);
2678ea6ecc8SChuck Lever 		goto out;
2688ea6ecc8SChuck Lever 	}
2698ea6ecc8SChuck Lever 
2708ea6ecc8SChuck Lever 	host = nlm_alloc_host(&ni, nsm);
2718ea6ecc8SChuck Lever 	if (unlikely(host == NULL))
2728ea6ecc8SChuck Lever 		goto out;
2738ea6ecc8SChuck Lever 
2748ea6ecc8SChuck Lever 	hlist_add_head(&host->h_hash, chain);
275caa4e76bSStanislav Kinsbursky 	ln->nrhosts++;
2768ea6ecc8SChuck Lever 	nrhosts++;
2778ea6ecc8SChuck Lever 
2788ea6ecc8SChuck Lever 	dprintk("lockd: %s created host %s (%s)\n", __func__,
2798ea6ecc8SChuck Lever 		host->h_name, host->h_addrbuf);
2808ea6ecc8SChuck Lever 
2818ea6ecc8SChuck Lever out:
2828ea6ecc8SChuck Lever 	mutex_unlock(&nlm_host_mutex);
2838ea6ecc8SChuck Lever 	return host;
2848ea6ecc8SChuck Lever }
2858ea6ecc8SChuck Lever 
2868ea6ecc8SChuck Lever /**
2878ea6ecc8SChuck Lever  * nlmclnt_release_host - release client nlm_host
2888ea6ecc8SChuck Lever  * @host: nlm_host to release
2898ea6ecc8SChuck Lever  *
2908ea6ecc8SChuck Lever  */
nlmclnt_release_host(struct nlm_host * host)2918ea6ecc8SChuck Lever void nlmclnt_release_host(struct nlm_host *host)
2928ea6ecc8SChuck Lever {
2938ea6ecc8SChuck Lever 	if (host == NULL)
2948ea6ecc8SChuck Lever 		return;
2958ea6ecc8SChuck Lever 
2968ea6ecc8SChuck Lever 	dprintk("lockd: release client host %s\n", host->h_name);
2978ea6ecc8SChuck Lever 
298a2d30a54STrond Myklebust 	WARN_ON_ONCE(host->h_server);
2998ea6ecc8SChuck Lever 
3004a9be28cSNeilBrown 	if (refcount_dec_and_mutex_lock(&host->h_count, &nlm_host_mutex)) {
301a2d30a54STrond Myklebust 		WARN_ON_ONCE(!list_empty(&host->h_lockowners));
302a2d30a54STrond Myklebust 		WARN_ON_ONCE(!list_empty(&host->h_granted));
303a2d30a54STrond Myklebust 		WARN_ON_ONCE(!list_empty(&host->h_reclaim));
3048ea6ecc8SChuck Lever 
3058ea6ecc8SChuck Lever 		nlm_destroy_host_locked(host);
3068ea6ecc8SChuck Lever 		mutex_unlock(&nlm_host_mutex);
3078ea6ecc8SChuck Lever 	}
308c585646dSAdrian Bunk }
309c585646dSAdrian Bunk 
3106bfbe8afSChuck Lever /**
3116bfbe8afSChuck Lever  * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
3126bfbe8afSChuck Lever  * @rqstp: incoming NLM request
3136bfbe8afSChuck Lever  * @hostname: name of client host
3146bfbe8afSChuck Lever  * @hostname_len: length of client hostname
3156bfbe8afSChuck Lever  *
3166bfbe8afSChuck Lever  * Returns an nlm_host structure that matches the [client address,
3176bfbe8afSChuck Lever  * transport protocol, NLM version, client hostname] of the passed-in
3186bfbe8afSChuck Lever  * NLM request.  If one doesn't already exist in the host cache, a
3196bfbe8afSChuck Lever  * new handle is created and returned.
3206bfbe8afSChuck Lever  *
3216bfbe8afSChuck Lever  * Before possibly creating a new nlm_host, construct a sockaddr
3226bfbe8afSChuck Lever  * for a specific source address in case the local system has
3236bfbe8afSChuck Lever  * multiple network addresses.  The family of the address in
3246bfbe8afSChuck Lever  * rq_daddr is guaranteed to be the same as the family of the
3256bfbe8afSChuck Lever  * address in rq_addr, so it's safe to use the same family for
3266bfbe8afSChuck Lever  * the source address.
327c585646dSAdrian Bunk  */
nlmsvc_lookup_host(const struct svc_rqst * rqstp,const char * hostname,const size_t hostname_len)3286bfbe8afSChuck Lever struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
3296bfbe8afSChuck Lever 				    const char *hostname,
3306bfbe8afSChuck Lever 				    const size_t hostname_len)
331c585646dSAdrian Bunk {
33267216b94SChuck Lever 	struct hlist_head *chain;
33367216b94SChuck Lever 	struct nlm_host	*host = NULL;
33467216b94SChuck Lever 	struct nsm_handle *nsm = NULL;
335849a1cf1SMi Jinlong 	struct sockaddr *src_sap = svc_daddr(rqstp);
336849a1cf1SMi Jinlong 	size_t src_len = rqstp->rq_daddrlen;
3379695c705SStanislav Kinsbursky 	struct net *net = SVC_NET(rqstp);
3387f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
3397f1ed18bSChuck Lever 		.server		= 1,
34088541c84SChuck Lever 		.sap		= svc_addr(rqstp),
34188541c84SChuck Lever 		.salen		= rqstp->rq_addrlen,
3427f1ed18bSChuck Lever 		.protocol	= rqstp->rq_prot,
3437f1ed18bSChuck Lever 		.version	= rqstp->rq_vers,
3447f1ed18bSChuck Lever 		.hostname	= hostname,
3457f1ed18bSChuck Lever 		.hostname_len	= hostname_len,
34666697bfdSStanislav Kinsbursky 		.net		= net,
3477f1ed18bSChuck Lever 	};
3483cf7fb07SStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
349c98451bdSFrank van Maarseveen 
35093f38b6fSAmir Goldstein 	dprintk("lockd: %s(host='%.*s', vers=%u, proto=%s)\n", __func__,
3517f1ed18bSChuck Lever 			(int)hostname_len, hostname, rqstp->rq_vers,
3527f1ed18bSChuck Lever 			(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
3537f1ed18bSChuck Lever 
35467216b94SChuck Lever 	mutex_lock(&nlm_host_mutex);
35567216b94SChuck Lever 
3563cf7fb07SStanislav Kinsbursky 	if (time_after_eq(jiffies, ln->next_gc))
35727adaddcSStanislav Kinsbursky 		nlm_gc_hosts(net);
35867216b94SChuck Lever 
359d2df0484SChuck Lever 	chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
360b67bfe0dSSasha Levin 	hlist_for_each_entry(host, chain, h_hash) {
36166697bfdSStanislav Kinsbursky 		if (host->net != net)
36266697bfdSStanislav Kinsbursky 			continue;
36367216b94SChuck Lever 		if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
36467216b94SChuck Lever 			continue;
36567216b94SChuck Lever 
36667216b94SChuck Lever 		/* Same address. Share an NSM handle if we already have one */
36767216b94SChuck Lever 		if (nsm == NULL)
36867216b94SChuck Lever 			nsm = host->h_nsmhandle;
36967216b94SChuck Lever 
37067216b94SChuck Lever 		if (host->h_proto != ni.protocol)
37167216b94SChuck Lever 			continue;
37267216b94SChuck Lever 		if (host->h_version != ni.version)
37367216b94SChuck Lever 			continue;
37479691836SChuck Lever 		if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
37567216b94SChuck Lever 			continue;
37667216b94SChuck Lever 
37767216b94SChuck Lever 		/* Move to head of hash chain. */
37867216b94SChuck Lever 		hlist_del(&host->h_hash);
37967216b94SChuck Lever 		hlist_add_head(&host->h_hash, chain);
38067216b94SChuck Lever 
38167216b94SChuck Lever 		nlm_get_host(host);
38267216b94SChuck Lever 		dprintk("lockd: %s found host %s (%s)\n",
38367216b94SChuck Lever 			__func__, host->h_name, host->h_addrbuf);
38467216b94SChuck Lever 		goto out;
38567216b94SChuck Lever 	}
38667216b94SChuck Lever 
38767216b94SChuck Lever 	host = nlm_alloc_host(&ni, nsm);
38867216b94SChuck Lever 	if (unlikely(host == NULL))
38967216b94SChuck Lever 		goto out;
39067216b94SChuck Lever 
39179691836SChuck Lever 	memcpy(nlm_srcaddr(host), src_sap, src_len);
39279691836SChuck Lever 	host->h_srcaddrlen = src_len;
39367216b94SChuck Lever 	hlist_add_head(&host->h_hash, chain);
394caa4e76bSStanislav Kinsbursky 	ln->nrhosts++;
39567216b94SChuck Lever 	nrhosts++;
39667216b94SChuck Lever 
397535cb8f3STrond Myklebust 	refcount_inc(&host->h_count);
398535cb8f3STrond Myklebust 
39967216b94SChuck Lever 	dprintk("lockd: %s created host %s (%s)\n",
40067216b94SChuck Lever 		__func__, host->h_name, host->h_addrbuf);
40167216b94SChuck Lever 
40267216b94SChuck Lever out:
40367216b94SChuck Lever 	mutex_unlock(&nlm_host_mutex);
40467216b94SChuck Lever 	return host;
40567216b94SChuck Lever }
40667216b94SChuck Lever 
40767216b94SChuck Lever /**
40867216b94SChuck Lever  * nlmsvc_release_host - release server nlm_host
40967216b94SChuck Lever  * @host: nlm_host to release
41067216b94SChuck Lever  *
41167216b94SChuck Lever  * Host is destroyed later in nlm_gc_host().
41267216b94SChuck Lever  */
nlmsvc_release_host(struct nlm_host * host)41367216b94SChuck Lever void nlmsvc_release_host(struct nlm_host *host)
41467216b94SChuck Lever {
41567216b94SChuck Lever 	if (host == NULL)
41667216b94SChuck Lever 		return;
41767216b94SChuck Lever 
41867216b94SChuck Lever 	dprintk("lockd: release server host %s\n", host->h_name);
41967216b94SChuck Lever 
420a2d30a54STrond Myklebust 	WARN_ON_ONCE(!host->h_server);
421fee21fb5SElena Reshetova 	refcount_dec(&host->h_count);
422c585646dSAdrian Bunk }
423c585646dSAdrian Bunk 
424c585646dSAdrian Bunk /*
4251da177e4SLinus Torvalds  * Create the NLM RPC client for an NLM peer
4261da177e4SLinus Torvalds  */
4271da177e4SLinus Torvalds struct rpc_clnt *
nlm_bind_host(struct nlm_host * host)4281da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
4311da177e4SLinus Torvalds 
4321df40b60SChuck Lever 	dprintk("lockd: nlm_bind_host %s (%s)\n",
4331df40b60SChuck Lever 			host->h_name, host->h_addrbuf);
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	/* Lock host handle */
43650467914STrond Myklebust 	mutex_lock(&host->h_mutex);
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	/* If we've already created an RPC client, check whether
4391da177e4SLinus Torvalds 	 * RPC rebind is required
4401da177e4SLinus Torvalds 	 */
4411da177e4SLinus Torvalds 	if ((clnt = host->h_rpcclnt) != NULL) {
4429b82d88dSCalum Mackay 		nlm_rebind_host(host);
4431da177e4SLinus Torvalds 	} else {
44421051ba6STrond Myklebust 		unsigned long increment = nlmsvc_timeout;
445e1ec7892SChuck Lever 		struct rpc_timeout timeparms = {
446e1ec7892SChuck Lever 			.to_initval	= increment,
447e1ec7892SChuck Lever 			.to_increment	= increment,
448e1ec7892SChuck Lever 			.to_maxval	= increment * 6UL,
449e1ec7892SChuck Lever 			.to_retries	= 5U,
450e1ec7892SChuck Lever 		};
451e1ec7892SChuck Lever 		struct rpc_create_args args = {
45266697bfdSStanislav Kinsbursky 			.net		= host->net,
453e1ec7892SChuck Lever 			.protocol	= host->h_proto,
454b4ed58fdSChuck Lever 			.address	= nlm_addr(host),
455b4ed58fdSChuck Lever 			.addrsize	= host->h_addrlen,
456e1ec7892SChuck Lever 			.timeout	= &timeparms,
457e1ec7892SChuck Lever 			.servername	= host->h_name,
458e1ec7892SChuck Lever 			.program	= &nlm_program,
459e1ec7892SChuck Lever 			.version	= host->h_version,
460e1ec7892SChuck Lever 			.authflavor	= RPC_AUTH_UNIX,
46190bd17c8SJeff Layton 			.flags		= (RPC_CLNT_CREATE_NOPING |
462e6237b6fSTrond Myklebust 					   RPC_CLNT_CREATE_AUTOBIND |
463e6237b6fSTrond Myklebust 					   RPC_CLNT_CREATE_REUSEPORT),
464b422df91STrond Myklebust 			.cred		= host->h_cred,
465e1ec7892SChuck Lever 		};
4661da177e4SLinus Torvalds 
46790bd17c8SJeff Layton 		/*
46890bd17c8SJeff Layton 		 * lockd retries server side blocks automatically so we want
46990bd17c8SJeff Layton 		 * those to be soft RPC calls. Client side calls need to be
47090bd17c8SJeff Layton 		 * hard RPC tasks.
47190bd17c8SJeff Layton 		 */
47290bd17c8SJeff Layton 		if (!host->h_server)
47390bd17c8SJeff Layton 			args.flags |= RPC_CLNT_CREATE_HARDRTRY;
4740cb2659bSChuck Lever 		if (host->h_noresvport)
4750cb2659bSChuck Lever 			args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
4768e35f8e7STrond Myklebust 		if (host->h_srcaddrlen)
4778e35f8e7STrond Myklebust 			args.saddress = nlm_srcaddr(host);
47890bd17c8SJeff Layton 
479e1ec7892SChuck Lever 		clnt = rpc_create(&args);
480e1ec7892SChuck Lever 		if (!IS_ERR(clnt))
4811da177e4SLinus Torvalds 			host->h_rpcclnt = clnt;
482e1ec7892SChuck Lever 		else {
483e1ec7892SChuck Lever 			printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
484e1ec7892SChuck Lever 			clnt = NULL;
485e1ec7892SChuck Lever 		}
4861da177e4SLinus Torvalds 	}
4871da177e4SLinus Torvalds 
48850467914STrond Myklebust 	mutex_unlock(&host->h_mutex);
4891da177e4SLinus Torvalds 	return clnt;
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds 
4929b82d88dSCalum Mackay /**
4939b82d88dSCalum Mackay  * nlm_rebind_host - If needed, force a portmap lookup of the peer's lockd port
4949b82d88dSCalum Mackay  * @host: NLM host handle for peer
4959b82d88dSCalum Mackay  *
4969b82d88dSCalum Mackay  * This is not needed when using a connection-oriented protocol, such as TCP.
4979b82d88dSCalum Mackay  * The existing autobind mechanism is sufficient to force a rebind when
4989b82d88dSCalum Mackay  * required, e.g. on connection state transitions.
4991da177e4SLinus Torvalds  */
5001da177e4SLinus Torvalds void
nlm_rebind_host(struct nlm_host * host)5011da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host)
5021da177e4SLinus Torvalds {
5039b82d88dSCalum Mackay 	if (host->h_proto != IPPROTO_UDP)
5049b82d88dSCalum Mackay 		return;
5059b82d88dSCalum Mackay 
5061da177e4SLinus Torvalds 	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
50735f5a422SChuck Lever 		rpc_force_rebind(host->h_rpcclnt);
5081da177e4SLinus Torvalds 		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
5091da177e4SLinus Torvalds 	}
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds /*
5131da177e4SLinus Torvalds  * Increment NLM host count
5141da177e4SLinus Torvalds  */
nlm_get_host(struct nlm_host * host)5151da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host)
5161da177e4SLinus Torvalds {
5171da177e4SLinus Torvalds 	if (host) {
5181da177e4SLinus Torvalds 		dprintk("lockd: get host %s\n", host->h_name);
519fee21fb5SElena Reshetova 		refcount_inc(&host->h_count);
5201da177e4SLinus Torvalds 		host->h_expires = jiffies + NLM_HOST_EXPIRE;
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 	return host;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
next_host_state(struct hlist_head * cache,struct nsm_handle * nsm,const struct nlm_reboot * info)525b10e30f6SJ. Bruce Fields static struct nlm_host *next_host_state(struct hlist_head *cache,
526b10e30f6SJ. Bruce Fields 					struct nsm_handle *nsm,
527b10e30f6SJ. Bruce Fields 					const struct nlm_reboot *info)
528b10e30f6SJ. Bruce Fields {
52980c30e8dSChuck Lever 	struct nlm_host *host;
530b10e30f6SJ. Bruce Fields 	struct hlist_head *chain;
531b10e30f6SJ. Bruce Fields 
532b10e30f6SJ. Bruce Fields 	mutex_lock(&nlm_host_mutex);
533b67bfe0dSSasha Levin 	for_each_host(host, chain, cache) {
534b10e30f6SJ. Bruce Fields 		if (host->h_nsmhandle == nsm
535b10e30f6SJ. Bruce Fields 		    && host->h_nsmstate != info->state) {
536b10e30f6SJ. Bruce Fields 			host->h_nsmstate = info->state;
537b10e30f6SJ. Bruce Fields 			host->h_state++;
538b10e30f6SJ. Bruce Fields 
539b10e30f6SJ. Bruce Fields 			nlm_get_host(host);
540b10e30f6SJ. Bruce Fields 			mutex_unlock(&nlm_host_mutex);
541b10e30f6SJ. Bruce Fields 			return host;
542b10e30f6SJ. Bruce Fields 		}
54380c30e8dSChuck Lever 	}
54480c30e8dSChuck Lever 
54580c30e8dSChuck Lever 	mutex_unlock(&nlm_host_mutex);
54680c30e8dSChuck Lever 	return NULL;
54780c30e8dSChuck Lever }
548b10e30f6SJ. Bruce Fields 
5497fefc9cbSChuck Lever /**
5507fefc9cbSChuck Lever  * nlm_host_rebooted - Release all resources held by rebooted host
5510ad95472SAndrey Ryabinin  * @net:  network namespace
5527fefc9cbSChuck Lever  * @info: pointer to decoded results of NLM_SM_NOTIFY call
5537fefc9cbSChuck Lever  *
5547fefc9cbSChuck Lever  * We were notified that the specified host has rebooted.  Release
5557fefc9cbSChuck Lever  * all resources held by that peer.
556cf712c24SOlaf Kirch  */
nlm_host_rebooted(const struct net * net,const struct nlm_reboot * info)5570ad95472SAndrey Ryabinin void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
558cf712c24SOlaf Kirch {
5595c8dd29cSOlaf Kirch 	struct nsm_handle *nsm;
5600cea3276SOlaf Kirch 	struct nlm_host	*host;
561cf712c24SOlaf Kirch 
5620ad95472SAndrey Ryabinin 	nsm = nsm_reboot_lookup(net, info);
5638c7378fdSChuck Lever 	if (unlikely(nsm == NULL))
564cf712c24SOlaf Kirch 		return;
5655c8dd29cSOlaf Kirch 
5665c8dd29cSOlaf Kirch 	/* Mark all hosts tied to this NSM state as having rebooted.
5675c8dd29cSOlaf Kirch 	 * We run the loop repeatedly, because we drop the host table
5685c8dd29cSOlaf Kirch 	 * lock for this.
5695c8dd29cSOlaf Kirch 	 * To avoid processing a host several times, we match the nsmstate.
5705c8dd29cSOlaf Kirch 	 */
571d2df0484SChuck Lever 	while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
572cf712c24SOlaf Kirch 		nlmsvc_free_host_resources(host);
57367216b94SChuck Lever 		nlmsvc_release_host(host);
5745c8dd29cSOlaf Kirch 	}
5758ea6ecc8SChuck Lever 	while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
5768ea6ecc8SChuck Lever 		nlmclnt_recovery(host);
5778ea6ecc8SChuck Lever 		nlmclnt_release_host(host);
5788ea6ecc8SChuck Lever 	}
5798ea6ecc8SChuck Lever 
580cdd30fa1SJeff Layton 	nsm_release(nsm);
581cf712c24SOlaf Kirch }
582cf712c24SOlaf Kirch 
nlm_complain_hosts(struct net * net)583d5850ff9SStanislav Kinsbursky static void nlm_complain_hosts(struct net *net)
584d5850ff9SStanislav Kinsbursky {
585d5850ff9SStanislav Kinsbursky 	struct hlist_head *chain;
586d5850ff9SStanislav Kinsbursky 	struct nlm_host	*host;
587d5850ff9SStanislav Kinsbursky 
588d5850ff9SStanislav Kinsbursky 	if (net) {
589d5850ff9SStanislav Kinsbursky 		struct lockd_net *ln = net_generic(net, lockd_net_id);
590d5850ff9SStanislav Kinsbursky 
591d5850ff9SStanislav Kinsbursky 		if (ln->nrhosts == 0)
592d5850ff9SStanislav Kinsbursky 			return;
593e919b076SVasily Averin 		pr_warn("lockd: couldn't shutdown host module for net %x!\n",
594e919b076SVasily Averin 			net->ns.inum);
595e919b076SVasily Averin 		dprintk("lockd: %lu hosts left in net %x:\n", ln->nrhosts,
596e919b076SVasily Averin 			net->ns.inum);
597d5850ff9SStanislav Kinsbursky 	} else {
598d5850ff9SStanislav Kinsbursky 		if (nrhosts == 0)
599d5850ff9SStanislav Kinsbursky 			return;
600d5850ff9SStanislav Kinsbursky 		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
601d5850ff9SStanislav Kinsbursky 		dprintk("lockd: %lu hosts left:\n", nrhosts);
602d5850ff9SStanislav Kinsbursky 	}
603d5850ff9SStanislav Kinsbursky 
604b67bfe0dSSasha Levin 	for_each_host(host, chain, nlm_server_hosts) {
605d5850ff9SStanislav Kinsbursky 		if (net && host->net != net)
606d5850ff9SStanislav Kinsbursky 			continue;
607e919b076SVasily Averin 		dprintk("       %s (cnt %d use %d exp %ld net %x)\n",
608fee21fb5SElena Reshetova 			host->h_name, refcount_read(&host->h_count),
609e919b076SVasily Averin 			host->h_inuse, host->h_expires, host->net->ns.inum);
610d5850ff9SStanislav Kinsbursky 	}
611d5850ff9SStanislav Kinsbursky }
612d5850ff9SStanislav Kinsbursky 
6131da177e4SLinus Torvalds void
nlm_shutdown_hosts_net(struct net * net)6143b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(struct net *net)
6151da177e4SLinus Torvalds {
6160cea3276SOlaf Kirch 	struct hlist_head *chain;
6171da177e4SLinus Torvalds 	struct nlm_host	*host;
6181da177e4SLinus Torvalds 
619353ab6e9SIngo Molnar 	mutex_lock(&nlm_host_mutex);
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	/* First, make all hosts eligible for gc */
622e919b076SVasily Averin 	dprintk("lockd: nuking all hosts in net %x...\n",
623e919b076SVasily Averin 		net ? net->ns.inum : 0);
624b67bfe0dSSasha Levin 	for_each_host(host, chain, nlm_server_hosts) {
6253b64739fSStanislav Kinsbursky 		if (net && host->net != net)
6263b64739fSStanislav Kinsbursky 			continue;
6271da177e4SLinus Torvalds 		host->h_expires = jiffies - 1;
628d801b861SJeff Layton 		if (host->h_rpcclnt) {
629d801b861SJeff Layton 			rpc_shutdown_client(host->h_rpcclnt);
630d801b861SJeff Layton 			host->h_rpcclnt = NULL;
631d801b861SJeff Layton 		}
632*bfca7a6fSJeff Layton 		nlmsvc_free_host_resources(host);
633d801b861SJeff Layton 	}
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 	/* Then, perform a garbage collection pass */
63627adaddcSStanislav Kinsbursky 	nlm_gc_hosts(net);
637d5850ff9SStanislav Kinsbursky 	nlm_complain_hosts(net);
6389e137ed5SVasily Averin 	mutex_unlock(&nlm_host_mutex);
6393b64739fSStanislav Kinsbursky }
6403b64739fSStanislav Kinsbursky 
6413b64739fSStanislav Kinsbursky /*
6423b64739fSStanislav Kinsbursky  * Shut down the hosts module.
6433b64739fSStanislav Kinsbursky  * Note that this routine is called only at server shutdown time.
6443b64739fSStanislav Kinsbursky  */
6453b64739fSStanislav Kinsbursky void
nlm_shutdown_hosts(void)6463b64739fSStanislav Kinsbursky nlm_shutdown_hosts(void)
6473b64739fSStanislav Kinsbursky {
648e2edaa98SStanislav Kinsbursky 	dprintk("lockd: shutting down host module\n");
6493b64739fSStanislav Kinsbursky 	nlm_shutdown_hosts_net(NULL);
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds /*
6531da177e4SLinus Torvalds  * Garbage collect any unused NLM hosts.
6541da177e4SLinus Torvalds  * This GC combines reference counting for async operations with
6551da177e4SLinus Torvalds  * mark & sweep for resources held by remote clients.
6561da177e4SLinus Torvalds  */
6571da177e4SLinus Torvalds static void
nlm_gc_hosts(struct net * net)65827adaddcSStanislav Kinsbursky nlm_gc_hosts(struct net *net)
6591da177e4SLinus Torvalds {
6600cea3276SOlaf Kirch 	struct hlist_head *chain;
661b67bfe0dSSasha Levin 	struct hlist_node *next;
6620cea3276SOlaf Kirch 	struct nlm_host	*host;
6631da177e4SLinus Torvalds 
664e919b076SVasily Averin 	dprintk("lockd: host garbage collection for net %x\n",
665e919b076SVasily Averin 		net ? net->ns.inum : 0);
666b67bfe0dSSasha Levin 	for_each_host(host, chain, nlm_server_hosts) {
66727adaddcSStanislav Kinsbursky 		if (net && host->net != net)
66827adaddcSStanislav Kinsbursky 			continue;
6691da177e4SLinus Torvalds 		host->h_inuse = 0;
67027adaddcSStanislav Kinsbursky 	}
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	/* Mark all hosts that hold locks, blocks or shares */
673b26411f8SStanislav Kinsbursky 	nlmsvc_mark_resources(net);
6741da177e4SLinus Torvalds 
675b67bfe0dSSasha Levin 	for_each_host_safe(host, next, chain, nlm_server_hosts) {
67627adaddcSStanislav Kinsbursky 		if (net && host->net != net)
67727adaddcSStanislav Kinsbursky 			continue;
678535cb8f3STrond Myklebust 		if (host->h_inuse || time_before(jiffies, host->h_expires)) {
679b1137468SJ. Bruce Fields 			dprintk("nlm_gc_hosts skipping %s "
680e919b076SVasily Averin 				"(cnt %d use %d exp %ld net %x)\n",
681fee21fb5SElena Reshetova 				host->h_name, refcount_read(&host->h_count),
682e919b076SVasily Averin 				host->h_inuse, host->h_expires,
683e919b076SVasily Averin 				host->net->ns.inum);
6841da177e4SLinus Torvalds 			continue;
6851da177e4SLinus Torvalds 		}
686535cb8f3STrond Myklebust 		if (refcount_dec_if_one(&host->h_count))
687723bb5b5SChuck Lever 			nlm_destroy_host_locked(host);
6881da177e4SLinus Torvalds 	}
6891da177e4SLinus Torvalds 
6903cf7fb07SStanislav Kinsbursky 	if (net) {
6913cf7fb07SStanislav Kinsbursky 		struct lockd_net *ln = net_generic(net, lockd_net_id);
6923cf7fb07SStanislav Kinsbursky 
6933cf7fb07SStanislav Kinsbursky 		ln->next_gc = jiffies + NLM_HOST_COLLECT;
6943cf7fb07SStanislav Kinsbursky 	}
6951da177e4SLinus Torvalds }
696