Merge branch 'master' of git://git.infradead.org/users/pcmoore/lblnet-2.6_next into...
[linux-2.6] / fs / gfs2 / locking / dlm / mount.c
1 /*
2  * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
3  * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
4  *
5  * This copyrighted material is made available to anyone wishing to use,
6  * modify, copy, or redistribute it subject to the terms and conditions
7  * of the GNU General Public License version 2.
8  */
9
10 #include "lock_dlm.h"
11
12 const struct lm_lockops gdlm_ops;
13
14
15 static struct gdlm_ls *init_gdlm(lm_callback_t cb, struct gfs2_sbd *sdp,
16                                  int flags, char *table_name)
17 {
18         struct gdlm_ls *ls;
19         char buf[256], *p;
20
21         ls = kzalloc(sizeof(struct gdlm_ls), GFP_KERNEL);
22         if (!ls)
23                 return NULL;
24
25         ls->fscb = cb;
26         ls->sdp = sdp;
27         ls->fsflags = flags;
28         spin_lock_init(&ls->async_lock);
29         INIT_LIST_HEAD(&ls->delayed);
30         INIT_LIST_HEAD(&ls->submit);
31         init_waitqueue_head(&ls->thread_wait);
32         init_waitqueue_head(&ls->wait_control);
33         ls->jid = -1;
34
35         strncpy(buf, table_name, 256);
36         buf[255] = '\0';
37
38         p = strchr(buf, ':');
39         if (!p) {
40                 log_info("invalid table_name \"%s\"", table_name);
41                 kfree(ls);
42                 return NULL;
43         }
44         *p = '\0';
45         p++;
46
47         strncpy(ls->clustername, buf, GDLM_NAME_LEN);
48         strncpy(ls->fsname, p, GDLM_NAME_LEN);
49
50         return ls;
51 }
52
53 static int make_args(struct gdlm_ls *ls, char *data_arg, int *nodir)
54 {
55         char data[256];
56         char *options, *x, *y;
57         int error = 0;
58
59         memset(data, 0, 256);
60         strncpy(data, data_arg, 255);
61
62         if (!strlen(data)) {
63                 log_error("no mount options, (u)mount helpers not installed");
64                 return -EINVAL;
65         }
66
67         for (options = data; (x = strsep(&options, ":")); ) {
68                 if (!*x)
69                         continue;
70
71                 y = strchr(x, '=');
72                 if (y)
73                         *y++ = 0;
74
75                 if (!strcmp(x, "jid")) {
76                         if (!y) {
77                                 log_error("need argument to jid");
78                                 error = -EINVAL;
79                                 break;
80                         }
81                         sscanf(y, "%u", &ls->jid);
82
83                 } else if (!strcmp(x, "first")) {
84                         if (!y) {
85                                 log_error("need argument to first");
86                                 error = -EINVAL;
87                                 break;
88                         }
89                         sscanf(y, "%u", &ls->first);
90
91                 } else if (!strcmp(x, "id")) {
92                         if (!y) {
93                                 log_error("need argument to id");
94                                 error = -EINVAL;
95                                 break;
96                         }
97                         sscanf(y, "%u", &ls->id);
98
99                 } else if (!strcmp(x, "nodir")) {
100                         if (!y) {
101                                 log_error("need argument to nodir");
102                                 error = -EINVAL;
103                                 break;
104                         }
105                         sscanf(y, "%u", nodir);
106
107                 } else {
108                         log_error("unkonwn option: %s", x);
109                         error = -EINVAL;
110                         break;
111                 }
112         }
113
114         return error;
115 }
116
117 static int gdlm_mount(char *table_name, char *host_data,
118                         lm_callback_t cb, void *cb_data,
119                         unsigned int min_lvb_size, int flags,
120                         struct lm_lockstruct *lockstruct,
121                         struct kobject *fskobj)
122 {
123         struct gdlm_ls *ls;
124         int error = -ENOMEM, nodir = 0;
125
126         if (min_lvb_size > GDLM_LVB_SIZE)
127                 goto out;
128
129         ls = init_gdlm(cb, cb_data, flags, table_name);
130         if (!ls)
131                 goto out;
132
133         error = make_args(ls, host_data, &nodir);
134         if (error)
135                 goto out;
136
137         error = gdlm_init_threads(ls);
138         if (error)
139                 goto out_free;
140
141         error = gdlm_kobject_setup(ls, fskobj);
142         if (error)
143                 goto out_thread;
144
145         error = dlm_new_lockspace(ls->fsname, strlen(ls->fsname),
146                                   &ls->dlm_lockspace,
147                                   DLM_LSFL_FS | DLM_LSFL_NEWEXCL |
148                                   (nodir ? DLM_LSFL_NODIR : 0),
149                                   GDLM_LVB_SIZE);
150         if (error) {
151                 log_error("dlm_new_lockspace error %d", error);
152                 goto out_kobj;
153         }
154
155         lockstruct->ls_jid = ls->jid;
156         lockstruct->ls_first = ls->first;
157         lockstruct->ls_lockspace = ls;
158         lockstruct->ls_ops = &gdlm_ops;
159         lockstruct->ls_flags = 0;
160         lockstruct->ls_lvb_size = GDLM_LVB_SIZE;
161         return 0;
162
163 out_kobj:
164         gdlm_kobject_release(ls);
165 out_thread:
166         gdlm_release_threads(ls);
167 out_free:
168         kfree(ls);
169 out:
170         return error;
171 }
172
173 static void gdlm_unmount(void *lockspace)
174 {
175         struct gdlm_ls *ls = lockspace;
176
177         log_debug("unmount flags %lx", ls->flags);
178
179         /* FIXME: serialize unmount and withdraw in case they
180            happen at once.  Also, if unmount follows withdraw,
181            wait for withdraw to finish. */
182
183         if (test_bit(DFL_WITHDRAW, &ls->flags))
184                 goto out;
185
186         gdlm_kobject_release(ls);
187         dlm_release_lockspace(ls->dlm_lockspace, 2);
188         gdlm_release_threads(ls);
189         BUG_ON(ls->all_locks_count);
190 out:
191         kfree(ls);
192 }
193
194 static void gdlm_recovery_done(void *lockspace, unsigned int jid,
195                                unsigned int message)
196 {
197         struct gdlm_ls *ls = lockspace;
198         ls->recover_jid_done = jid;
199         ls->recover_jid_status = message;
200         kobject_uevent(&ls->kobj, KOBJ_CHANGE);
201 }
202
203 static void gdlm_others_may_mount(void *lockspace)
204 {
205         struct gdlm_ls *ls = lockspace;
206         ls->first_done = 1;
207         kobject_uevent(&ls->kobj, KOBJ_CHANGE);
208 }
209
210 /* Userspace gets the offline uevent, blocks new gfs locks on
211    other mounters, and lets us know (sets WITHDRAW flag).  Then,
212    userspace leaves the mount group while we leave the lockspace. */
213
214 static void gdlm_withdraw(void *lockspace)
215 {
216         struct gdlm_ls *ls = lockspace;
217
218         kobject_uevent(&ls->kobj, KOBJ_OFFLINE);
219
220         wait_event_interruptible(ls->wait_control,
221                                  test_bit(DFL_WITHDRAW, &ls->flags));
222
223         dlm_release_lockspace(ls->dlm_lockspace, 2);
224         gdlm_release_threads(ls);
225         gdlm_kobject_release(ls);
226 }
227
228 static int gdlm_plock(void *lockspace, struct lm_lockname *name,
229                struct file *file, int cmd, struct file_lock *fl)
230 {
231         struct gdlm_ls *ls = lockspace;
232         return dlm_posix_lock(ls->dlm_lockspace, name->ln_number, file, cmd, fl);
233 }
234
235 static int gdlm_punlock(void *lockspace, struct lm_lockname *name,
236                  struct file *file, struct file_lock *fl)
237 {
238         struct gdlm_ls *ls = lockspace;
239         return dlm_posix_unlock(ls->dlm_lockspace, name->ln_number, file, fl);
240 }
241
242 static int gdlm_plock_get(void *lockspace, struct lm_lockname *name,
243                    struct file *file, struct file_lock *fl)
244 {
245         struct gdlm_ls *ls = lockspace;
246         return dlm_posix_get(ls->dlm_lockspace, name->ln_number, file, fl);
247 }
248
249 const struct lm_lockops gdlm_ops = {
250         .lm_proto_name = "lock_dlm",
251         .lm_mount = gdlm_mount,
252         .lm_others_may_mount = gdlm_others_may_mount,
253         .lm_unmount = gdlm_unmount,
254         .lm_withdraw = gdlm_withdraw,
255         .lm_get_lock = gdlm_get_lock,
256         .lm_put_lock = gdlm_put_lock,
257         .lm_lock = gdlm_lock,
258         .lm_unlock = gdlm_unlock,
259         .lm_plock = gdlm_plock,
260         .lm_punlock = gdlm_punlock,
261         .lm_plock_get = gdlm_plock_get,
262         .lm_cancel = gdlm_cancel,
263         .lm_hold_lvb = gdlm_hold_lvb,
264         .lm_unhold_lvb = gdlm_unhold_lvb,
265         .lm_recovery_done = gdlm_recovery_done,
266         .lm_owner = THIS_MODULE,
267 };
268