File: /usr/src/linux/fs/lockd/clntlock.c

1     /*
2      * linux/fs/lockd/clntlock.c
3      *
4      * Lock handling for the client side NLM implementation
5      *
6      * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7      */
8     
9     #define __KERNEL_SYSCALLS__
10     
11     #include <linux/types.h>
12     #include <linux/sched.h>
13     #include <linux/nfs_fs.h>
14     #include <linux/unistd.h>
15     #include <linux/sunrpc/clnt.h>
16     #include <linux/sunrpc/svc.h>
17     #include <linux/lockd/lockd.h>
18     #include <linux/smp_lock.h>
19     
20     #define NLMDBG_FACILITY		NLMDBG_CIENT
21     
22     /*
23      * Local function prototypes
24      */
25     static int			reclaimer(void *ptr);
26     
27     /*
28      * The following functions handle blocking and granting from the
29      * client perspective.
30      */
31     
32     /*
33      * This is the representation of a blocked client lock.
34      */
35     struct nlm_wait {
36     	struct nlm_wait *	b_next;		/* linked list */
37     	wait_queue_head_t	b_wait;		/* where to wait on */
38     	struct nlm_host *	b_host;
39     	struct file_lock *	b_lock;		/* local file lock */
40     	unsigned short		b_reclaim;	/* got to reclaim lock */
41     	u32			b_status;	/* grant callback status */
42     };
43     
44     static struct nlm_wait *	nlm_blocked;
45     
46     /*
47      * Block on a lock
48      */
49     int
50     nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
51     {
52     	struct nlm_wait	block, **head;
53     	int		err;
54     	u32		pstate;
55     
56     	block.b_host   = host;
57     	block.b_lock   = fl;
58     	init_waitqueue_head(&block.b_wait);
59     	block.b_status = NLM_LCK_BLOCKED;
60     	block.b_next   = nlm_blocked;
61     	nlm_blocked    = &block;
62     
63     	/* Remember pseudo nsm state */
64     	pstate = host->h_state;
65     
66     	/* Go to sleep waiting for GRANT callback. Some servers seem
67     	 * to lose callbacks, however, so we're going to poll from
68     	 * time to time just to make sure.
69     	 *
70     	 * For now, the retry frequency is pretty high; normally 
71     	 * a 1 minute timeout would do. See the comment before
72     	 * nlmclnt_lock for an explanation.
73     	 */
74     	sleep_on_timeout(&block.b_wait, 30*HZ);
75     
76     	for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
77     		if (*head == &block) {
78     			*head = block.b_next;
79     			break;
80     		}
81     	}
82     
83     	if (!signalled()) {
84     		*statp = block.b_status;
85     		return 0;
86     	}
87     
88     	/* Okay, we were interrupted. Cancel the pending request
89     	 * unless the server has rebooted.
90     	 */
91     	if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
92     		printk(KERN_NOTICE
93     			"lockd: CANCEL call failed (errno %d)\n", -err);
94     
95     	return -ERESTARTSYS;
96     }
97     
98     /*
99      * The server lockd has called us back to tell us the lock was granted
100      */
101     u32
102     nlmclnt_grant(struct nlm_lock *lock)
103     {
104     	struct nlm_wait	*block;
105     
106     	/*
107     	 * Look up blocked request based on arguments. 
108     	 * Warning: must not use cookie to match it!
109     	 */
110     	for (block = nlm_blocked; block; block = block->b_next) {
111     		if (nlm_compare_locks(block->b_lock, &lock->fl))
112     			break;
113     	}
114     
115     	/* Ooops, no blocked request found. */
116     	if (block == NULL)
117     		return nlm_lck_denied;
118     
119     	/* Alright, we found the lock. Set the return status and
120     	 * wake up the caller.
121     	 */
122     	block->b_status = NLM_LCK_GRANTED;
123     	wake_up(&block->b_wait);
124     
125     	return nlm_granted;
126     }
127     
128     /*
129      * The following procedures deal with the recovery of locks after a
130      * server crash.
131      */
132     
133     /*
134      * Reclaim all locks on server host. We do this by spawning a separate
135      * reclaimer thread.
136      * FIXME: should bump MOD_USE_COUNT while reclaiming
137      */
138     void
139     nlmclnt_recovery(struct nlm_host *host, u32 newstate)
140     {
141     	if (!host->h_reclaiming++) {
142     		if (host->h_nsmstate == newstate)
143     			return;
144     		printk(KERN_WARNING
145     			"lockd: Uh-oh! Interfering reclaims for host %s",
146     			host->h_name);
147     		host->h_monitored = 0;
148     		host->h_nsmstate = newstate;
149     		host->h_state++;
150     		nlm_release_host(host);
151     	} else {
152     		host->h_monitored = 0;
153     		host->h_nsmstate = newstate;
154     		host->h_state++;
155     		nlm_get_host(host);
156     		kernel_thread(reclaimer, host, 0);
157     	}
158     }
159     
160     static int
161     reclaimer(void *ptr)
162     {
163     	struct nlm_host	  *host = (struct nlm_host *) ptr;
164     	struct nlm_wait	  *block;
165     	struct list_head *tmp;
166     
167     	reparent_to_init();
168     	snprintf(current->comm, sizeof(current->comm),
169     		 "%s-reclaim",
170     		 host->h_name);
171     	
172     	/* This one ensures that our parent doesn't terminate while the
173     	 * reclaim is in progress */
174     	lock_kernel();
175     	lockd_up();
176     
177     	/* First, reclaim all locks that have been granted previously. */
178     restart:
179     	tmp = file_lock_list.next;
180     	while (tmp != &file_lock_list) {
181     		struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
182     		struct inode *inode = fl->fl_file->f_dentry->d_inode;
183     		if (inode->i_sb->s_magic == NFS_SUPER_MAGIC &&
184     				nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) &&
185     				fl->fl_u.nfs_fl.state != host->h_state &&
186     				(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) {
187     			fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED;
188     			nlmclnt_reclaim(host, fl);	/* This sleeps */
189     			goto restart;
190     		}
191     		tmp = tmp->next;
192     	}
193     
194     	host->h_reclaiming = 0;
195     	wake_up(&host->h_gracewait);
196     
197     	/* Now, wake up all processes that sleep on a blocked lock */
198     	for (block = nlm_blocked; block; block = block->b_next) {
199     		if (block->b_host == host) {
200     			block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
201     			wake_up(&block->b_wait);
202     		}
203     	}
204     
205     	/* Release host handle after use */
206     	nlm_release_host(host);
207     	lockd_down();
208     	unlock_kernel();
209     
210     	return 0;
211     }
212