clntlock.c 6.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * 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>

#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 {
34
	struct list_head	b_list;		/* linked list */
L
Linus Torvalds 已提交
35 36 37 38
	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 已提交
39
	__be32			b_status;	/* grant callback status */
L
Linus Torvalds 已提交
40 41
};

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

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/**
 * nlmclnt_init - Set up per-NFS mount point lockd data structures
 * @server_name: server's hostname
 * @server_address: server's network address
 * @server_addrlen: length of server's address
 * @protocol: transport protocol lockd should use
 * @nfs_version: NFS protocol version for this mount point
 *
 * Returns pointer to an appropriate nlm_host struct,
 * or an ERR_PTR value.
 */
struct nlm_host *nlmclnt_init(const char *server_name,
			      const struct sockaddr *server_address,
			      size_t server_addrlen,
			      unsigned short protocol, u32 nfs_version)
{
	struct nlm_host *host;
	u32 nlm_version = (nfs_version == 2) ? 1 : 4;
	int status;

	status = lockd_up(protocol);
	if (status < 0)
		return ERR_PTR(status);

	host = nlmclnt_lookup_host((struct sockaddr_in *)server_address,
				   protocol, nlm_version,
				   server_name, strlen(server_name));
	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 已提交
92
/*
93
 * Queue up a lock for blocking so that the GRANTED request can see it
L
Linus Torvalds 已提交
94
 */
T
Trond Myklebust 已提交
95
struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl)
96 97 98 99
{
	struct nlm_wait *block;

	block = kmalloc(sizeof(*block), GFP_KERNEL);
T
Trond Myklebust 已提交
100 101 102 103
	if (block != NULL) {
		block->b_host = host;
		block->b_lock = fl;
		init_waitqueue_head(&block->b_wait);
A
Al Viro 已提交
104
		block->b_status = nlm_lck_blocked;
T
Trond Myklebust 已提交
105 106 107
		list_add(&block->b_list, &nlm_blocked);
	}
	return block;
108 109
}

T
Trond Myklebust 已提交
110
void nlmclnt_finish_block(struct nlm_wait *block)
L
Linus Torvalds 已提交
111
{
112 113 114 115 116
	if (block == NULL)
		return;
	list_del(&block->b_list);
	kfree(block);
}
L
Linus Torvalds 已提交
117

118 119 120
/*
 * Block on a lock
 */
T
Trond Myklebust 已提交
121
int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
122 123
{
	long ret;
L
Linus Torvalds 已提交
124

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

	/* 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.
	 */
139
	ret = wait_event_interruptible_timeout(block->b_wait,
A
Al Viro 已提交
140
			block->b_status != nlm_lck_blocked,
141
			timeout);
T
Trond Myklebust 已提交
142 143 144 145
	if (ret < 0)
		return -ERESTARTSYS;
	req->a_res.status = block->b_status;
	return 0;
L
Linus Torvalds 已提交
146 147 148 149 150
}

/*
 * The server lockd has called us back to tell us the lock was granted
 */
A
Al Viro 已提交
151
__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock)
L
Linus Torvalds 已提交
152
{
153 154
	const struct file_lock *fl = &lock->fl;
	const struct nfs_fh *fh = &lock->fh;
L
Linus Torvalds 已提交
155
	struct nlm_wait	*block;
A
Al Viro 已提交
156
	__be32 res = nlm_lck_denied;
L
Linus Torvalds 已提交
157 158 159 160 161

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

165 166 167 168 169 170 171 172 173
		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)
174 175 176
			continue;
		if (!nlm_cmp_addr(&block->b_host->h_addr, addr))
			continue;
J
Josef Sipek 已提交
177
		if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0)
178 179 180 181
			continue;
		/* Alright, we found a lock. Set the return status
		 * and wake up the caller
		 */
A
Al Viro 已提交
182
		block->b_status = nlm_granted;
183 184
		wake_up(&block->b_wait);
		res = nlm_granted;
L
Linus Torvalds 已提交
185
	}
186
	return res;
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196 197 198
}

/*
 * 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
199
nlmclnt_recovery(struct nlm_host *host)
L
Linus Torvalds 已提交
200
{
T
Trond Myklebust 已提交
201
	if (!host->h_reclaiming++) {
L
Linus Torvalds 已提交
202 203
		nlm_get_host(host);
		__module_get(THIS_MODULE);
204
		if (kernel_thread(reclaimer, host, CLONE_FS | CLONE_FILES) < 0)
L
Linus Torvalds 已提交
205 206 207 208 209 210 211 212 213
			module_put(THIS_MODULE);
	}
}

static int
reclaimer(void *ptr)
{
	struct nlm_host	  *host = (struct nlm_host *) ptr;
	struct nlm_wait	  *block;
214
	struct file_lock *fl, *next;
T
Trond Myklebust 已提交
215
	u32 nsmstate;
L
Linus Torvalds 已提交
216 217 218 219

	daemonize("%s-reclaim", host->h_name);
	allow_signal(SIGKILL);

220 221
	down_write(&host->h_rwsem);

L
Linus Torvalds 已提交
222 223 224
	/* This one ensures that our parent doesn't terminate while the
	 * reclaim is in progress */
	lock_kernel();
225
	lockd_up(0); /* note: this cannot fail as lockd is already running */
L
Linus Torvalds 已提交
226

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

L
Linus Torvalds 已提交
229
restart:
T
Trond Myklebust 已提交
230
	nsmstate = host->h_nsmstate;
231 232 233 234

	/* Force a portmap getport - the peer's lockd will
	 * most likely end up on a different port.
	 */
235
	host->h_nextrebind = jiffies;
236 237 238 239
	nlm_rebind_host(host);

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

243
		/* Why are we leaking memory here? --okir */
L
Linus Torvalds 已提交
244
		if (signalled())
245
			continue;
T
Trond Myklebust 已提交
246 247 248 249 250 251 252
		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 已提交
253
	}
254 255 256

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

	/* Now, wake up all processes that sleep on a blocked lock */
260
	list_for_each_entry(block, &nlm_blocked, b_list) {
L
Linus Torvalds 已提交
261
		if (block->b_host == host) {
A
Al Viro 已提交
262
			block->b_status = nlm_lck_denied_grace_period;
L
Linus Torvalds 已提交
263 264 265 266 267 268 269 270 271 272
			wake_up(&block->b_wait);
		}
	}

	/* Release host handle after use */
	nlm_release_host(host);
	lockd_down();
	unlock_kernel();
	module_put_and_exit(0);
}