Whamcloud - gitweb
6bf38e8a23cee55ac00235163bc5ba8a35a4cd8a
[fs/lustre-release.git] / lustre / ldlm / ldlm_lock.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Copyright (C) 2002 Cluster File Systems, Inc.
5  *
6  * This code is issued under the GNU General Public License.
7  * See the file COPYING in this distribution
8  *
9  * by Cluster File Systems, Inc.
10  * authors, Peter Braam <braam@clusterfs.com> & 
11  * Phil Schwan <phil@clusterfs.com>
12  */
13
14 #define EXPORT_SYMTAB
15
16 #include <linux/version.h>
17 #include <linux/module.h>
18 #include <linux/slab.h>
19 #include <asm/unistd.h>
20
21 #define DEBUG_SUBSYSTEM S_LDLM
22
23 #include <linux/obd_support.h>
24 #include <linux/obd_class.h>
25
26 #include <linux/lustre_dlm.h>
27
28 extern kmem_cache_t *ldlm_lock_slab;
29
30 static int ldlm_plain_compat(struct ldlm_lock *a, struct ldlm_lock *b);
31 static int ldlm_intent_compat(struct ldlm_lock *a, struct ldlm_lock *b);
32
33 ldlm_res_compat ldlm_res_compat_table [] = {
34         [LDLM_PLAIN] ldlm_plain_compat,
35         [LDLM_EXTENT] ldlm_extent_compat,
36         [LDLM_MDSINTENT] ldlm_intent_compat
37 };
38
39 ldlm_res_policy ldlm_res_policy_table [] = {
40         [LDLM_PLAIN] NULL,
41         [LDLM_EXTENT] ldlm_extent_policy,
42         [LDLM_MDSINTENT] NULL
43 };
44
45 static int ldlm_plain_compat(struct ldlm_lock *a, struct ldlm_lock *b)
46 {
47         return lockmode_compat(a->l_req_mode, b->l_req_mode);
48 }
49
50 static int ldlm_intent_compat(struct ldlm_lock *a, struct ldlm_lock *b)
51 {
52         LBUG();
53         return 0;
54 }
55
56 static struct ldlm_lock *ldlm_lock_new(struct ldlm_lock *parent,
57                                        struct ldlm_resource *resource)
58 {
59         struct ldlm_lock *lock;
60
61         if (resource == NULL)
62                 LBUG();
63
64         lock = kmem_cache_alloc(ldlm_lock_slab, SLAB_KERNEL);
65         if (lock == NULL)
66                 return NULL;
67
68         memset(lock, 0, sizeof(*lock));
69         lock->l_resource = resource;
70         INIT_LIST_HEAD(&lock->l_children);
71
72         if (parent != NULL) {
73                 lock->l_parent = parent;
74                 list_add(&lock->l_childof, &parent->l_children);
75         }
76
77         return lock;
78 }
79
80 void ldlm_lock_free(struct ldlm_lock *lock)
81 {
82         kmem_cache_free(ldlm_lock_slab, lock);
83 }
84
85 void ldlm_lock2desc(struct ldlm_lock *lock, struct ldlm_lock_desc *desc)
86 {
87         ldlm_res2desc(lock->l_resource, &desc->l_resource);
88         desc->l_req_mode = lock->l_req_mode;
89         desc->l_granted_mode = lock->l_granted_mode;
90         memcpy(&desc->l_extent, &lock->l_extent, sizeof(desc->l_extent));
91         memcpy(desc->l_version, lock->l_version, sizeof(desc->l_version));
92 }
93
94 static int ldlm_lock_compat(struct ldlm_lock *lock)
95 {
96         struct list_head *tmp;
97         int rc = 0;
98
99         list_for_each(tmp, &lock->l_resource->lr_granted) {
100                 struct ldlm_lock *child;
101                 ldlm_res_compat compat;
102
103                 child = list_entry(tmp, struct ldlm_lock, l_res_link);
104
105                 compat = ldlm_res_compat_table[child->l_resource->lr_type];
106                 if (compat(child, lock) ||
107                     lockmode_compat(child->l_req_mode, lock->l_req_mode))
108                         continue;
109
110                 rc = 1;
111
112                 if (child->l_blocking_ast != NULL)
113                         child->l_blocking_ast(child, lock, child->l_data,
114                                               child->l_data_len);
115         }
116
117         return rc;
118 }
119
120 static void ldlm_grant_lock(struct ldlm_resource *res, struct ldlm_lock *lock)
121 {
122         ldlm_resource_add_lock(res, &res->lr_granted, lock);
123         lock->l_granted_mode = lock->l_req_mode;
124
125         if (lock->l_granted_mode < res->lr_most_restr)
126                 res->lr_most_restr = lock->l_granted_mode;
127
128         if (lock->l_completion_ast)
129                 lock->l_completion_ast(lock, NULL,
130                                        lock->l_data, lock->l_data_len);
131 }
132
133 ldlm_error_t ldlm_local_lock_create(__u32 ns_id,
134                                     struct ldlm_handle *parent_lock_handle,
135                                     __u64 *res_id,
136                                     __u32 type,
137                                     struct ldlm_handle *lockh)
138 {
139         struct ldlm_namespace *ns;
140         struct ldlm_resource *res, *parent_res = NULL;
141         struct ldlm_lock *lock, *parent_lock;
142
143         ns = ldlm_namespace_find(ns_id);
144         if (ns == NULL || ns->ns_hash == NULL) 
145                 RETURN(-ELDLM_BAD_NAMESPACE);
146
147         parent_lock = ldlm_handle2object(parent_lock_handle);
148         if (parent_lock)
149                 parent_res = parent_lock->l_resource;
150
151         res = ldlm_resource_get(ns, parent_res, res_id, type, 1);
152         if (res == NULL)
153                 RETURN(-ENOMEM);
154
155         lock = ldlm_lock_new(parent_lock, res);
156         if (lock == NULL)
157                 RETURN(-ENOMEM);
158
159         ldlm_object2handle(lock, lockh);
160
161         return ELDLM_OK;
162 }
163
164 /* XXX: Revisit the error handling; we do not, for example, do
165  * ldlm_resource_put()s in our error cases, and we probably leak any allocated
166  * memory. */
167 ldlm_error_t ldlm_local_lock_enqueue(struct ldlm_handle *lockh,
168                                      ldlm_mode_t mode,
169                                      struct ldlm_extent *req_ex,
170                                      int *flags,
171                                      ldlm_lock_callback completion,
172                                      ldlm_lock_callback blocking,
173                                      void *data,
174                                      __u32 data_len)
175 {
176         struct ldlm_lock *lock;
177         struct ldlm_extent new_ex;
178         int incompat = 0, rc;
179         ldlm_res_policy policy;
180         ENTRY;
181
182         lock = ldlm_handle2object(lockh);
183         if ((policy = ldlm_res_policy_table[lock->l_resource->lr_type])) {
184                 rc = policy(lock->l_resource, req_ex, &new_ex, mode, NULL);
185                 if (rc == ELDLM_LOCK_CHANGED) {
186                         *flags |= LDLM_FL_LOCK_CHANGED;
187                         memcpy(req_ex, &new_ex, sizeof(new_ex));
188                 }
189         }
190
191         if ((lock->l_resource->lr_type == LDLM_EXTENT && !req_ex) ||
192             (lock->l_resource->lr_type != LDLM_EXTENT && req_ex))
193                 LBUG();
194         if (req_ex)
195                 memcpy(&lock->l_extent, req_ex, sizeof(*req_ex));
196         lock->l_req_mode = mode;
197         lock->l_data = data;
198         lock->l_data_len = data_len;
199         lock->l_completion_ast = completion;
200         lock->l_blocking_ast = blocking;
201         spin_lock(&lock->l_resource->lr_lock);
202
203         /* FIXME: We may want to optimize by checking lr_most_restr */
204
205         if (!list_empty(&lock->l_resource->lr_converting)) {
206                 ldlm_resource_add_lock(lock->l_resource,
207                                        lock->l_resource->lr_waiting.prev, lock);
208                 *flags |= LDLM_FL_BLOCK_CONV;
209                 GOTO(out, ELDLM_OK);
210         }
211         if (!list_empty(&lock->l_resource->lr_waiting)) {
212                 ldlm_resource_add_lock(lock->l_resource,
213                                        lock->l_resource->lr_waiting.prev, lock);
214                 *flags |= LDLM_FL_BLOCK_WAIT;
215                 GOTO(out, ELDLM_OK);
216         }
217
218         incompat = ldlm_lock_compat(lock);
219         if (incompat) {
220                 ldlm_resource_add_lock(lock->l_resource,
221                                        lock->l_resource->lr_waiting.prev, lock);
222                 *flags |= LDLM_FL_BLOCK_GRANTED;
223                 GOTO(out, ELDLM_OK);
224         }
225
226         ldlm_grant_lock(lock->l_resource, lock);
227         EXIT;
228  out:
229         spin_unlock(&lock->l_resource->lr_lock);
230         return ELDLM_OK;
231 }
232
233 static int ldlm_reprocess_queue(struct ldlm_resource *res,
234                                 struct list_head *converting)
235 {
236         struct list_head *tmp, *pos;
237         int incompat = 0;
238
239         list_for_each_safe(tmp, pos, converting) { 
240                 struct ldlm_lock *pending;
241                 pending = list_entry(tmp, struct ldlm_lock, l_res_link);
242
243                 incompat = ldlm_lock_compat(pending);
244                 if (incompat)
245                         break;
246
247                 list_del(&pending->l_res_link); 
248                 ldlm_grant_lock(res, pending);
249         }
250
251         return incompat;
252 }
253
254 static void ldlm_reprocess_all(struct ldlm_resource *res)
255 {
256         ldlm_reprocess_queue(res, &res->lr_converting);
257         if (list_empty(&res->lr_converting))
258                 ldlm_reprocess_queue(res, &res->lr_waiting);
259 }
260
261 ldlm_error_t ldlm_local_lock_cancel(struct ldlm_handle *lockh)
262 {
263         struct ldlm_lock *lock;
264         struct ldlm_resource *res;
265         ENTRY;
266
267         lock = ldlm_handle2object(lockh);
268         res = lock->l_resource;
269
270         ldlm_resource_del_lock(lock);
271
272         ldlm_lock_free(lock);
273         if (ldlm_resource_put(res))
274                 RETURN(ELDLM_OK);
275         ldlm_reprocess_all(res);
276
277         RETURN(ELDLM_OK);
278 }
279
280 ldlm_error_t ldlm_local_lock_convert(struct ldlm_handle *lockh,
281                                      int new_mode, int *flags)
282 {
283         struct ldlm_lock *lock;
284         struct ldlm_resource *res;
285         ENTRY;
286
287         lock = ldlm_handle2object(lockh);
288         res = lock->l_resource;
289         list_del(&lock->l_res_link);
290         lock->l_req_mode = new_mode;
291
292         list_add(&lock->l_res_link, res->lr_converting.prev);
293
294         ldlm_reprocess_all(res);
295
296         RETURN(ELDLM_OK);
297 }
298
299 void ldlm_lock_dump(struct ldlm_lock *lock)
300 {
301         char ver[128];
302
303         if (RES_VERSION_SIZE != 4)
304                 LBUG();
305
306         snprintf(ver, sizeof(ver), "%x %x %x %x",
307                  lock->l_version[0], lock->l_version[1],
308                  lock->l_version[2], lock->l_version[3]);
309
310         CDEBUG(D_OTHER, "  -- Lock dump: %p (%s)\n", lock, ver);
311         CDEBUG(D_OTHER, "  Parent: %p\n", lock->l_parent);
312         CDEBUG(D_OTHER, "  Resource: %p\n", lock->l_resource);
313         CDEBUG(D_OTHER, "  Requested mode: %d, granted mode: %d\n",
314                (int)lock->l_req_mode, (int)lock->l_granted_mode);
315         if (lock->l_resource->lr_type == LDLM_EXTENT)
316                 CDEBUG(D_OTHER, "  Extent: %Lu -> %Lu\n",
317                        lock->l_extent.start, lock->l_extent.end);
318 }