]>
Commit | Line | Data |
---|---|---|
869d81df DT |
1 | /* |
2 | * Copyright (C) 2005 Red Hat, Inc. All rights reserved. | |
3 | * | |
4 | * This copyrighted material is made available to anyone wishing to use, | |
5 | * modify, copy, or redistribute it subject to the terms and conditions | |
e9fc2aa0 | 6 | * of the GNU General Public License version 2. |
869d81df DT |
7 | */ |
8 | ||
9 | #include <linux/miscdevice.h> | |
10 | #include <linux/lock_dlm_plock.h> | |
bd01f843 | 11 | #include <linux/poll.h> |
869d81df DT |
12 | |
13 | #include "lock_dlm.h" | |
14 | ||
15 | ||
16 | static spinlock_t ops_lock; | |
17 | static struct list_head send_list; | |
18 | static struct list_head recv_list; | |
19 | static wait_queue_head_t send_wq; | |
20 | static wait_queue_head_t recv_wq; | |
21 | ||
22 | struct plock_op { | |
23 | struct list_head list; | |
24 | int done; | |
25 | struct gdlm_plock_info info; | |
26 | }; | |
27 | ||
586759f0 ME |
28 | struct plock_xop { |
29 | struct plock_op xop; | |
30 | void *callback; | |
31 | void *fl; | |
32 | void *file; | |
33 | struct file_lock flc; | |
34 | }; | |
35 | ||
36 | ||
869d81df DT |
37 | static inline void set_version(struct gdlm_plock_info *info) |
38 | { | |
39 | info->version[0] = GDLM_PLOCK_VERSION_MAJOR; | |
40 | info->version[1] = GDLM_PLOCK_VERSION_MINOR; | |
41 | info->version[2] = GDLM_PLOCK_VERSION_PATCH; | |
42 | } | |
43 | ||
44 | static int check_version(struct gdlm_plock_info *info) | |
45 | { | |
46 | if ((GDLM_PLOCK_VERSION_MAJOR != info->version[0]) || | |
47 | (GDLM_PLOCK_VERSION_MINOR < info->version[1])) { | |
48 | log_error("plock device version mismatch: " | |
49 | "kernel (%u.%u.%u), user (%u.%u.%u)", | |
50 | GDLM_PLOCK_VERSION_MAJOR, | |
51 | GDLM_PLOCK_VERSION_MINOR, | |
52 | GDLM_PLOCK_VERSION_PATCH, | |
53 | info->version[0], | |
54 | info->version[1], | |
55 | info->version[2]); | |
56 | return -EINVAL; | |
57 | } | |
58 | return 0; | |
59 | } | |
60 | ||
61 | static void send_op(struct plock_op *op) | |
62 | { | |
63 | set_version(&op->info); | |
64 | INIT_LIST_HEAD(&op->list); | |
65 | spin_lock(&ops_lock); | |
66 | list_add_tail(&op->list, &send_list); | |
67 | spin_unlock(&ops_lock); | |
68 | wake_up(&send_wq); | |
69 | } | |
70 | ||
9b47c11d | 71 | int gdlm_plock(void *lockspace, struct lm_lockname *name, |
869d81df DT |
72 | struct file *file, int cmd, struct file_lock *fl) |
73 | { | |
9b47c11d | 74 | struct gdlm_ls *ls = lockspace; |
869d81df | 75 | struct plock_op *op; |
586759f0 | 76 | struct plock_xop *xop; |
869d81df DT |
77 | int rv; |
78 | ||
586759f0 ME |
79 | xop = kzalloc(sizeof(*xop), GFP_KERNEL); |
80 | if (!xop) | |
869d81df DT |
81 | return -ENOMEM; |
82 | ||
586759f0 | 83 | op = &xop->xop; |
869d81df | 84 | op->info.optype = GDLM_PLOCK_OP_LOCK; |
3a2a9c96 | 85 | op->info.pid = fl->fl_pid; |
869d81df DT |
86 | op->info.ex = (fl->fl_type == F_WRLCK); |
87 | op->info.wait = IS_SETLKW(cmd); | |
88 | op->info.fsid = ls->id; | |
89 | op->info.number = name->ln_number; | |
90 | op->info.start = fl->fl_start; | |
91 | op->info.end = fl->fl_end; | |
de9b75d3 | 92 | op->info.owner = (__u64)(long) fl->fl_owner; |
586759f0 ME |
93 | if (fl->fl_lmops && fl->fl_lmops->fl_grant) { |
94 | xop->callback = fl->fl_lmops->fl_grant; | |
95 | locks_init_lock(&xop->flc); | |
96 | locks_copy_lock(&xop->flc, fl); | |
97 | xop->fl = fl; | |
98 | xop->file = file; | |
99 | } else | |
100 | xop->callback = NULL; | |
869d81df DT |
101 | |
102 | send_op(op); | |
586759f0 ME |
103 | |
104 | if (xop->callback == NULL) | |
105 | wait_event(recv_wq, (op->done != 0)); | |
106 | else | |
107 | return -EINPROGRESS; | |
869d81df DT |
108 | |
109 | spin_lock(&ops_lock); | |
110 | if (!list_empty(&op->list)) { | |
d92a8d48 | 111 | printk(KERN_INFO "plock op on list\n"); |
869d81df DT |
112 | list_del(&op->list); |
113 | } | |
114 | spin_unlock(&ops_lock); | |
115 | ||
116 | rv = op->info.rv; | |
117 | ||
118 | if (!rv) { | |
119 | if (posix_lock_file_wait(file, fl) < 0) | |
120 | log_error("gdlm_plock: vfs lock error %x,%llx", | |
9229f013 DT |
121 | name->ln_type, |
122 | (unsigned long long)name->ln_number); | |
869d81df DT |
123 | } |
124 | ||
586759f0 ME |
125 | kfree(xop); |
126 | return rv; | |
127 | } | |
128 | ||
129 | /* Returns failure iff a succesful lock operation should be canceled */ | |
130 | static int gdlm_plock_callback(struct plock_op *op) | |
131 | { | |
132 | struct file *file; | |
133 | struct file_lock *fl; | |
134 | struct file_lock *flc; | |
135 | int (*notify)(void *, void *, int) = NULL; | |
136 | struct plock_xop *xop = (struct plock_xop *)op; | |
137 | int rv = 0; | |
138 | ||
139 | spin_lock(&ops_lock); | |
140 | if (!list_empty(&op->list)) { | |
141 | printk(KERN_INFO "plock op on list\n"); | |
142 | list_del(&op->list); | |
143 | } | |
144 | spin_unlock(&ops_lock); | |
145 | ||
146 | /* check if the following 2 are still valid or make a copy */ | |
147 | file = xop->file; | |
148 | flc = &xop->flc; | |
149 | fl = xop->fl; | |
150 | notify = xop->callback; | |
151 | ||
152 | if (op->info.rv) { | |
153 | notify(flc, NULL, op->info.rv); | |
154 | goto out; | |
155 | } | |
156 | ||
157 | /* got fs lock; bookkeep locally as well: */ | |
158 | flc->fl_flags &= ~FL_SLEEP; | |
159 | if (posix_lock_file(file, flc, NULL)) { | |
160 | /* | |
161 | * This can only happen in the case of kmalloc() failure. | |
162 | * The filesystem's own lock is the authoritative lock, | |
163 | * so a failure to get the lock locally is not a disaster. | |
164 | * As long as GFS cannot reliably cancel locks (especially | |
165 | * in a low-memory situation), we're better off ignoring | |
166 | * this failure than trying to recover. | |
167 | */ | |
168 | log_error("gdlm_plock: vfs lock error file %p fl %p", | |
169 | file, fl); | |
170 | } | |
171 | ||
172 | rv = notify(flc, NULL, 0); | |
173 | if (rv) { | |
174 | /* XXX: We need to cancel the fs lock here: */ | |
175 | printk("gfs2 lock granted after lock request failed;" | |
176 | " dangling lock!\n"); | |
177 | goto out; | |
178 | } | |
179 | ||
180 | out: | |
181 | kfree(xop); | |
869d81df DT |
182 | return rv; |
183 | } | |
184 | ||
9b47c11d | 185 | int gdlm_punlock(void *lockspace, struct lm_lockname *name, |
869d81df DT |
186 | struct file *file, struct file_lock *fl) |
187 | { | |
9b47c11d | 188 | struct gdlm_ls *ls = lockspace; |
869d81df DT |
189 | struct plock_op *op; |
190 | int rv; | |
191 | ||
192 | op = kzalloc(sizeof(*op), GFP_KERNEL); | |
193 | if (!op) | |
194 | return -ENOMEM; | |
195 | ||
196 | if (posix_lock_file_wait(file, fl) < 0) | |
197 | log_error("gdlm_punlock: vfs unlock error %x,%llx", | |
9229f013 | 198 | name->ln_type, (unsigned long long)name->ln_number); |
869d81df DT |
199 | |
200 | op->info.optype = GDLM_PLOCK_OP_UNLOCK; | |
3a2a9c96 | 201 | op->info.pid = fl->fl_pid; |
869d81df DT |
202 | op->info.fsid = ls->id; |
203 | op->info.number = name->ln_number; | |
204 | op->info.start = fl->fl_start; | |
205 | op->info.end = fl->fl_end; | |
de9b75d3 | 206 | op->info.owner = (__u64)(long) fl->fl_owner; |
869d81df DT |
207 | |
208 | send_op(op); | |
209 | wait_event(recv_wq, (op->done != 0)); | |
210 | ||
211 | spin_lock(&ops_lock); | |
212 | if (!list_empty(&op->list)) { | |
d92a8d48 | 213 | printk(KERN_INFO "punlock op on list\n"); |
869d81df DT |
214 | list_del(&op->list); |
215 | } | |
216 | spin_unlock(&ops_lock); | |
217 | ||
218 | rv = op->info.rv; | |
219 | ||
586759f0 ME |
220 | if (rv == -ENOENT) |
221 | rv = 0; | |
222 | ||
869d81df DT |
223 | kfree(op); |
224 | return rv; | |
225 | } | |
226 | ||
9b47c11d | 227 | int gdlm_plock_get(void *lockspace, struct lm_lockname *name, |
869d81df DT |
228 | struct file *file, struct file_lock *fl) |
229 | { | |
9b47c11d | 230 | struct gdlm_ls *ls = lockspace; |
869d81df DT |
231 | struct plock_op *op; |
232 | int rv; | |
233 | ||
234 | op = kzalloc(sizeof(*op), GFP_KERNEL); | |
235 | if (!op) | |
236 | return -ENOMEM; | |
237 | ||
238 | op->info.optype = GDLM_PLOCK_OP_GET; | |
3a2a9c96 | 239 | op->info.pid = fl->fl_pid; |
869d81df DT |
240 | op->info.ex = (fl->fl_type == F_WRLCK); |
241 | op->info.fsid = ls->id; | |
242 | op->info.number = name->ln_number; | |
243 | op->info.start = fl->fl_start; | |
244 | op->info.end = fl->fl_end; | |
d88101d4 | 245 | op->info.owner = (__u64)(long) fl->fl_owner; |
586759f0 | 246 | |
869d81df DT |
247 | send_op(op); |
248 | wait_event(recv_wq, (op->done != 0)); | |
249 | ||
250 | spin_lock(&ops_lock); | |
251 | if (!list_empty(&op->list)) { | |
d92a8d48 | 252 | printk(KERN_INFO "plock_get op on list\n"); |
869d81df DT |
253 | list_del(&op->list); |
254 | } | |
255 | spin_unlock(&ops_lock); | |
256 | ||
257 | rv = op->info.rv; | |
258 | ||
586759f0 ME |
259 | fl->fl_type = F_UNLCK; |
260 | if (rv == -ENOENT) | |
261 | rv = 0; | |
262 | else if (rv == 0 && op->info.pid != fl->fl_pid) { | |
869d81df DT |
263 | fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; |
264 | fl->fl_pid = op->info.pid; | |
265 | fl->fl_start = op->info.start; | |
266 | fl->fl_end = op->info.end; | |
267 | } | |
268 | ||
269 | kfree(op); | |
270 | return rv; | |
271 | } | |
272 | ||
273 | /* a read copies out one plock request from the send list */ | |
274 | static ssize_t dev_read(struct file *file, char __user *u, size_t count, | |
275 | loff_t *ppos) | |
276 | { | |
277 | struct gdlm_plock_info info; | |
278 | struct plock_op *op = NULL; | |
279 | ||
280 | if (count < sizeof(info)) | |
281 | return -EINVAL; | |
282 | ||
283 | spin_lock(&ops_lock); | |
284 | if (!list_empty(&send_list)) { | |
285 | op = list_entry(send_list.next, struct plock_op, list); | |
286 | list_move(&op->list, &recv_list); | |
287 | memcpy(&info, &op->info, sizeof(info)); | |
288 | } | |
289 | spin_unlock(&ops_lock); | |
290 | ||
291 | if (!op) | |
292 | return -EAGAIN; | |
293 | ||
294 | if (copy_to_user(u, &info, sizeof(info))) | |
295 | return -EFAULT; | |
296 | return sizeof(info); | |
297 | } | |
298 | ||
299 | /* a write copies in one plock result that should match a plock_op | |
300 | on the recv list */ | |
301 | static ssize_t dev_write(struct file *file, const char __user *u, size_t count, | |
302 | loff_t *ppos) | |
303 | { | |
304 | struct gdlm_plock_info info; | |
305 | struct plock_op *op; | |
306 | int found = 0; | |
307 | ||
308 | if (count != sizeof(info)) | |
309 | return -EINVAL; | |
310 | ||
311 | if (copy_from_user(&info, u, sizeof(info))) | |
312 | return -EFAULT; | |
313 | ||
314 | if (check_version(&info)) | |
315 | return -EINVAL; | |
316 | ||
317 | spin_lock(&ops_lock); | |
318 | list_for_each_entry(op, &recv_list, list) { | |
9b47c11d | 319 | if (op->info.fsid == info.fsid && op->info.number == info.number && |
08eac93a | 320 | op->info.owner == info.owner) { |
869d81df DT |
321 | list_del_init(&op->list); |
322 | found = 1; | |
323 | op->done = 1; | |
324 | memcpy(&op->info, &info, sizeof(info)); | |
325 | break; | |
326 | } | |
327 | } | |
328 | spin_unlock(&ops_lock); | |
329 | ||
586759f0 ME |
330 | if (found) { |
331 | struct plock_xop *xop; | |
332 | xop = (struct plock_xop *)op; | |
333 | if (xop->callback) | |
334 | count = gdlm_plock_callback(op); | |
335 | else | |
336 | wake_up(&recv_wq); | |
337 | } else | |
d92a8d48 | 338 | printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid, |
9229f013 | 339 | (unsigned long long)info.number); |
869d81df DT |
340 | return count; |
341 | } | |
342 | ||
343 | static unsigned int dev_poll(struct file *file, poll_table *wait) | |
344 | { | |
345 | poll_wait(file, &send_wq, wait); | |
346 | ||
347 | spin_lock(&ops_lock); | |
348 | if (!list_empty(&send_list)) { | |
349 | spin_unlock(&ops_lock); | |
350 | return POLLIN | POLLRDNORM; | |
351 | } | |
352 | spin_unlock(&ops_lock); | |
353 | return 0; | |
354 | } | |
355 | ||
00977a59 | 356 | static const struct file_operations dev_fops = { |
869d81df DT |
357 | .read = dev_read, |
358 | .write = dev_write, | |
359 | .poll = dev_poll, | |
360 | .owner = THIS_MODULE | |
361 | }; | |
362 | ||
363 | static struct miscdevice plock_dev_misc = { | |
364 | .minor = MISC_DYNAMIC_MINOR, | |
365 | .name = GDLM_PLOCK_MISC_NAME, | |
366 | .fops = &dev_fops | |
367 | }; | |
368 | ||
369 | int gdlm_plock_init(void) | |
370 | { | |
371 | int rv; | |
372 | ||
373 | spin_lock_init(&ops_lock); | |
374 | INIT_LIST_HEAD(&send_list); | |
375 | INIT_LIST_HEAD(&recv_list); | |
376 | init_waitqueue_head(&send_wq); | |
377 | init_waitqueue_head(&recv_wq); | |
378 | ||
379 | rv = misc_register(&plock_dev_misc); | |
380 | if (rv) | |
d92a8d48 SW |
381 | printk(KERN_INFO "gdlm_plock_init: misc_register failed %d", |
382 | rv); | |
869d81df DT |
383 | return rv; |
384 | } | |
385 | ||
386 | void gdlm_plock_exit(void) | |
387 | { | |
388 | if (misc_deregister(&plock_dev_misc) < 0) | |
d92a8d48 | 389 | printk(KERN_INFO "gdlm_plock_exit: misc_deregister failed"); |
869d81df DT |
390 | } |
391 |