Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/dlm/dir.c
15112 views
1
/******************************************************************************
2
*******************************************************************************
3
**
4
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
5
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
6
**
7
** This copyrighted material is made available to anyone wishing to use,
8
** modify, copy, or redistribute it subject to the terms and conditions
9
** of the GNU General Public License v.2.
10
**
11
*******************************************************************************
12
******************************************************************************/
13
14
#include "dlm_internal.h"
15
#include "lockspace.h"
16
#include "member.h"
17
#include "lowcomms.h"
18
#include "rcom.h"
19
#include "config.h"
20
#include "memory.h"
21
#include "recover.h"
22
#include "util.h"
23
#include "lock.h"
24
#include "dir.h"
25
26
27
static void put_free_de(struct dlm_ls *ls, struct dlm_direntry *de)
28
{
29
spin_lock(&ls->ls_recover_list_lock);
30
list_add(&de->list, &ls->ls_recover_list);
31
spin_unlock(&ls->ls_recover_list_lock);
32
}
33
34
static struct dlm_direntry *get_free_de(struct dlm_ls *ls, int len)
35
{
36
int found = 0;
37
struct dlm_direntry *de;
38
39
spin_lock(&ls->ls_recover_list_lock);
40
list_for_each_entry(de, &ls->ls_recover_list, list) {
41
if (de->length == len) {
42
list_del(&de->list);
43
de->master_nodeid = 0;
44
memset(de->name, 0, len);
45
found = 1;
46
break;
47
}
48
}
49
spin_unlock(&ls->ls_recover_list_lock);
50
51
if (!found)
52
de = kzalloc(sizeof(struct dlm_direntry) + len, GFP_NOFS);
53
return de;
54
}
55
56
void dlm_clear_free_entries(struct dlm_ls *ls)
57
{
58
struct dlm_direntry *de;
59
60
spin_lock(&ls->ls_recover_list_lock);
61
while (!list_empty(&ls->ls_recover_list)) {
62
de = list_entry(ls->ls_recover_list.next, struct dlm_direntry,
63
list);
64
list_del(&de->list);
65
kfree(de);
66
}
67
spin_unlock(&ls->ls_recover_list_lock);
68
}
69
70
/*
71
* We use the upper 16 bits of the hash value to select the directory node.
72
* Low bits are used for distribution of rsb's among hash buckets on each node.
73
*
74
* To give the exact range wanted (0 to num_nodes-1), we apply a modulus of
75
* num_nodes to the hash value. This value in the desired range is used as an
76
* offset into the sorted list of nodeid's to give the particular nodeid.
77
*/
78
79
int dlm_hash2nodeid(struct dlm_ls *ls, uint32_t hash)
80
{
81
struct list_head *tmp;
82
struct dlm_member *memb = NULL;
83
uint32_t node, n = 0;
84
int nodeid;
85
86
if (ls->ls_num_nodes == 1) {
87
nodeid = dlm_our_nodeid();
88
goto out;
89
}
90
91
if (ls->ls_node_array) {
92
node = (hash >> 16) % ls->ls_total_weight;
93
nodeid = ls->ls_node_array[node];
94
goto out;
95
}
96
97
/* make_member_array() failed to kmalloc ls_node_array... */
98
99
node = (hash >> 16) % ls->ls_num_nodes;
100
101
list_for_each(tmp, &ls->ls_nodes) {
102
if (n++ != node)
103
continue;
104
memb = list_entry(tmp, struct dlm_member, list);
105
break;
106
}
107
108
DLM_ASSERT(memb , printk("num_nodes=%u n=%u node=%u\n",
109
ls->ls_num_nodes, n, node););
110
nodeid = memb->nodeid;
111
out:
112
return nodeid;
113
}
114
115
int dlm_dir_nodeid(struct dlm_rsb *r)
116
{
117
return dlm_hash2nodeid(r->res_ls, r->res_hash);
118
}
119
120
static inline uint32_t dir_hash(struct dlm_ls *ls, char *name, int len)
121
{
122
uint32_t val;
123
124
val = jhash(name, len, 0);
125
val &= (ls->ls_dirtbl_size - 1);
126
127
return val;
128
}
129
130
static void add_entry_to_hash(struct dlm_ls *ls, struct dlm_direntry *de)
131
{
132
uint32_t bucket;
133
134
bucket = dir_hash(ls, de->name, de->length);
135
list_add_tail(&de->list, &ls->ls_dirtbl[bucket].list);
136
}
137
138
static struct dlm_direntry *search_bucket(struct dlm_ls *ls, char *name,
139
int namelen, uint32_t bucket)
140
{
141
struct dlm_direntry *de;
142
143
list_for_each_entry(de, &ls->ls_dirtbl[bucket].list, list) {
144
if (de->length == namelen && !memcmp(name, de->name, namelen))
145
goto out;
146
}
147
de = NULL;
148
out:
149
return de;
150
}
151
152
void dlm_dir_remove_entry(struct dlm_ls *ls, int nodeid, char *name, int namelen)
153
{
154
struct dlm_direntry *de;
155
uint32_t bucket;
156
157
bucket = dir_hash(ls, name, namelen);
158
159
spin_lock(&ls->ls_dirtbl[bucket].lock);
160
161
de = search_bucket(ls, name, namelen, bucket);
162
163
if (!de) {
164
log_error(ls, "remove fr %u none", nodeid);
165
goto out;
166
}
167
168
if (de->master_nodeid != nodeid) {
169
log_error(ls, "remove fr %u ID %u", nodeid, de->master_nodeid);
170
goto out;
171
}
172
173
list_del(&de->list);
174
kfree(de);
175
out:
176
spin_unlock(&ls->ls_dirtbl[bucket].lock);
177
}
178
179
void dlm_dir_clear(struct dlm_ls *ls)
180
{
181
struct list_head *head;
182
struct dlm_direntry *de;
183
int i;
184
185
DLM_ASSERT(list_empty(&ls->ls_recover_list), );
186
187
for (i = 0; i < ls->ls_dirtbl_size; i++) {
188
spin_lock(&ls->ls_dirtbl[i].lock);
189
head = &ls->ls_dirtbl[i].list;
190
while (!list_empty(head)) {
191
de = list_entry(head->next, struct dlm_direntry, list);
192
list_del(&de->list);
193
put_free_de(ls, de);
194
}
195
spin_unlock(&ls->ls_dirtbl[i].lock);
196
}
197
}
198
199
int dlm_recover_directory(struct dlm_ls *ls)
200
{
201
struct dlm_member *memb;
202
struct dlm_direntry *de;
203
char *b, *last_name = NULL;
204
int error = -ENOMEM, last_len, count = 0;
205
uint16_t namelen;
206
207
log_debug(ls, "dlm_recover_directory");
208
209
if (dlm_no_directory(ls))
210
goto out_status;
211
212
dlm_dir_clear(ls);
213
214
last_name = kmalloc(DLM_RESNAME_MAXLEN, GFP_NOFS);
215
if (!last_name)
216
goto out;
217
218
list_for_each_entry(memb, &ls->ls_nodes, list) {
219
memset(last_name, 0, DLM_RESNAME_MAXLEN);
220
last_len = 0;
221
222
for (;;) {
223
int left;
224
error = dlm_recovery_stopped(ls);
225
if (error)
226
goto out_free;
227
228
error = dlm_rcom_names(ls, memb->nodeid,
229
last_name, last_len);
230
if (error)
231
goto out_free;
232
233
schedule();
234
235
/*
236
* pick namelen/name pairs out of received buffer
237
*/
238
239
b = ls->ls_recover_buf->rc_buf;
240
left = ls->ls_recover_buf->rc_header.h_length;
241
left -= sizeof(struct dlm_rcom);
242
243
for (;;) {
244
__be16 v;
245
246
error = -EINVAL;
247
if (left < sizeof(__be16))
248
goto out_free;
249
250
memcpy(&v, b, sizeof(__be16));
251
namelen = be16_to_cpu(v);
252
b += sizeof(__be16);
253
left -= sizeof(__be16);
254
255
/* namelen of 0xFFFFF marks end of names for
256
this node; namelen of 0 marks end of the
257
buffer */
258
259
if (namelen == 0xFFFF)
260
goto done;
261
if (!namelen)
262
break;
263
264
if (namelen > left)
265
goto out_free;
266
267
if (namelen > DLM_RESNAME_MAXLEN)
268
goto out_free;
269
270
error = -ENOMEM;
271
de = get_free_de(ls, namelen);
272
if (!de)
273
goto out_free;
274
275
de->master_nodeid = memb->nodeid;
276
de->length = namelen;
277
last_len = namelen;
278
memcpy(de->name, b, namelen);
279
memcpy(last_name, b, namelen);
280
b += namelen;
281
left -= namelen;
282
283
add_entry_to_hash(ls, de);
284
count++;
285
}
286
}
287
done:
288
;
289
}
290
291
out_status:
292
error = 0;
293
dlm_set_recover_status(ls, DLM_RS_DIR);
294
log_debug(ls, "dlm_recover_directory %d entries", count);
295
out_free:
296
kfree(last_name);
297
out:
298
dlm_clear_free_entries(ls);
299
return error;
300
}
301
302
static int get_entry(struct dlm_ls *ls, int nodeid, char *name,
303
int namelen, int *r_nodeid)
304
{
305
struct dlm_direntry *de, *tmp;
306
uint32_t bucket;
307
308
bucket = dir_hash(ls, name, namelen);
309
310
spin_lock(&ls->ls_dirtbl[bucket].lock);
311
de = search_bucket(ls, name, namelen, bucket);
312
if (de) {
313
*r_nodeid = de->master_nodeid;
314
spin_unlock(&ls->ls_dirtbl[bucket].lock);
315
if (*r_nodeid == nodeid)
316
return -EEXIST;
317
return 0;
318
}
319
320
spin_unlock(&ls->ls_dirtbl[bucket].lock);
321
322
if (namelen > DLM_RESNAME_MAXLEN)
323
return -EINVAL;
324
325
de = kzalloc(sizeof(struct dlm_direntry) + namelen, GFP_NOFS);
326
if (!de)
327
return -ENOMEM;
328
329
de->master_nodeid = nodeid;
330
de->length = namelen;
331
memcpy(de->name, name, namelen);
332
333
spin_lock(&ls->ls_dirtbl[bucket].lock);
334
tmp = search_bucket(ls, name, namelen, bucket);
335
if (tmp) {
336
kfree(de);
337
de = tmp;
338
} else {
339
list_add_tail(&de->list, &ls->ls_dirtbl[bucket].list);
340
}
341
*r_nodeid = de->master_nodeid;
342
spin_unlock(&ls->ls_dirtbl[bucket].lock);
343
return 0;
344
}
345
346
int dlm_dir_lookup(struct dlm_ls *ls, int nodeid, char *name, int namelen,
347
int *r_nodeid)
348
{
349
return get_entry(ls, nodeid, name, namelen, r_nodeid);
350
}
351
352
static struct dlm_rsb *find_rsb_root(struct dlm_ls *ls, char *name, int len)
353
{
354
struct dlm_rsb *r;
355
356
down_read(&ls->ls_root_sem);
357
list_for_each_entry(r, &ls->ls_root_list, res_root_list) {
358
if (len == r->res_length && !memcmp(name, r->res_name, len)) {
359
up_read(&ls->ls_root_sem);
360
return r;
361
}
362
}
363
up_read(&ls->ls_root_sem);
364
return NULL;
365
}
366
367
/* Find the rsb where we left off (or start again), then send rsb names
368
for rsb's we're master of and whose directory node matches the requesting
369
node. inbuf is the rsb name last sent, inlen is the name's length */
370
371
void dlm_copy_master_names(struct dlm_ls *ls, char *inbuf, int inlen,
372
char *outbuf, int outlen, int nodeid)
373
{
374
struct list_head *list;
375
struct dlm_rsb *r;
376
int offset = 0, dir_nodeid;
377
__be16 be_namelen;
378
379
down_read(&ls->ls_root_sem);
380
381
if (inlen > 1) {
382
r = find_rsb_root(ls, inbuf, inlen);
383
if (!r) {
384
inbuf[inlen - 1] = '\0';
385
log_error(ls, "copy_master_names from %d start %d %s",
386
nodeid, inlen, inbuf);
387
goto out;
388
}
389
list = r->res_root_list.next;
390
} else {
391
list = ls->ls_root_list.next;
392
}
393
394
for (offset = 0; list != &ls->ls_root_list; list = list->next) {
395
r = list_entry(list, struct dlm_rsb, res_root_list);
396
if (r->res_nodeid)
397
continue;
398
399
dir_nodeid = dlm_dir_nodeid(r);
400
if (dir_nodeid != nodeid)
401
continue;
402
403
/*
404
* The block ends when we can't fit the following in the
405
* remaining buffer space:
406
* namelen (uint16_t) +
407
* name (r->res_length) +
408
* end-of-block record 0x0000 (uint16_t)
409
*/
410
411
if (offset + sizeof(uint16_t)*2 + r->res_length > outlen) {
412
/* Write end-of-block record */
413
be_namelen = cpu_to_be16(0);
414
memcpy(outbuf + offset, &be_namelen, sizeof(__be16));
415
offset += sizeof(__be16);
416
goto out;
417
}
418
419
be_namelen = cpu_to_be16(r->res_length);
420
memcpy(outbuf + offset, &be_namelen, sizeof(__be16));
421
offset += sizeof(__be16);
422
memcpy(outbuf + offset, r->res_name, r->res_length);
423
offset += r->res_length;
424
}
425
426
/*
427
* If we've reached the end of the list (and there's room) write a
428
* terminating record.
429
*/
430
431
if ((list == &ls->ls_root_list) &&
432
(offset + sizeof(uint16_t) <= outlen)) {
433
be_namelen = cpu_to_be16(0xFFFF);
434
memcpy(outbuf + offset, &be_namelen, sizeof(__be16));
435
offset += sizeof(__be16);
436
}
437
438
out:
439
up_read(&ls->ls_root_sem);
440
}
441
442
443