xref: /openbmc/linux/fs/dlm/member.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12522fe45SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e7fd4179SDavid Teigland /******************************************************************************
3e7fd4179SDavid Teigland *******************************************************************************
4e7fd4179SDavid Teigland **
560f98d18SDavid Teigland **  Copyright (C) 2005-2011 Red Hat, Inc.  All rights reserved.
6e7fd4179SDavid Teigland **
7e7fd4179SDavid Teigland **
8e7fd4179SDavid Teigland *******************************************************************************
9e7fd4179SDavid Teigland ******************************************************************************/
10e7fd4179SDavid Teigland 
11e7fd4179SDavid Teigland #include "dlm_internal.h"
12e7fd4179SDavid Teigland #include "lockspace.h"
13e7fd4179SDavid Teigland #include "member.h"
14e7fd4179SDavid Teigland #include "recoverd.h"
15e7fd4179SDavid Teigland #include "recover.h"
16e7fd4179SDavid Teigland #include "rcom.h"
17e7fd4179SDavid Teigland #include "config.h"
18a070a91cSAlexander Aring #include "midcomms.h"
19391fbdc5SChristine Caulfield #include "lowcomms.h"
20e7fd4179SDavid Teigland 
dlm_slots_version(const struct dlm_header * h)21*11519351SAlexander Aring int dlm_slots_version(const struct dlm_header *h)
22757a4271SDavid Teigland {
233428785aSAlexander Aring 	if ((le32_to_cpu(h->h_version) & 0x0000FFFF) < DLM_HEADER_SLOTS)
24757a4271SDavid Teigland 		return 0;
25757a4271SDavid Teigland 	return 1;
26757a4271SDavid Teigland }
27757a4271SDavid Teigland 
dlm_slot_save(struct dlm_ls * ls,struct dlm_rcom * rc,struct dlm_member * memb)28757a4271SDavid Teigland void dlm_slot_save(struct dlm_ls *ls, struct dlm_rcom *rc,
29757a4271SDavid Teigland 		   struct dlm_member *memb)
30757a4271SDavid Teigland {
31757a4271SDavid Teigland 	struct rcom_config *rf = (struct rcom_config *)rc->rc_buf;
32757a4271SDavid Teigland 
33757a4271SDavid Teigland 	if (!dlm_slots_version(&rc->rc_header))
34757a4271SDavid Teigland 		return;
35757a4271SDavid Teigland 
36757a4271SDavid Teigland 	memb->slot = le16_to_cpu(rf->rf_our_slot);
37757a4271SDavid Teigland 	memb->generation = le32_to_cpu(rf->rf_generation);
38757a4271SDavid Teigland }
39757a4271SDavid Teigland 
dlm_slots_copy_out(struct dlm_ls * ls,struct dlm_rcom * rc)40757a4271SDavid Teigland void dlm_slots_copy_out(struct dlm_ls *ls, struct dlm_rcom *rc)
41757a4271SDavid Teigland {
42757a4271SDavid Teigland 	struct dlm_slot *slot;
43757a4271SDavid Teigland 	struct rcom_slot *ro;
44757a4271SDavid Teigland 	int i;
45757a4271SDavid Teigland 
46757a4271SDavid Teigland 	ro = (struct rcom_slot *)(rc->rc_buf + sizeof(struct rcom_config));
47757a4271SDavid Teigland 
48757a4271SDavid Teigland 	/* ls_slots array is sparse, but not rcom_slots */
49757a4271SDavid Teigland 
50757a4271SDavid Teigland 	for (i = 0; i < ls->ls_slots_size; i++) {
51757a4271SDavid Teigland 		slot = &ls->ls_slots[i];
52757a4271SDavid Teigland 		if (!slot->nodeid)
53757a4271SDavid Teigland 			continue;
54757a4271SDavid Teigland 		ro->ro_nodeid = cpu_to_le32(slot->nodeid);
55757a4271SDavid Teigland 		ro->ro_slot = cpu_to_le16(slot->slot);
56757a4271SDavid Teigland 		ro++;
57757a4271SDavid Teigland 	}
58757a4271SDavid Teigland }
59757a4271SDavid Teigland 
60757a4271SDavid Teigland #define SLOT_DEBUG_LINE 128
61757a4271SDavid Teigland 
log_slots(struct dlm_ls * ls,uint32_t gen,int num_slots,struct rcom_slot * ro0,struct dlm_slot * array,int array_size)62075f0177SDavid Teigland static void log_slots(struct dlm_ls *ls, uint32_t gen, int num_slots,
63757a4271SDavid Teigland 		      struct rcom_slot *ro0, struct dlm_slot *array,
64757a4271SDavid Teigland 		      int array_size)
65757a4271SDavid Teigland {
66757a4271SDavid Teigland 	char line[SLOT_DEBUG_LINE];
67757a4271SDavid Teigland 	int len = SLOT_DEBUG_LINE - 1;
68757a4271SDavid Teigland 	int pos = 0;
69757a4271SDavid Teigland 	int ret, i;
70757a4271SDavid Teigland 
71757a4271SDavid Teigland 	memset(line, 0, sizeof(line));
72757a4271SDavid Teigland 
73757a4271SDavid Teigland 	if (array) {
74757a4271SDavid Teigland 		for (i = 0; i < array_size; i++) {
75757a4271SDavid Teigland 			if (!array[i].nodeid)
76757a4271SDavid Teigland 				continue;
77757a4271SDavid Teigland 
78757a4271SDavid Teigland 			ret = snprintf(line + pos, len - pos, " %d:%d",
79757a4271SDavid Teigland 				       array[i].slot, array[i].nodeid);
80757a4271SDavid Teigland 			if (ret >= len - pos)
81757a4271SDavid Teigland 				break;
82757a4271SDavid Teigland 			pos += ret;
83757a4271SDavid Teigland 		}
84757a4271SDavid Teigland 	} else if (ro0) {
85757a4271SDavid Teigland 		for (i = 0; i < num_slots; i++) {
86757a4271SDavid Teigland 			ret = snprintf(line + pos, len - pos, " %d:%d",
87757a4271SDavid Teigland 				       ro0[i].ro_slot, ro0[i].ro_nodeid);
88757a4271SDavid Teigland 			if (ret >= len - pos)
89757a4271SDavid Teigland 				break;
90757a4271SDavid Teigland 			pos += ret;
91757a4271SDavid Teigland 		}
92757a4271SDavid Teigland 	}
93757a4271SDavid Teigland 
94075f0177SDavid Teigland 	log_rinfo(ls, "generation %u slots %d%s", gen, num_slots, line);
95757a4271SDavid Teigland }
96757a4271SDavid Teigland 
dlm_slots_copy_in(struct dlm_ls * ls)97757a4271SDavid Teigland int dlm_slots_copy_in(struct dlm_ls *ls)
98757a4271SDavid Teigland {
99757a4271SDavid Teigland 	struct dlm_member *memb;
100757a4271SDavid Teigland 	struct dlm_rcom *rc = ls->ls_recover_buf;
101757a4271SDavid Teigland 	struct rcom_config *rf = (struct rcom_config *)rc->rc_buf;
102757a4271SDavid Teigland 	struct rcom_slot *ro0, *ro;
103757a4271SDavid Teigland 	int our_nodeid = dlm_our_nodeid();
104757a4271SDavid Teigland 	int i, num_slots;
105757a4271SDavid Teigland 	uint32_t gen;
106757a4271SDavid Teigland 
107757a4271SDavid Teigland 	if (!dlm_slots_version(&rc->rc_header))
108757a4271SDavid Teigland 		return -1;
109757a4271SDavid Teigland 
110757a4271SDavid Teigland 	gen = le32_to_cpu(rf->rf_generation);
111757a4271SDavid Teigland 	if (gen <= ls->ls_generation) {
112757a4271SDavid Teigland 		log_error(ls, "dlm_slots_copy_in gen %u old %u",
113757a4271SDavid Teigland 			  gen, ls->ls_generation);
114757a4271SDavid Teigland 	}
115757a4271SDavid Teigland 	ls->ls_generation = gen;
116757a4271SDavid Teigland 
117757a4271SDavid Teigland 	num_slots = le16_to_cpu(rf->rf_num_slots);
118757a4271SDavid Teigland 	if (!num_slots)
119757a4271SDavid Teigland 		return -1;
120757a4271SDavid Teigland 
121757a4271SDavid Teigland 	ro0 = (struct rcom_slot *)(rc->rc_buf + sizeof(struct rcom_config));
122757a4271SDavid Teigland 
123075f0177SDavid Teigland 	log_slots(ls, gen, num_slots, ro0, NULL, 0);
124757a4271SDavid Teigland 
125757a4271SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
126757a4271SDavid Teigland 		for (i = 0, ro = ro0; i < num_slots; i++, ro++) {
1272f9dbedaSAlexander Aring 			if (le32_to_cpu(ro->ro_nodeid) != memb->nodeid)
128757a4271SDavid Teigland 				continue;
1292f9dbedaSAlexander Aring 			memb->slot = le16_to_cpu(ro->ro_slot);
130757a4271SDavid Teigland 			memb->slot_prev = memb->slot;
131757a4271SDavid Teigland 			break;
132757a4271SDavid Teigland 		}
133757a4271SDavid Teigland 
134757a4271SDavid Teigland 		if (memb->nodeid == our_nodeid) {
135757a4271SDavid Teigland 			if (ls->ls_slot && ls->ls_slot != memb->slot) {
136757a4271SDavid Teigland 				log_error(ls, "dlm_slots_copy_in our slot "
137757a4271SDavid Teigland 					  "changed %d %d", ls->ls_slot,
138757a4271SDavid Teigland 					  memb->slot);
139757a4271SDavid Teigland 				return -1;
140757a4271SDavid Teigland 			}
141757a4271SDavid Teigland 
142757a4271SDavid Teigland 			if (!ls->ls_slot)
143757a4271SDavid Teigland 				ls->ls_slot = memb->slot;
144757a4271SDavid Teigland 		}
145757a4271SDavid Teigland 
146757a4271SDavid Teigland 		if (!memb->slot) {
147757a4271SDavid Teigland 			log_error(ls, "dlm_slots_copy_in nodeid %d no slot",
148757a4271SDavid Teigland 				   memb->nodeid);
149757a4271SDavid Teigland 			return -1;
150757a4271SDavid Teigland 		}
151757a4271SDavid Teigland 	}
152757a4271SDavid Teigland 
153757a4271SDavid Teigland 	return 0;
154757a4271SDavid Teigland }
155757a4271SDavid Teigland 
156757a4271SDavid Teigland /* for any nodes that do not support slots, we will not have set memb->slot
157757a4271SDavid Teigland    in wait_status_all(), so memb->slot will remain -1, and we will not
158757a4271SDavid Teigland    assign slots or set ls_num_slots here */
159757a4271SDavid Teigland 
dlm_slots_assign(struct dlm_ls * ls,int * num_slots,int * slots_size,struct dlm_slot ** slots_out,uint32_t * gen_out)160757a4271SDavid Teigland int dlm_slots_assign(struct dlm_ls *ls, int *num_slots, int *slots_size,
161757a4271SDavid Teigland 		     struct dlm_slot **slots_out, uint32_t *gen_out)
162757a4271SDavid Teigland {
163757a4271SDavid Teigland 	struct dlm_member *memb;
164757a4271SDavid Teigland 	struct dlm_slot *array;
165757a4271SDavid Teigland 	int our_nodeid = dlm_our_nodeid();
166757a4271SDavid Teigland 	int array_size, max_slots, i;
167757a4271SDavid Teigland 	int need = 0;
168757a4271SDavid Teigland 	int max = 0;
169757a4271SDavid Teigland 	int num = 0;
170757a4271SDavid Teigland 	uint32_t gen = 0;
171757a4271SDavid Teigland 
172757a4271SDavid Teigland 	/* our own memb struct will have slot -1 gen 0 */
173757a4271SDavid Teigland 
174757a4271SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
175757a4271SDavid Teigland 		if (memb->nodeid == our_nodeid) {
176757a4271SDavid Teigland 			memb->slot = ls->ls_slot;
177757a4271SDavid Teigland 			memb->generation = ls->ls_generation;
178757a4271SDavid Teigland 			break;
179757a4271SDavid Teigland 		}
180757a4271SDavid Teigland 	}
181757a4271SDavid Teigland 
182757a4271SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
183757a4271SDavid Teigland 		if (memb->generation > gen)
184757a4271SDavid Teigland 			gen = memb->generation;
185757a4271SDavid Teigland 
186757a4271SDavid Teigland 		/* node doesn't support slots */
187757a4271SDavid Teigland 
188757a4271SDavid Teigland 		if (memb->slot == -1)
189757a4271SDavid Teigland 			return -1;
190757a4271SDavid Teigland 
191757a4271SDavid Teigland 		/* node needs a slot assigned */
192757a4271SDavid Teigland 
193757a4271SDavid Teigland 		if (!memb->slot)
194757a4271SDavid Teigland 			need++;
195757a4271SDavid Teigland 
196757a4271SDavid Teigland 		/* node has a slot assigned */
197757a4271SDavid Teigland 
198757a4271SDavid Teigland 		num++;
199757a4271SDavid Teigland 
200757a4271SDavid Teigland 		if (!max || max < memb->slot)
201757a4271SDavid Teigland 			max = memb->slot;
202757a4271SDavid Teigland 
203757a4271SDavid Teigland 		/* sanity check, once slot is assigned it shouldn't change */
204757a4271SDavid Teigland 
205757a4271SDavid Teigland 		if (memb->slot_prev && memb->slot && memb->slot_prev != memb->slot) {
206757a4271SDavid Teigland 			log_error(ls, "nodeid %d slot changed %d %d",
207757a4271SDavid Teigland 				  memb->nodeid, memb->slot_prev, memb->slot);
208757a4271SDavid Teigland 			return -1;
209757a4271SDavid Teigland 		}
210757a4271SDavid Teigland 		memb->slot_prev = memb->slot;
211757a4271SDavid Teigland 	}
212757a4271SDavid Teigland 
213757a4271SDavid Teigland 	array_size = max + need;
2142f48e061SMarkus Elfring 	array = kcalloc(array_size, sizeof(*array), GFP_NOFS);
215757a4271SDavid Teigland 	if (!array)
216757a4271SDavid Teigland 		return -ENOMEM;
217757a4271SDavid Teigland 
218757a4271SDavid Teigland 	num = 0;
219757a4271SDavid Teigland 
220757a4271SDavid Teigland 	/* fill in slots (offsets) that are used */
221757a4271SDavid Teigland 
222757a4271SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
223757a4271SDavid Teigland 		if (!memb->slot)
224757a4271SDavid Teigland 			continue;
225757a4271SDavid Teigland 
226757a4271SDavid Teigland 		if (memb->slot > array_size) {
227757a4271SDavid Teigland 			log_error(ls, "invalid slot number %d", memb->slot);
228757a4271SDavid Teigland 			kfree(array);
229757a4271SDavid Teigland 			return -1;
230757a4271SDavid Teigland 		}
231757a4271SDavid Teigland 
232757a4271SDavid Teigland 		array[memb->slot - 1].nodeid = memb->nodeid;
233757a4271SDavid Teigland 		array[memb->slot - 1].slot = memb->slot;
234757a4271SDavid Teigland 		num++;
235757a4271SDavid Teigland 	}
236757a4271SDavid Teigland 
237757a4271SDavid Teigland 	/* assign new slots from unused offsets */
238757a4271SDavid Teigland 
239757a4271SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
240757a4271SDavid Teigland 		if (memb->slot)
241757a4271SDavid Teigland 			continue;
242757a4271SDavid Teigland 
243757a4271SDavid Teigland 		for (i = 0; i < array_size; i++) {
244757a4271SDavid Teigland 			if (array[i].nodeid)
245757a4271SDavid Teigland 				continue;
246757a4271SDavid Teigland 
247757a4271SDavid Teigland 			memb->slot = i + 1;
248757a4271SDavid Teigland 			memb->slot_prev = memb->slot;
249757a4271SDavid Teigland 			array[i].nodeid = memb->nodeid;
250757a4271SDavid Teigland 			array[i].slot = memb->slot;
251757a4271SDavid Teigland 			num++;
252757a4271SDavid Teigland 
253757a4271SDavid Teigland 			if (!ls->ls_slot && memb->nodeid == our_nodeid)
254757a4271SDavid Teigland 				ls->ls_slot = memb->slot;
255757a4271SDavid Teigland 			break;
256757a4271SDavid Teigland 		}
257757a4271SDavid Teigland 
258757a4271SDavid Teigland 		if (!memb->slot) {
259757a4271SDavid Teigland 			log_error(ls, "no free slot found");
260757a4271SDavid Teigland 			kfree(array);
261757a4271SDavid Teigland 			return -1;
262757a4271SDavid Teigland 		}
263757a4271SDavid Teigland 	}
264757a4271SDavid Teigland 
265757a4271SDavid Teigland 	gen++;
266757a4271SDavid Teigland 
267075f0177SDavid Teigland 	log_slots(ls, gen, num, NULL, array, array_size);
268757a4271SDavid Teigland 
269d10a0b88SAlexander Aring 	max_slots = (DLM_MAX_APP_BUFSIZE - sizeof(struct dlm_rcom) -
270757a4271SDavid Teigland 		     sizeof(struct rcom_config)) / sizeof(struct rcom_slot);
271757a4271SDavid Teigland 
272757a4271SDavid Teigland 	if (num > max_slots) {
273757a4271SDavid Teigland 		log_error(ls, "num_slots %d exceeds max_slots %d",
274757a4271SDavid Teigland 			  num, max_slots);
275757a4271SDavid Teigland 		kfree(array);
276757a4271SDavid Teigland 		return -1;
277757a4271SDavid Teigland 	}
278757a4271SDavid Teigland 
279757a4271SDavid Teigland 	*gen_out = gen;
280757a4271SDavid Teigland 	*slots_out = array;
281757a4271SDavid Teigland 	*slots_size = array_size;
282757a4271SDavid Teigland 	*num_slots = num;
283757a4271SDavid Teigland 	return 0;
284757a4271SDavid Teigland }
285757a4271SDavid Teigland 
add_ordered_member(struct dlm_ls * ls,struct dlm_member * new)286e7fd4179SDavid Teigland static void add_ordered_member(struct dlm_ls *ls, struct dlm_member *new)
287e7fd4179SDavid Teigland {
288e7fd4179SDavid Teigland 	struct dlm_member *memb = NULL;
289e7fd4179SDavid Teigland 	struct list_head *tmp;
290e7fd4179SDavid Teigland 	struct list_head *newlist = &new->list;
291e7fd4179SDavid Teigland 	struct list_head *head = &ls->ls_nodes;
292e7fd4179SDavid Teigland 
293e7fd4179SDavid Teigland 	list_for_each(tmp, head) {
294e7fd4179SDavid Teigland 		memb = list_entry(tmp, struct dlm_member, list);
295e7fd4179SDavid Teigland 		if (new->nodeid < memb->nodeid)
296e7fd4179SDavid Teigland 			break;
297e7fd4179SDavid Teigland 	}
298e7fd4179SDavid Teigland 
299e7fd4179SDavid Teigland 	if (!memb)
300e7fd4179SDavid Teigland 		list_add_tail(newlist, head);
301e7fd4179SDavid Teigland 	else {
302e7fd4179SDavid Teigland 		/* FIXME: can use list macro here */
303e7fd4179SDavid Teigland 		newlist->prev = tmp->prev;
304e7fd4179SDavid Teigland 		newlist->next = tmp;
305e7fd4179SDavid Teigland 		tmp->prev->next = newlist;
306e7fd4179SDavid Teigland 		tmp->prev = newlist;
307e7fd4179SDavid Teigland 	}
308e7fd4179SDavid Teigland }
309e7fd4179SDavid Teigland 
add_remote_member(int nodeid)31007ee3867SAlexander Aring static int add_remote_member(int nodeid)
31107ee3867SAlexander Aring {
31207ee3867SAlexander Aring 	int error;
31307ee3867SAlexander Aring 
31407ee3867SAlexander Aring 	if (nodeid == dlm_our_nodeid())
31507ee3867SAlexander Aring 		return 0;
31607ee3867SAlexander Aring 
31707ee3867SAlexander Aring 	error = dlm_lowcomms_connect_node(nodeid);
31807ee3867SAlexander Aring 	if (error < 0)
31907ee3867SAlexander Aring 		return error;
32007ee3867SAlexander Aring 
32107ee3867SAlexander Aring 	dlm_midcomms_add_member(nodeid);
32207ee3867SAlexander Aring 	return 0;
32307ee3867SAlexander Aring }
32407ee3867SAlexander Aring 
dlm_add_member(struct dlm_ls * ls,struct dlm_config_node * node)32560f98d18SDavid Teigland static int dlm_add_member(struct dlm_ls *ls, struct dlm_config_node *node)
326e7fd4179SDavid Teigland {
327e7fd4179SDavid Teigland 	struct dlm_member *memb;
32860f98d18SDavid Teigland 	int error;
329e7fd4179SDavid Teigland 
330d12ad1a9SMarkus Elfring 	memb = kzalloc(sizeof(*memb), GFP_NOFS);
331e7fd4179SDavid Teigland 	if (!memb)
332e7fd4179SDavid Teigland 		return -ENOMEM;
333e7fd4179SDavid Teigland 
33407ee3867SAlexander Aring 	memb->nodeid = node->nodeid;
33507ee3867SAlexander Aring 	memb->weight = node->weight;
33607ee3867SAlexander Aring 	memb->comm_seq = node->comm_seq;
33707ee3867SAlexander Aring 
33807ee3867SAlexander Aring 	error = add_remote_member(node->nodeid);
339391fbdc5SChristine Caulfield 	if (error < 0) {
340391fbdc5SChristine Caulfield 		kfree(memb);
341391fbdc5SChristine Caulfield 		return error;
342391fbdc5SChristine Caulfield 	}
343391fbdc5SChristine Caulfield 
344e7fd4179SDavid Teigland 	add_ordered_member(ls, memb);
345e7fd4179SDavid Teigland 	ls->ls_num_nodes++;
346e7fd4179SDavid Teigland 	return 0;
347e7fd4179SDavid Teigland }
348e7fd4179SDavid Teigland 
find_memb(struct list_head * head,int nodeid)34960f98d18SDavid Teigland static struct dlm_member *find_memb(struct list_head *head, int nodeid)
350e7fd4179SDavid Teigland {
35160f98d18SDavid Teigland 	struct dlm_member *memb;
35260f98d18SDavid Teigland 
35360f98d18SDavid Teigland 	list_for_each_entry(memb, head, list) {
35460f98d18SDavid Teigland 		if (memb->nodeid == nodeid)
35560f98d18SDavid Teigland 			return memb;
35660f98d18SDavid Teigland 	}
35760f98d18SDavid Teigland 	return NULL;
358e7fd4179SDavid Teigland }
359e7fd4179SDavid Teigland 
dlm_is_member(struct dlm_ls * ls,int nodeid)36046b43eedSDavid Teigland int dlm_is_member(struct dlm_ls *ls, int nodeid)
361e7fd4179SDavid Teigland {
36260f98d18SDavid Teigland 	if (find_memb(&ls->ls_nodes, nodeid))
36390135925SDavid Teigland 		return 1;
36490135925SDavid Teigland 	return 0;
365e7fd4179SDavid Teigland }
366e7fd4179SDavid Teigland 
dlm_is_removed(struct dlm_ls * ls,int nodeid)367e7fd4179SDavid Teigland int dlm_is_removed(struct dlm_ls *ls, int nodeid)
368e7fd4179SDavid Teigland {
36960f98d18SDavid Teigland 	if (find_memb(&ls->ls_nodes_gone, nodeid))
37090135925SDavid Teigland 		return 1;
37190135925SDavid Teigland 	return 0;
372e7fd4179SDavid Teigland }
373e7fd4179SDavid Teigland 
clear_memb_list(struct list_head * head,void (* after_del)(int nodeid))374a070a91cSAlexander Aring static void clear_memb_list(struct list_head *head,
375a070a91cSAlexander Aring 			    void (*after_del)(int nodeid))
376e7fd4179SDavid Teigland {
377e7fd4179SDavid Teigland 	struct dlm_member *memb;
378e7fd4179SDavid Teigland 
379e7fd4179SDavid Teigland 	while (!list_empty(head)) {
380e7fd4179SDavid Teigland 		memb = list_entry(head->next, struct dlm_member, list);
381e7fd4179SDavid Teigland 		list_del(&memb->list);
382a070a91cSAlexander Aring 		if (after_del)
383a070a91cSAlexander Aring 			after_del(memb->nodeid);
384e7fd4179SDavid Teigland 		kfree(memb);
385e7fd4179SDavid Teigland 	}
386e7fd4179SDavid Teigland }
387e7fd4179SDavid Teigland 
remove_remote_member(int nodeid)38807ee3867SAlexander Aring static void remove_remote_member(int nodeid)
38907ee3867SAlexander Aring {
39007ee3867SAlexander Aring 	if (nodeid == dlm_our_nodeid())
39107ee3867SAlexander Aring 		return;
39207ee3867SAlexander Aring 
39307ee3867SAlexander Aring 	dlm_midcomms_remove_member(nodeid);
39407ee3867SAlexander Aring }
39507ee3867SAlexander Aring 
dlm_clear_members(struct dlm_ls * ls)396e7fd4179SDavid Teigland void dlm_clear_members(struct dlm_ls *ls)
397e7fd4179SDavid Teigland {
398c84c4733SAlexander Aring 	clear_memb_list(&ls->ls_nodes, remove_remote_member);
399e7fd4179SDavid Teigland 	ls->ls_num_nodes = 0;
400e7fd4179SDavid Teigland }
401e7fd4179SDavid Teigland 
dlm_clear_members_gone(struct dlm_ls * ls)402e7fd4179SDavid Teigland void dlm_clear_members_gone(struct dlm_ls *ls)
403e7fd4179SDavid Teigland {
404a070a91cSAlexander Aring 	clear_memb_list(&ls->ls_nodes_gone, NULL);
405e7fd4179SDavid Teigland }
406e7fd4179SDavid Teigland 
make_member_array(struct dlm_ls * ls)407e7fd4179SDavid Teigland static void make_member_array(struct dlm_ls *ls)
408e7fd4179SDavid Teigland {
409e7fd4179SDavid Teigland 	struct dlm_member *memb;
410e7fd4179SDavid Teigland 	int i, w, x = 0, total = 0, all_zero = 0, *array;
411e7fd4179SDavid Teigland 
412e7fd4179SDavid Teigland 	kfree(ls->ls_node_array);
413e7fd4179SDavid Teigland 	ls->ls_node_array = NULL;
414e7fd4179SDavid Teigland 
415e7fd4179SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
416e7fd4179SDavid Teigland 		if (memb->weight)
417e7fd4179SDavid Teigland 			total += memb->weight;
418e7fd4179SDavid Teigland 	}
419e7fd4179SDavid Teigland 
420e7fd4179SDavid Teigland 	/* all nodes revert to weight of 1 if all have weight 0 */
421e7fd4179SDavid Teigland 
422e7fd4179SDavid Teigland 	if (!total) {
423e7fd4179SDavid Teigland 		total = ls->ls_num_nodes;
424e7fd4179SDavid Teigland 		all_zero = 1;
425e7fd4179SDavid Teigland 	}
426e7fd4179SDavid Teigland 
427e7fd4179SDavid Teigland 	ls->ls_total_weight = total;
428790854beSMarkus Elfring 	array = kmalloc_array(total, sizeof(*array), GFP_NOFS);
429e7fd4179SDavid Teigland 	if (!array)
430e7fd4179SDavid Teigland 		return;
431e7fd4179SDavid Teigland 
432e7fd4179SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
433e7fd4179SDavid Teigland 		if (!all_zero && !memb->weight)
434e7fd4179SDavid Teigland 			continue;
435e7fd4179SDavid Teigland 
436e7fd4179SDavid Teigland 		if (all_zero)
437e7fd4179SDavid Teigland 			w = 1;
438e7fd4179SDavid Teigland 		else
439e7fd4179SDavid Teigland 			w = memb->weight;
440e7fd4179SDavid Teigland 
441e7fd4179SDavid Teigland 		DLM_ASSERT(x < total, printk("total %d x %d\n", total, x););
442e7fd4179SDavid Teigland 
443e7fd4179SDavid Teigland 		for (i = 0; i < w; i++)
444e7fd4179SDavid Teigland 			array[x++] = memb->nodeid;
445e7fd4179SDavid Teigland 	}
446e7fd4179SDavid Teigland 
447e7fd4179SDavid Teigland 	ls->ls_node_array = array;
448e7fd4179SDavid Teigland }
449e7fd4179SDavid Teigland 
450e7fd4179SDavid Teigland /* send a status request to all members just to establish comms connections */
451e7fd4179SDavid Teigland 
ping_members(struct dlm_ls * ls,uint64_t seq)452c4f4e135SAlexander Aring static int ping_members(struct dlm_ls *ls, uint64_t seq)
453e7fd4179SDavid Teigland {
454e7fd4179SDavid Teigland 	struct dlm_member *memb;
455f6db1b8eSDavid Teigland 	int error = 0;
456f6db1b8eSDavid Teigland 
457f6db1b8eSDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
458e10249b1SAlexander Aring 		if (dlm_recovery_stopped(ls)) {
459aee742c9SAlexander Aring 			error = -EINTR;
460f6db1b8eSDavid Teigland 			break;
461aee742c9SAlexander Aring 		}
462c4f4e135SAlexander Aring 		error = dlm_rcom_status(ls, memb->nodeid, 0, seq);
463f6db1b8eSDavid Teigland 		if (error)
464f6db1b8eSDavid Teigland 			break;
465f6db1b8eSDavid Teigland 	}
466f6db1b8eSDavid Teigland 	if (error)
467075f0177SDavid Teigland 		log_rinfo(ls, "ping_members aborted %d last nodeid %d",
468faa0f267SDavid Teigland 			  error, ls->ls_recover_nodeid);
469f6db1b8eSDavid Teigland 	return error;
470e7fd4179SDavid Teigland }
471e7fd4179SDavid Teigland 
dlm_lsop_recover_prep(struct dlm_ls * ls)47260f98d18SDavid Teigland static void dlm_lsop_recover_prep(struct dlm_ls *ls)
47360f98d18SDavid Teigland {
47460f98d18SDavid Teigland 	if (!ls->ls_ops || !ls->ls_ops->recover_prep)
47560f98d18SDavid Teigland 		return;
47660f98d18SDavid Teigland 	ls->ls_ops->recover_prep(ls->ls_ops_arg);
47760f98d18SDavid Teigland }
47860f98d18SDavid Teigland 
dlm_lsop_recover_slot(struct dlm_ls * ls,struct dlm_member * memb)47960f98d18SDavid Teigland static void dlm_lsop_recover_slot(struct dlm_ls *ls, struct dlm_member *memb)
48060f98d18SDavid Teigland {
48160f98d18SDavid Teigland 	struct dlm_slot slot;
48260f98d18SDavid Teigland 	uint32_t seq;
48360f98d18SDavid Teigland 	int error;
48460f98d18SDavid Teigland 
48560f98d18SDavid Teigland 	if (!ls->ls_ops || !ls->ls_ops->recover_slot)
48660f98d18SDavid Teigland 		return;
48760f98d18SDavid Teigland 
48860f98d18SDavid Teigland 	/* if there is no comms connection with this node
48960f98d18SDavid Teigland 	   or the present comms connection is newer
49060f98d18SDavid Teigland 	   than the one when this member was added, then
49160f98d18SDavid Teigland 	   we consider the node to have failed (versus
49260f98d18SDavid Teigland 	   being removed due to dlm_release_lockspace) */
49360f98d18SDavid Teigland 
49460f98d18SDavid Teigland 	error = dlm_comm_seq(memb->nodeid, &seq);
49560f98d18SDavid Teigland 
49660f98d18SDavid Teigland 	if (!error && seq == memb->comm_seq)
49760f98d18SDavid Teigland 		return;
49860f98d18SDavid Teigland 
49960f98d18SDavid Teigland 	slot.nodeid = memb->nodeid;
50060f98d18SDavid Teigland 	slot.slot = memb->slot;
50160f98d18SDavid Teigland 
50260f98d18SDavid Teigland 	ls->ls_ops->recover_slot(ls->ls_ops_arg, &slot);
50360f98d18SDavid Teigland }
50460f98d18SDavid Teigland 
dlm_lsop_recover_done(struct dlm_ls * ls)50560f98d18SDavid Teigland void dlm_lsop_recover_done(struct dlm_ls *ls)
50660f98d18SDavid Teigland {
50760f98d18SDavid Teigland 	struct dlm_member *memb;
50860f98d18SDavid Teigland 	struct dlm_slot *slots;
50960f98d18SDavid Teigland 	int i, num;
51060f98d18SDavid Teigland 
51160f98d18SDavid Teigland 	if (!ls->ls_ops || !ls->ls_ops->recover_done)
51260f98d18SDavid Teigland 		return;
51360f98d18SDavid Teigland 
51460f98d18SDavid Teigland 	num = ls->ls_num_nodes;
5152f48e061SMarkus Elfring 	slots = kcalloc(num, sizeof(*slots), GFP_KERNEL);
51660f98d18SDavid Teigland 	if (!slots)
51760f98d18SDavid Teigland 		return;
51860f98d18SDavid Teigland 
51960f98d18SDavid Teigland 	i = 0;
52060f98d18SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
52160f98d18SDavid Teigland 		if (i == num) {
52260f98d18SDavid Teigland 			log_error(ls, "dlm_lsop_recover_done bad num %d", num);
52360f98d18SDavid Teigland 			goto out;
52460f98d18SDavid Teigland 		}
52560f98d18SDavid Teigland 		slots[i].nodeid = memb->nodeid;
52660f98d18SDavid Teigland 		slots[i].slot = memb->slot;
52760f98d18SDavid Teigland 		i++;
52860f98d18SDavid Teigland 	}
52960f98d18SDavid Teigland 
53060f98d18SDavid Teigland 	ls->ls_ops->recover_done(ls->ls_ops_arg, slots, num,
53160f98d18SDavid Teigland 				 ls->ls_slot, ls->ls_generation);
53260f98d18SDavid Teigland  out:
53360f98d18SDavid Teigland 	kfree(slots);
53460f98d18SDavid Teigland }
53560f98d18SDavid Teigland 
find_config_node(struct dlm_recover * rv,int nodeid)53660f98d18SDavid Teigland static struct dlm_config_node *find_config_node(struct dlm_recover *rv,
53760f98d18SDavid Teigland 						int nodeid)
53860f98d18SDavid Teigland {
53960f98d18SDavid Teigland 	int i;
54060f98d18SDavid Teigland 
54160f98d18SDavid Teigland 	for (i = 0; i < rv->nodes_count; i++) {
54260f98d18SDavid Teigland 		if (rv->nodes[i].nodeid == nodeid)
54360f98d18SDavid Teigland 			return &rv->nodes[i];
54460f98d18SDavid Teigland 	}
54560f98d18SDavid Teigland 	return NULL;
54660f98d18SDavid Teigland }
54760f98d18SDavid Teigland 
dlm_recover_members(struct dlm_ls * ls,struct dlm_recover * rv,int * neg_out)548e7fd4179SDavid Teigland int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out)
549e7fd4179SDavid Teigland {
550e7fd4179SDavid Teigland 	struct dlm_member *memb, *safe;
55160f98d18SDavid Teigland 	struct dlm_config_node *node;
55260f98d18SDavid Teigland 	int i, error, neg = 0, low = -1;
553e7fd4179SDavid Teigland 
55491c0dc93SDavid Teigland 	/* previously removed members that we've not finished removing need to
555ca8031d9SAlexander Aring 	 * count as a negative change so the "neg" recovery steps will happen
556ca8031d9SAlexander Aring 	 *
557ca8031d9SAlexander Aring 	 * This functionality must report all member changes to lsops or
558ca8031d9SAlexander Aring 	 * midcomms layer and must never return before.
559ca8031d9SAlexander Aring 	 */
56091c0dc93SDavid Teigland 
56191c0dc93SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes_gone, list) {
562075f0177SDavid Teigland 		log_rinfo(ls, "prev removed member %d", memb->nodeid);
56391c0dc93SDavid Teigland 		neg++;
56491c0dc93SDavid Teigland 	}
56591c0dc93SDavid Teigland 
566e7fd4179SDavid Teigland 	/* move departed members from ls_nodes to ls_nodes_gone */
567e7fd4179SDavid Teigland 
568e7fd4179SDavid Teigland 	list_for_each_entry_safe(memb, safe, &ls->ls_nodes, list) {
56960f98d18SDavid Teigland 		node = find_config_node(rv, memb->nodeid);
57060f98d18SDavid Teigland 		if (node && !node->new)
571d44e0fc7SDavid Teigland 			continue;
572d44e0fc7SDavid Teigland 
57360f98d18SDavid Teigland 		if (!node) {
574075f0177SDavid Teigland 			log_rinfo(ls, "remove member %d", memb->nodeid);
57560f98d18SDavid Teigland 		} else {
57660f98d18SDavid Teigland 			/* removed and re-added */
577075f0177SDavid Teigland 			log_rinfo(ls, "remove member %d comm_seq %u %u",
57860f98d18SDavid Teigland 				  memb->nodeid, memb->comm_seq, node->comm_seq);
57960f98d18SDavid Teigland 		}
58060f98d18SDavid Teigland 
581d44e0fc7SDavid Teigland 		neg++;
58260f98d18SDavid Teigland 		list_move(&memb->list, &ls->ls_nodes_gone);
58307ee3867SAlexander Aring 		remove_remote_member(memb->nodeid);
58460f98d18SDavid Teigland 		ls->ls_num_nodes--;
58560f98d18SDavid Teigland 		dlm_lsop_recover_slot(ls, memb);
586d44e0fc7SDavid Teigland 	}
587d44e0fc7SDavid Teigland 
588e7fd4179SDavid Teigland 	/* add new members to ls_nodes */
589e7fd4179SDavid Teigland 
59060f98d18SDavid Teigland 	for (i = 0; i < rv->nodes_count; i++) {
59160f98d18SDavid Teigland 		node = &rv->nodes[i];
59260f98d18SDavid Teigland 		if (dlm_is_member(ls, node->nodeid))
593e7fd4179SDavid Teigland 			continue;
5949c693d76SAlexander Aring 		error = dlm_add_member(ls, node);
5959c693d76SAlexander Aring 		if (error)
5969c693d76SAlexander Aring 			return error;
5979c693d76SAlexander Aring 
598075f0177SDavid Teigland 		log_rinfo(ls, "add member %d", node->nodeid);
599e7fd4179SDavid Teigland 	}
600e7fd4179SDavid Teigland 
601e7fd4179SDavid Teigland 	list_for_each_entry(memb, &ls->ls_nodes, list) {
602e7fd4179SDavid Teigland 		if (low == -1 || memb->nodeid < low)
603e7fd4179SDavid Teigland 			low = memb->nodeid;
604e7fd4179SDavid Teigland 	}
605e7fd4179SDavid Teigland 	ls->ls_low_nodeid = low;
606e7fd4179SDavid Teigland 
607e7fd4179SDavid Teigland 	make_member_array(ls);
608e7fd4179SDavid Teigland 	*neg_out = neg;
609e7fd4179SDavid Teigland 
610c4f4e135SAlexander Aring 	error = ping_members(ls, rv->seq);
611075f0177SDavid Teigland 	log_rinfo(ls, "dlm_recover_members %d nodes", ls->ls_num_nodes);
612e7fd4179SDavid Teigland 	return error;
613e7fd4179SDavid Teigland }
614e7fd4179SDavid Teigland 
615c36258b5SDavid Teigland /* Userspace guarantees that dlm_ls_stop() has completed on all nodes before
616c36258b5SDavid Teigland    dlm_ls_start() is called on any of them to start the new recovery. */
617e7fd4179SDavid Teigland 
dlm_ls_stop(struct dlm_ls * ls)618e7fd4179SDavid Teigland int dlm_ls_stop(struct dlm_ls *ls)
619e7fd4179SDavid Teigland {
620e7fd4179SDavid Teigland 	int new;
621e7fd4179SDavid Teigland 
622e7fd4179SDavid Teigland 	/*
623c36258b5SDavid Teigland 	 * Prevent dlm_recv from being in the middle of something when we do
624c36258b5SDavid Teigland 	 * the stop.  This includes ensuring dlm_recv isn't processing a
625c36258b5SDavid Teigland 	 * recovery message (rcom), while dlm_recoverd is aborting and
626c36258b5SDavid Teigland 	 * resetting things from an in-progress recovery.  i.e. we want
627c36258b5SDavid Teigland 	 * dlm_recoverd to abort its recovery without worrying about dlm_recv
628c36258b5SDavid Teigland 	 * processing an rcom at the same time.  Stopping dlm_recv also makes
629c36258b5SDavid Teigland 	 * it easy for dlm_receive_message() to check locking stopped and add a
630c36258b5SDavid Teigland 	 * message to the requestqueue without races.
631c36258b5SDavid Teigland 	 */
632c36258b5SDavid Teigland 
633c36258b5SDavid Teigland 	down_write(&ls->ls_recv_active);
634c36258b5SDavid Teigland 
635c36258b5SDavid Teigland 	/*
636475f230cSDavid Teigland 	 * Abort any recovery that's in progress (see RECOVER_STOP,
637c36258b5SDavid Teigland 	 * dlm_recovery_stopped()) and tell any other threads running in the
638c36258b5SDavid Teigland 	 * dlm to quit any processing (see RUNNING, dlm_locking_stopped()).
639e7fd4179SDavid Teigland 	 */
640e7fd4179SDavid Teigland 
641e7fd4179SDavid Teigland 	spin_lock(&ls->ls_recover_lock);
642475f230cSDavid Teigland 	set_bit(LSFL_RECOVER_STOP, &ls->ls_flags);
643e7fd4179SDavid Teigland 	new = test_and_clear_bit(LSFL_RUNNING, &ls->ls_flags);
644e7fd4179SDavid Teigland 	ls->ls_recover_seq++;
645e7fd4179SDavid Teigland 	spin_unlock(&ls->ls_recover_lock);
646e7fd4179SDavid Teigland 
647e7fd4179SDavid Teigland 	/*
648c36258b5SDavid Teigland 	 * Let dlm_recv run again, now any normal messages will be saved on the
649c36258b5SDavid Teigland 	 * requestqueue for later.
650c36258b5SDavid Teigland 	 */
651c36258b5SDavid Teigland 
652c36258b5SDavid Teigland 	up_write(&ls->ls_recv_active);
653c36258b5SDavid Teigland 
654c36258b5SDavid Teigland 	/*
655e7fd4179SDavid Teigland 	 * This in_recovery lock does two things:
656e7fd4179SDavid Teigland 	 * 1) Keeps this function from returning until all threads are out
657c41b20e7SAdam Buchbinder 	 *    of locking routines and locking is truly stopped.
658e7fd4179SDavid Teigland 	 * 2) Keeps any new requests from being processed until it's unlocked
659e7fd4179SDavid Teigland 	 *    when recovery is complete.
660e7fd4179SDavid Teigland 	 */
661e7fd4179SDavid Teigland 
662475f230cSDavid Teigland 	if (new) {
663475f230cSDavid Teigland 		set_bit(LSFL_RECOVER_DOWN, &ls->ls_flags);
664475f230cSDavid Teigland 		wake_up_process(ls->ls_recoverd_task);
665475f230cSDavid Teigland 		wait_event(ls->ls_recover_lock_wait,
666475f230cSDavid Teigland 			   test_bit(LSFL_RECOVER_LOCK, &ls->ls_flags));
667475f230cSDavid Teigland 	}
668e7fd4179SDavid Teigland 
669e7fd4179SDavid Teigland 	/*
670e7fd4179SDavid Teigland 	 * The recoverd suspend/resume makes sure that dlm_recoverd (if
671475f230cSDavid Teigland 	 * running) has noticed RECOVER_STOP above and quit processing the
672c36258b5SDavid Teigland 	 * previous recovery.
673e7fd4179SDavid Teigland 	 */
674e7fd4179SDavid Teigland 
675e7fd4179SDavid Teigland 	dlm_recoverd_suspend(ls);
676757a4271SDavid Teigland 
677757a4271SDavid Teigland 	spin_lock(&ls->ls_recover_lock);
678757a4271SDavid Teigland 	kfree(ls->ls_slots);
679757a4271SDavid Teigland 	ls->ls_slots = NULL;
680757a4271SDavid Teigland 	ls->ls_num_slots = 0;
681757a4271SDavid Teigland 	ls->ls_slots_size = 0;
682e7fd4179SDavid Teigland 	ls->ls_recover_status = 0;
683757a4271SDavid Teigland 	spin_unlock(&ls->ls_recover_lock);
684757a4271SDavid Teigland 
685e7fd4179SDavid Teigland 	dlm_recoverd_resume(ls);
6863ae1acf9SDavid Teigland 
6873ae1acf9SDavid Teigland 	if (!ls->ls_recover_begin)
6883ae1acf9SDavid Teigland 		ls->ls_recover_begin = jiffies;
68960f98d18SDavid Teigland 
6907e09b15cSAlexander Aring 	/* call recover_prep ops only once and not multiple times
6917e09b15cSAlexander Aring 	 * for each possible dlm_ls_stop() when recovery is already
6927e09b15cSAlexander Aring 	 * stopped.
6937e09b15cSAlexander Aring 	 *
6947e09b15cSAlexander Aring 	 * If we successful was able to clear LSFL_RUNNING bit and
6957e09b15cSAlexander Aring 	 * it was set we know it is the first dlm_ls_stop() call.
6967e09b15cSAlexander Aring 	 */
6977e09b15cSAlexander Aring 	if (new)
69860f98d18SDavid Teigland 		dlm_lsop_recover_prep(ls);
6997e09b15cSAlexander Aring 
700e7fd4179SDavid Teigland 	return 0;
701e7fd4179SDavid Teigland }
702e7fd4179SDavid Teigland 
dlm_ls_start(struct dlm_ls * ls)703e7fd4179SDavid Teigland int dlm_ls_start(struct dlm_ls *ls)
704e7fd4179SDavid Teigland {
7052ab93ae1SMarkus Elfring 	struct dlm_recover *rv, *rv_old;
70658a923adSDenis V. Lunev 	struct dlm_config_node *nodes = NULL;
70760f98d18SDavid Teigland 	int error, count;
708e7fd4179SDavid Teigland 
709d12ad1a9SMarkus Elfring 	rv = kzalloc(sizeof(*rv), GFP_NOFS);
710e7fd4179SDavid Teigland 	if (!rv)
711e7fd4179SDavid Teigland 		return -ENOMEM;
712e7fd4179SDavid Teigland 
71360f98d18SDavid Teigland 	error = dlm_config_nodes(ls->ls_name, &nodes, &count);
714d44e0fc7SDavid Teigland 	if (error < 0)
715d968b4e2STycho Andersen 		goto fail_rv;
716e7fd4179SDavid Teigland 
717e7fd4179SDavid Teigland 	spin_lock(&ls->ls_recover_lock);
718e7fd4179SDavid Teigland 
719e7fd4179SDavid Teigland 	/* the lockspace needs to be stopped before it can be started */
720e7fd4179SDavid Teigland 
721e7fd4179SDavid Teigland 	if (!dlm_locking_stopped(ls)) {
722e7fd4179SDavid Teigland 		spin_unlock(&ls->ls_recover_lock);
723e7fd4179SDavid Teigland 		log_error(ls, "start ignored: lockspace running");
724e7fd4179SDavid Teigland 		error = -EINVAL;
725e7fd4179SDavid Teigland 		goto fail;
726e7fd4179SDavid Teigland 	}
727e7fd4179SDavid Teigland 
72860f98d18SDavid Teigland 	rv->nodes = nodes;
72960f98d18SDavid Teigland 	rv->nodes_count = count;
730e7fd4179SDavid Teigland 	rv->seq = ++ls->ls_recover_seq;
731e7fd4179SDavid Teigland 	rv_old = ls->ls_recover_args;
732e7fd4179SDavid Teigland 	ls->ls_recover_args = rv;
733e7fd4179SDavid Teigland 	spin_unlock(&ls->ls_recover_lock);
734e7fd4179SDavid Teigland 
735e7fd4179SDavid Teigland 	if (rv_old) {
736d44e0fc7SDavid Teigland 		log_error(ls, "unused recovery %llx %d",
73760f98d18SDavid Teigland 			  (unsigned long long)rv_old->seq, rv_old->nodes_count);
73860f98d18SDavid Teigland 		kfree(rv_old->nodes);
739e7fd4179SDavid Teigland 		kfree(rv_old);
740e7fd4179SDavid Teigland 	}
741e7fd4179SDavid Teigland 
742475f230cSDavid Teigland 	set_bit(LSFL_RECOVER_WORK, &ls->ls_flags);
743475f230cSDavid Teigland 	wake_up_process(ls->ls_recoverd_task);
744e7fd4179SDavid Teigland 	return 0;
745e7fd4179SDavid Teigland 
746e7fd4179SDavid Teigland  fail:
74760f98d18SDavid Teigland 	kfree(nodes);
748d968b4e2STycho Andersen  fail_rv:
749d968b4e2STycho Andersen 	kfree(rv);
750e7fd4179SDavid Teigland 	return error;
751e7fd4179SDavid Teigland }
752e7fd4179SDavid Teigland 
753