12522fe45SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e7fd4179SDavid Teigland /******************************************************************************
3e7fd4179SDavid Teigland *******************************************************************************
4e7fd4179SDavid Teigland **
5e7fd4179SDavid Teigland ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
6e7fd4179SDavid Teigland ** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
7e7fd4179SDavid Teigland **
8e7fd4179SDavid Teigland **
9e7fd4179SDavid Teigland *******************************************************************************
10e7fd4179SDavid Teigland ******************************************************************************/
11e7fd4179SDavid Teigland
12e7fd4179SDavid Teigland #include "dlm_internal.h"
13e7fd4179SDavid Teigland #include "lockspace.h"
14e7fd4179SDavid Teigland #include "member.h"
15e7fd4179SDavid Teigland #include "lowcomms.h"
16e7fd4179SDavid Teigland #include "rcom.h"
17e7fd4179SDavid Teigland #include "config.h"
18e7fd4179SDavid Teigland #include "memory.h"
19e7fd4179SDavid Teigland #include "recover.h"
20e7fd4179SDavid Teigland #include "util.h"
21e7fd4179SDavid Teigland #include "lock.h"
22e7fd4179SDavid Teigland #include "dir.h"
23e7fd4179SDavid Teigland
24e7fd4179SDavid Teigland /*
25e7fd4179SDavid Teigland * We use the upper 16 bits of the hash value to select the directory node.
26e7fd4179SDavid Teigland * Low bits are used for distribution of rsb's among hash buckets on each node.
27e7fd4179SDavid Teigland *
28e7fd4179SDavid Teigland * To give the exact range wanted (0 to num_nodes-1), we apply a modulus of
29e7fd4179SDavid Teigland * num_nodes to the hash value. This value in the desired range is used as an
30e7fd4179SDavid Teigland * offset into the sorted list of nodeid's to give the particular nodeid.
31e7fd4179SDavid Teigland */
32e7fd4179SDavid Teigland
dlm_hash2nodeid(struct dlm_ls * ls,uint32_t hash)33e7fd4179SDavid Teigland int dlm_hash2nodeid(struct dlm_ls *ls, uint32_t hash)
34e7fd4179SDavid Teigland {
35c04fecb4SDavid Teigland uint32_t node;
36e7fd4179SDavid Teigland
37c04fecb4SDavid Teigland if (ls->ls_num_nodes == 1)
38c04fecb4SDavid Teigland return dlm_our_nodeid();
39c04fecb4SDavid Teigland else {
40e7fd4179SDavid Teigland node = (hash >> 16) % ls->ls_total_weight;
41c04fecb4SDavid Teigland return ls->ls_node_array[node];
42e7fd4179SDavid Teigland }
43e7fd4179SDavid Teigland }
44e7fd4179SDavid Teigland
dlm_dir_nodeid(struct dlm_rsb * r)45e7fd4179SDavid Teigland int dlm_dir_nodeid(struct dlm_rsb *r)
46e7fd4179SDavid Teigland {
47c04fecb4SDavid Teigland return r->res_dir_nodeid;
48e7fd4179SDavid Teigland }
49e7fd4179SDavid Teigland
dlm_recover_dir_nodeid(struct dlm_ls * ls)50c04fecb4SDavid Teigland void dlm_recover_dir_nodeid(struct dlm_ls *ls)
51e7fd4179SDavid Teigland {
52c04fecb4SDavid Teigland struct dlm_rsb *r;
53e7fd4179SDavid Teigland
54c04fecb4SDavid Teigland down_read(&ls->ls_root_sem);
55c04fecb4SDavid Teigland list_for_each_entry(r, &ls->ls_root_list, res_root_list) {
56c04fecb4SDavid Teigland r->res_dir_nodeid = dlm_hash2nodeid(ls, r->res_hash);
57e7fd4179SDavid Teigland }
58c04fecb4SDavid Teigland up_read(&ls->ls_root_sem);
59e7fd4179SDavid Teigland }
60e7fd4179SDavid Teigland
dlm_recover_directory(struct dlm_ls * ls,uint64_t seq)61c4f4e135SAlexander Aring int dlm_recover_directory(struct dlm_ls *ls, uint64_t seq)
62e7fd4179SDavid Teigland {
63e7fd4179SDavid Teigland struct dlm_member *memb;
64e7fd4179SDavid Teigland char *b, *last_name = NULL;
65c04fecb4SDavid Teigland int error = -ENOMEM, last_len, nodeid, result;
66e7fd4179SDavid Teigland uint16_t namelen;
67c04fecb4SDavid Teigland unsigned int count = 0, count_match = 0, count_bad = 0, count_add = 0;
68e7fd4179SDavid Teigland
69075f0177SDavid Teigland log_rinfo(ls, "dlm_recover_directory");
70e7fd4179SDavid Teigland
71e7fd4179SDavid Teigland if (dlm_no_directory(ls))
72e7fd4179SDavid Teigland goto out_status;
73e7fd4179SDavid Teigland
74573c24c4SDavid Teigland last_name = kmalloc(DLM_RESNAME_MAXLEN, GFP_NOFS);
75e7fd4179SDavid Teigland if (!last_name)
76e7fd4179SDavid Teigland goto out;
77e7fd4179SDavid Teigland
78e7fd4179SDavid Teigland list_for_each_entry(memb, &ls->ls_nodes, list) {
79c04fecb4SDavid Teigland if (memb->nodeid == dlm_our_nodeid())
80c04fecb4SDavid Teigland continue;
81c04fecb4SDavid Teigland
82e7fd4179SDavid Teigland memset(last_name, 0, DLM_RESNAME_MAXLEN);
83e7fd4179SDavid Teigland last_len = 0;
84e7fd4179SDavid Teigland
85e7fd4179SDavid Teigland for (;;) {
86cd9df1aaSAl Viro int left;
87e10249b1SAlexander Aring if (dlm_recovery_stopped(ls)) {
88aee742c9SAlexander Aring error = -EINTR;
89e7fd4179SDavid Teigland goto out_free;
90aee742c9SAlexander Aring }
91e7fd4179SDavid Teigland
92e7fd4179SDavid Teigland error = dlm_rcom_names(ls, memb->nodeid,
93c4f4e135SAlexander Aring last_name, last_len, seq);
94e7fd4179SDavid Teigland if (error)
95e7fd4179SDavid Teigland goto out_free;
96e7fd4179SDavid Teigland
97c04fecb4SDavid Teigland cond_resched();
98e7fd4179SDavid Teigland
99e7fd4179SDavid Teigland /*
100e7fd4179SDavid Teigland * pick namelen/name pairs out of received buffer
101e7fd4179SDavid Teigland */
102e7fd4179SDavid Teigland
1034007685cSAl Viro b = ls->ls_recover_buf->rc_buf;
1043428785aSAlexander Aring left = le16_to_cpu(ls->ls_recover_buf->rc_header.h_length);
105cd9df1aaSAl Viro left -= sizeof(struct dlm_rcom);
106e7fd4179SDavid Teigland
107e7fd4179SDavid Teigland for (;;) {
108cd9df1aaSAl Viro __be16 v;
109cd9df1aaSAl Viro
110cd9df1aaSAl Viro error = -EINVAL;
111cd9df1aaSAl Viro if (left < sizeof(__be16))
112cd9df1aaSAl Viro goto out_free;
113cd9df1aaSAl Viro
114cd9df1aaSAl Viro memcpy(&v, b, sizeof(__be16));
115cd9df1aaSAl Viro namelen = be16_to_cpu(v);
116cd9df1aaSAl Viro b += sizeof(__be16);
117cd9df1aaSAl Viro left -= sizeof(__be16);
118e7fd4179SDavid Teigland
119e7fd4179SDavid Teigland /* namelen of 0xFFFFF marks end of names for
120e7fd4179SDavid Teigland this node; namelen of 0 marks end of the
121e7fd4179SDavid Teigland buffer */
122e7fd4179SDavid Teigland
123e7fd4179SDavid Teigland if (namelen == 0xFFFF)
124e7fd4179SDavid Teigland goto done;
125e7fd4179SDavid Teigland if (!namelen)
126e7fd4179SDavid Teigland break;
127e7fd4179SDavid Teigland
128cd9df1aaSAl Viro if (namelen > left)
129cd9df1aaSAl Viro goto out_free;
130cd9df1aaSAl Viro
131cd9df1aaSAl Viro if (namelen > DLM_RESNAME_MAXLEN)
132cd9df1aaSAl Viro goto out_free;
133cd9df1aaSAl Viro
134c04fecb4SDavid Teigland error = dlm_master_lookup(ls, memb->nodeid,
135c04fecb4SDavid Teigland b, namelen,
136c04fecb4SDavid Teigland DLM_LU_RECOVER_DIR,
137c04fecb4SDavid Teigland &nodeid, &result);
138c04fecb4SDavid Teigland if (error) {
139c04fecb4SDavid Teigland log_error(ls, "recover_dir lookup %d",
140c04fecb4SDavid Teigland error);
141e7fd4179SDavid Teigland goto out_free;
142c04fecb4SDavid Teigland }
143e7fd4179SDavid Teigland
144c04fecb4SDavid Teigland /* The name was found in rsbtbl, but the
145c04fecb4SDavid Teigland * master nodeid is different from
146c04fecb4SDavid Teigland * memb->nodeid which says it is the master.
147c04fecb4SDavid Teigland * This should not happen. */
148c04fecb4SDavid Teigland
149c04fecb4SDavid Teigland if (result == DLM_LU_MATCH &&
150c04fecb4SDavid Teigland nodeid != memb->nodeid) {
151c04fecb4SDavid Teigland count_bad++;
152c04fecb4SDavid Teigland log_error(ls, "recover_dir lookup %d "
153c04fecb4SDavid Teigland "nodeid %d memb %d bad %u",
154c04fecb4SDavid Teigland result, nodeid, memb->nodeid,
155c04fecb4SDavid Teigland count_bad);
156c04fecb4SDavid Teigland print_hex_dump_bytes("dlm_recover_dir ",
157c04fecb4SDavid Teigland DUMP_PREFIX_NONE,
158c04fecb4SDavid Teigland b, namelen);
159c04fecb4SDavid Teigland }
160c04fecb4SDavid Teigland
161c04fecb4SDavid Teigland /* The name was found in rsbtbl, and the
162c04fecb4SDavid Teigland * master nodeid matches memb->nodeid. */
163c04fecb4SDavid Teigland
164c04fecb4SDavid Teigland if (result == DLM_LU_MATCH &&
165c04fecb4SDavid Teigland nodeid == memb->nodeid) {
166c04fecb4SDavid Teigland count_match++;
167c04fecb4SDavid Teigland }
168c04fecb4SDavid Teigland
169c04fecb4SDavid Teigland /* The name was not found in rsbtbl and was
170c04fecb4SDavid Teigland * added with memb->nodeid as the master. */
171c04fecb4SDavid Teigland
172c04fecb4SDavid Teigland if (result == DLM_LU_ADD) {
173c04fecb4SDavid Teigland count_add++;
174c04fecb4SDavid Teigland }
175c04fecb4SDavid Teigland
176e7fd4179SDavid Teigland last_len = namelen;
177e7fd4179SDavid Teigland memcpy(last_name, b, namelen);
178e7fd4179SDavid Teigland b += namelen;
179cd9df1aaSAl Viro left -= namelen;
180e7fd4179SDavid Teigland count++;
181e7fd4179SDavid Teigland }
182e7fd4179SDavid Teigland }
183e7fd4179SDavid Teigland done:
184e7fd4179SDavid Teigland ;
185e7fd4179SDavid Teigland }
186e7fd4179SDavid Teigland
187e7fd4179SDavid Teigland out_status:
188e7fd4179SDavid Teigland error = 0;
189c04fecb4SDavid Teigland dlm_set_recover_status(ls, DLM_RS_DIR);
190c04fecb4SDavid Teigland
191075f0177SDavid Teigland log_rinfo(ls, "dlm_recover_directory %u in %u new",
192c04fecb4SDavid Teigland count, count_add);
193e7fd4179SDavid Teigland out_free:
194e7fd4179SDavid Teigland kfree(last_name);
195e7fd4179SDavid Teigland out:
196e7fd4179SDavid Teigland return error;
197e7fd4179SDavid Teigland }
198e7fd4179SDavid Teigland
find_rsb_root(struct dlm_ls * ls,const char * name,int len)199*11519351SAlexander Aring static struct dlm_rsb *find_rsb_root(struct dlm_ls *ls, const char *name,
200*11519351SAlexander Aring int len)
20185f0379aSDavid Teigland {
20285f0379aSDavid Teigland struct dlm_rsb *r;
2037210cb7aSDavid Teigland uint32_t hash, bucket;
2047210cb7aSDavid Teigland int rv;
2057210cb7aSDavid Teigland
2067210cb7aSDavid Teigland hash = jhash(name, len, 0);
2077210cb7aSDavid Teigland bucket = hash & (ls->ls_rsbtbl_size - 1);
2087210cb7aSDavid Teigland
2097210cb7aSDavid Teigland spin_lock(&ls->ls_rsbtbl[bucket].lock);
210c04fecb4SDavid Teigland rv = dlm_search_rsb_tree(&ls->ls_rsbtbl[bucket].keep, name, len, &r);
2117210cb7aSDavid Teigland if (rv)
2127210cb7aSDavid Teigland rv = dlm_search_rsb_tree(&ls->ls_rsbtbl[bucket].toss,
213c04fecb4SDavid Teigland name, len, &r);
2147210cb7aSDavid Teigland spin_unlock(&ls->ls_rsbtbl[bucket].lock);
2157210cb7aSDavid Teigland
2167210cb7aSDavid Teigland if (!rv)
2177210cb7aSDavid Teigland return r;
21885f0379aSDavid Teigland
21985f0379aSDavid Teigland down_read(&ls->ls_root_sem);
22085f0379aSDavid Teigland list_for_each_entry(r, &ls->ls_root_list, res_root_list) {
22185f0379aSDavid Teigland if (len == r->res_length && !memcmp(name, r->res_name, len)) {
22285f0379aSDavid Teigland up_read(&ls->ls_root_sem);
223c04fecb4SDavid Teigland log_debug(ls, "find_rsb_root revert to root_list %s",
2247210cb7aSDavid Teigland r->res_name);
22585f0379aSDavid Teigland return r;
22685f0379aSDavid Teigland }
22785f0379aSDavid Teigland }
22885f0379aSDavid Teigland up_read(&ls->ls_root_sem);
22985f0379aSDavid Teigland return NULL;
23085f0379aSDavid Teigland }
23185f0379aSDavid Teigland
23285f0379aSDavid Teigland /* Find the rsb where we left off (or start again), then send rsb names
23385f0379aSDavid Teigland for rsb's we're master of and whose directory node matches the requesting
23485f0379aSDavid Teigland node. inbuf is the rsb name last sent, inlen is the name's length */
235e7fd4179SDavid Teigland
dlm_copy_master_names(struct dlm_ls * ls,const char * inbuf,int inlen,char * outbuf,int outlen,int nodeid)236*11519351SAlexander Aring void dlm_copy_master_names(struct dlm_ls *ls, const char *inbuf, int inlen,
237e7fd4179SDavid Teigland char *outbuf, int outlen, int nodeid)
238e7fd4179SDavid Teigland {
239e7fd4179SDavid Teigland struct list_head *list;
24085f0379aSDavid Teigland struct dlm_rsb *r;
24185f0379aSDavid Teigland int offset = 0, dir_nodeid;
242cd8e4679SHarvey Harrison __be16 be_namelen;
243e7fd4179SDavid Teigland
244e7fd4179SDavid Teigland down_read(&ls->ls_root_sem);
24585f0379aSDavid Teigland
24685f0379aSDavid Teigland if (inlen > 1) {
24785f0379aSDavid Teigland r = find_rsb_root(ls, inbuf, inlen);
24885f0379aSDavid Teigland if (!r) {
249561c67d8SAlexander Aring log_error(ls, "copy_master_names from %d start %d %.*s",
250561c67d8SAlexander Aring nodeid, inlen, inlen, inbuf);
25185f0379aSDavid Teigland goto out;
25285f0379aSDavid Teigland }
25385f0379aSDavid Teigland list = r->res_root_list.next;
25485f0379aSDavid Teigland } else {
255e7fd4179SDavid Teigland list = ls->ls_root_list.next;
25685f0379aSDavid Teigland }
257e7fd4179SDavid Teigland
258e7fd4179SDavid Teigland for (offset = 0; list != &ls->ls_root_list; list = list->next) {
259e7fd4179SDavid Teigland r = list_entry(list, struct dlm_rsb, res_root_list);
260e7fd4179SDavid Teigland if (r->res_nodeid)
261e7fd4179SDavid Teigland continue;
262e7fd4179SDavid Teigland
263e7fd4179SDavid Teigland dir_nodeid = dlm_dir_nodeid(r);
264e7fd4179SDavid Teigland if (dir_nodeid != nodeid)
265e7fd4179SDavid Teigland continue;
266e7fd4179SDavid Teigland
267e7fd4179SDavid Teigland /*
268e7fd4179SDavid Teigland * The block ends when we can't fit the following in the
269e7fd4179SDavid Teigland * remaining buffer space:
270e7fd4179SDavid Teigland * namelen (uint16_t) +
271e7fd4179SDavid Teigland * name (r->res_length) +
272e7fd4179SDavid Teigland * end-of-block record 0x0000 (uint16_t)
273e7fd4179SDavid Teigland */
274e7fd4179SDavid Teigland
275e7fd4179SDavid Teigland if (offset + sizeof(uint16_t)*2 + r->res_length > outlen) {
276e7fd4179SDavid Teigland /* Write end-of-block record */
277cd8e4679SHarvey Harrison be_namelen = cpu_to_be16(0);
278cd8e4679SHarvey Harrison memcpy(outbuf + offset, &be_namelen, sizeof(__be16));
279cd8e4679SHarvey Harrison offset += sizeof(__be16);
280c04fecb4SDavid Teigland ls->ls_recover_dir_sent_msg++;
281e7fd4179SDavid Teigland goto out;
282e7fd4179SDavid Teigland }
283e7fd4179SDavid Teigland
284e7fd4179SDavid Teigland be_namelen = cpu_to_be16(r->res_length);
285cd8e4679SHarvey Harrison memcpy(outbuf + offset, &be_namelen, sizeof(__be16));
286cd8e4679SHarvey Harrison offset += sizeof(__be16);
287e7fd4179SDavid Teigland memcpy(outbuf + offset, r->res_name, r->res_length);
288e7fd4179SDavid Teigland offset += r->res_length;
289c04fecb4SDavid Teigland ls->ls_recover_dir_sent_res++;
290e7fd4179SDavid Teigland }
291e7fd4179SDavid Teigland
292e7fd4179SDavid Teigland /*
293e7fd4179SDavid Teigland * If we've reached the end of the list (and there's room) write a
294e7fd4179SDavid Teigland * terminating record.
295e7fd4179SDavid Teigland */
296e7fd4179SDavid Teigland
297e7fd4179SDavid Teigland if ((list == &ls->ls_root_list) &&
298e7fd4179SDavid Teigland (offset + sizeof(uint16_t) <= outlen)) {
299cd8e4679SHarvey Harrison be_namelen = cpu_to_be16(0xFFFF);
300cd8e4679SHarvey Harrison memcpy(outbuf + offset, &be_namelen, sizeof(__be16));
301cd8e4679SHarvey Harrison offset += sizeof(__be16);
302c04fecb4SDavid Teigland ls->ls_recover_dir_sent_msg++;
303e7fd4179SDavid Teigland }
304e7fd4179SDavid Teigland out:
305e7fd4179SDavid Teigland up_read(&ls->ls_root_sem);
306e7fd4179SDavid Teigland }
307e7fd4179SDavid Teigland
308