clntlock.c 8.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * linux/fs/lockd/clntlock.c
 *
 * Lock handling for the client side NLM implementation
 *
 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/time.h>
#include <linux/nfs_fs.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
#include <linux/smp_lock.h>
17
#include <linux/kthread.h>
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#define NLMDBG_FACILITY		NLMDBG_CLIENT

/*
 * Local function prototypes
 */
static int			reclaimer(void *ptr);

/*
 * The following functions handle blocking and granting from the
 * client perspective.
 */

/*
 * This is the representation of a blocked client lock.
 */
struct nlm_wait {
35
	struct list_head	b_list;		/* linked list */
L
Linus Torvalds 已提交
36 37 38 39
	wait_queue_head_t	b_wait;		/* where to wait on */
	struct nlm_host *	b_host;
	struct file_lock *	b_lock;		/* local file lock */
	unsigned short		b_reclaim;	/* got to reclaim lock */
A
Al Viro 已提交
40
	__be32			b_status;	/* grant callback status */
L
Linus Torvalds 已提交
41 42
};

43
static LIST_HEAD(nlm_blocked);
L
Linus Torvalds 已提交
44

45 46
/**
 * nlmclnt_init - Set up per-NFS mount point lockd data structures
47
 * @nlm_init: pointer to arguments structure
48 49 50 51
 *
 * Returns pointer to an appropriate nlm_host struct,
 * or an ERR_PTR value.
 */
52
struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
53 54
{
	struct nlm_host *host;
55
	u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
56 57
	int status;

58
	status = lockd_up();
59 60 61
	if (status < 0)
		return ERR_PTR(status);

62
	host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
63
				   nlm_init->protocol, nlm_version,
64
				   nlm_init->hostname, nlm_init->noresvport);
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	if (host == NULL) {
		lockd_down();
		return ERR_PTR(-ENOLCK);
	}

	return host;
}
EXPORT_SYMBOL_GPL(nlmclnt_init);

/**
 * nlmclnt_done - Release resources allocated by nlmclnt_init()
 * @host: nlm_host structure reserved by nlmclnt_init()
 *
 */
void nlmclnt_done(struct nlm_host *host)
{
	nlm_release_host(host);
	lockd_down();
}
EXPORT_SYMBOL_GPL(nlmclnt_done);

L
Linus Torvalds 已提交
86
/*
87
 * Queue up a lock for blocking so that the GRANTED request can see it
L
Linus Torvalds 已提交
88
 */
T
Trond Myklebust 已提交
89
struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl)
90 91 92 93
{
	struct nlm_wait *block;

	block = kmalloc(sizeof(*block), GFP_KERNEL);
T
Trond Myklebust 已提交
94 95 96 97
	if (block != NULL) {
		block->b_host = host;
		block->b_lock = fl;
		init_waitqueue_head(&block->b_wait);
A
Al Viro 已提交
98
		block->b_status = nlm_lck_blocked;
T
Trond Myklebust 已提交
99 100 101
		list_add(&block->b_list, &nlm_blocked);
	}
	return block;
102 103
}

T
Trond Myklebust 已提交
104
void nlmclnt_finish_block(struct nlm_wait *block)
L
Linus Torvalds 已提交
105
{
106 107 108 109 110
	if (block == NULL)
		return;
	list_del(&block->b_list);
	kfree(block);
}
L
Linus Torvalds 已提交
111

112 113 114
/*
 * Block on a lock
 */
T
Trond Myklebust 已提交
115
int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
116 117
{
	long ret;
L
Linus Torvalds 已提交
118

119 120 121
	/* A borken server might ask us to block even if we didn't
	 * request it. Just say no!
	 */
T
Trond Myklebust 已提交
122
	if (block == NULL)
123
		return -EAGAIN;
L
Linus Torvalds 已提交
124 125 126 127 128 129 130 131 132

	/* Go to sleep waiting for GRANT callback. Some servers seem
	 * to lose callbacks, however, so we're going to poll from
	 * time to time just to make sure.
	 *
	 * For now, the retry frequency is pretty high; normally 
	 * a 1 minute timeout would do. See the comment before
	 * nlmclnt_lock for an explanation.
	 */
133
	ret = wait_event_interruptible_timeout(block->b_wait,
A
Al Viro 已提交
134
			block->b_status != nlm_lck_blocked,
135
			timeout);
T
Trond Myklebust 已提交
136 137 138 139
	if (ret < 0)
		return -ERESTARTSYS;
	req->a_res.status = block->b_status;
	return 0;
L
Linus Torvalds 已提交
140 141
}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static const struct in6_addr *nlmclnt_map_v4addr(const struct sockaddr *sap,
						 struct in6_addr *addr_mapped)
{
	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;

	switch (sap->sa_family) {
	case AF_INET6:
		return &((const struct sockaddr_in6 *)sap)->sin6_addr;
	case AF_INET:
		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, addr_mapped);
		return addr_mapped;
	}

	return NULL;
}

/*
 * If lockd is using a PF_INET6 listener, all incoming requests appear
 * to come from AF_INET6 remotes.  The address of AF_INET remotes are
 * mapped to AF_INET6 automatically by the network layer.  In case the
 * user passed an AF_INET server address at mount time, ensure both
 * addresses are AF_INET6 before comparing them.
 */
static int nlmclnt_cmp_addr(const struct nlm_host *host,
			    const struct sockaddr *sap)
{
	const struct in6_addr *addr1;
	const struct in6_addr *addr2;
	struct in6_addr addr1_mapped;
	struct in6_addr addr2_mapped;

	addr1 = nlmclnt_map_v4addr(nlm_addr(host), &addr1_mapped);
	if (likely(addr1 != NULL)) {
		addr2 = nlmclnt_map_v4addr(sap, &addr2_mapped);
		if (likely(addr2 != NULL))
			return ipv6_addr_equal(addr1, addr2);
	}

	return 0;
}
#else	/* !(CONFIG_IPV6 || CONFIG_IPV6_MODULE) */
static int nlmclnt_cmp_addr(const struct nlm_host *host,
			    const struct sockaddr *sap)
{
	return nlm_cmp_addr(nlm_addr(host), sap);
}
#endif	/* !(CONFIG_IPV6 || CONFIG_IPV6_MODULE) */

L
Linus Torvalds 已提交
191 192 193
/*
 * The server lockd has called us back to tell us the lock was granted
 */
194
__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
L
Linus Torvalds 已提交
195
{
196 197
	const struct file_lock *fl = &lock->fl;
	const struct nfs_fh *fh = &lock->fh;
L
Linus Torvalds 已提交
198
	struct nlm_wait	*block;
A
Al Viro 已提交
199
	__be32 res = nlm_lck_denied;
L
Linus Torvalds 已提交
200 201 202 203 204

	/*
	 * Look up blocked request based on arguments. 
	 * Warning: must not use cookie to match it!
	 */
205
	list_for_each_entry(block, &nlm_blocked, b_list) {
206 207
		struct file_lock *fl_blocked = block->b_lock;

208 209 210 211 212 213 214 215 216
		if (fl_blocked->fl_start != fl->fl_start)
			continue;
		if (fl_blocked->fl_end != fl->fl_end)
			continue;
		/*
		 * Careful! The NLM server will return the 32-bit "pid" that
		 * we put on the wire: in this case the lockowner "pid".
		 */
		if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
217
			continue;
218
		if (!nlmclnt_cmp_addr(block->b_host, addr))
219
			continue;
J
Josef Sipek 已提交
220
		if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0)
221 222 223 224
			continue;
		/* Alright, we found a lock. Set the return status
		 * and wake up the caller
		 */
A
Al Viro 已提交
225
		block->b_status = nlm_granted;
226 227
		wake_up(&block->b_wait);
		res = nlm_granted;
L
Linus Torvalds 已提交
228
	}
229
	return res;
L
Linus Torvalds 已提交
230 231 232 233 234 235 236 237 238 239 240 241
}

/*
 * The following procedures deal with the recovery of locks after a
 * server crash.
 */

/*
 * Reclaim all locks on server host. We do this by spawning a separate
 * reclaimer thread.
 */
void
242
nlmclnt_recovery(struct nlm_host *host)
L
Linus Torvalds 已提交
243
{
244 245
	struct task_struct *task;

T
Trond Myklebust 已提交
246
	if (!host->h_reclaiming++) {
L
Linus Torvalds 已提交
247
		nlm_get_host(host);
248 249 250 251 252
		task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
		if (IS_ERR(task))
			printk(KERN_ERR "lockd: unable to spawn reclaimer "
				"thread. Locks for %s won't be reclaimed! "
				"(%ld)\n", host->h_name, PTR_ERR(task));
L
Linus Torvalds 已提交
253 254 255 256 257 258 259 260
	}
}

static int
reclaimer(void *ptr)
{
	struct nlm_host	  *host = (struct nlm_host *) ptr;
	struct nlm_wait	  *block;
261
	struct file_lock *fl, *next;
T
Trond Myklebust 已提交
262
	u32 nsmstate;
L
Linus Torvalds 已提交
263 264 265

	allow_signal(SIGKILL);

266 267
	down_write(&host->h_rwsem);

L
Linus Torvalds 已提交
268 269 270
	/* This one ensures that our parent doesn't terminate while the
	 * reclaim is in progress */
	lock_kernel();
271
	lockd_up();	/* note: this cannot fail as lockd is already running */
L
Linus Torvalds 已提交
272

273
	dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
274

L
Linus Torvalds 已提交
275
restart:
T
Trond Myklebust 已提交
276
	nsmstate = host->h_nsmstate;
277 278 279 280

	/* Force a portmap getport - the peer's lockd will
	 * most likely end up on a different port.
	 */
281
	host->h_nextrebind = jiffies;
282 283 284 285
	nlm_rebind_host(host);

	/* First, reclaim all locks that have been granted. */
	list_splice_init(&host->h_granted, &host->h_reclaim);
286
	list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
287
		list_del_init(&fl->fl_u.nfs_fl.list);
L
Linus Torvalds 已提交
288

289 290 291 292 293 294
		/*
		 * sending this thread a SIGKILL will result in any unreclaimed
		 * locks being removed from the h_granted list. This means that
		 * the kernel will not attempt to reclaim them again if a new
		 * reclaimer thread is spawned for this host.
		 */
L
Linus Torvalds 已提交
295
		if (signalled())
296
			continue;
T
Trond Myklebust 已提交
297 298 299 300 301 302 303
		if (nlmclnt_reclaim(host, fl) != 0)
			continue;
		list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted);
		if (host->h_nsmstate != nsmstate) {
			/* Argh! The server rebooted again! */
			goto restart;
		}
L
Linus Torvalds 已提交
304
	}
305 306 307

	host->h_reclaiming = 0;
	up_write(&host->h_rwsem);
308
	dprintk("NLM: done reclaiming locks for host %s\n", host->h_name);
L
Linus Torvalds 已提交
309 310

	/* Now, wake up all processes that sleep on a blocked lock */
311
	list_for_each_entry(block, &nlm_blocked, b_list) {
L
Linus Torvalds 已提交
312
		if (block->b_host == host) {
A
Al Viro 已提交
313
			block->b_status = nlm_lck_denied_grace_period;
L
Linus Torvalds 已提交
314 315 316 317 318 319 320 321
			wake_up(&block->b_wait);
		}
	}

	/* Release host handle after use */
	nlm_release_host(host);
	lockd_down();
	unlock_kernel();
322
	return 0;
L
Linus Torvalds 已提交
323
}