Blame view

fs/btrfs/dev-replace.c 29.2 KB
e93c89c1a   Stefan Behrens   Btrfs: add new so...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /*
   * Copyright (C) STRATO AG 2012.  All rights reserved.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public
   * License v2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public
   * License along with this program; if not, write to the
   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   * Boston, MA 021110-1307, USA.
   */
  #include <linux/sched.h>
  #include <linux/bio.h>
  #include <linux/slab.h>
  #include <linux/buffer_head.h>
  #include <linux/blkdev.h>
  #include <linux/random.h>
  #include <linux/iocontext.h>
  #include <linux/capability.h>
  #include <linux/kthread.h>
  #include <linux/math64.h>
  #include <asm/div64.h>
e93c89c1a   Stefan Behrens   Btrfs: add new so...
29
30
31
32
33
34
35
36
37
38
  #include "ctree.h"
  #include "extent_map.h"
  #include "disk-io.h"
  #include "transaction.h"
  #include "print-tree.h"
  #include "volumes.h"
  #include "async-thread.h"
  #include "check-integrity.h"
  #include "rcu-string.h"
  #include "dev-replace.h"
49c6f736f   Anand Jain   btrfs: dev replac...
39
  #include "sysfs.h"
e93c89c1a   Stefan Behrens   Btrfs: add new so...
40

e93c89c1a   Stefan Behrens   Btrfs: add new so...
41
42
43
44
45
46
  static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
  				       int scrub_ret);
  static void btrfs_dev_replace_update_device_in_mapping_tree(
  						struct btrfs_fs_info *fs_info,
  						struct btrfs_device *srcdev,
  						struct btrfs_device *tgtdev);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info);
  static int btrfs_dev_replace_kthread(void *data);
  static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info);
  
  
  int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info)
  {
  	struct btrfs_key key;
  	struct btrfs_root *dev_root = fs_info->dev_root;
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  	struct extent_buffer *eb;
  	int slot;
  	int ret = 0;
  	struct btrfs_path *path = NULL;
  	int item_size;
  	struct btrfs_dev_replace_item *ptr;
  	u64 src_devid;
  
  	path = btrfs_alloc_path();
  	if (!path) {
  		ret = -ENOMEM;
  		goto out;
  	}
  
  	key.objectid = 0;
  	key.type = BTRFS_DEV_REPLACE_KEY;
  	key.offset = 0;
  	ret = btrfs_search_slot(NULL, dev_root, &key, path, 0, 0);
  	if (ret) {
  no_valid_dev_replace_entry_found:
  		ret = 0;
  		dev_replace->replace_state =
  			BTRFS_DEV_REPLACE_ITEM_STATE_NEVER_STARTED;
  		dev_replace->cont_reading_from_srcdev_mode =
  		    BTRFS_DEV_REPLACE_ITEM_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
  		dev_replace->replace_state = 0;
  		dev_replace->time_started = 0;
  		dev_replace->time_stopped = 0;
  		atomic64_set(&dev_replace->num_write_errors, 0);
  		atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0);
  		dev_replace->cursor_left = 0;
  		dev_replace->committed_cursor_left = 0;
  		dev_replace->cursor_left_last_write_of_item = 0;
  		dev_replace->cursor_right = 0;
  		dev_replace->srcdev = NULL;
  		dev_replace->tgtdev = NULL;
  		dev_replace->is_valid = 0;
  		dev_replace->item_needs_writeback = 0;
  		goto out;
  	}
  	slot = path->slots[0];
  	eb = path->nodes[0];
  	item_size = btrfs_item_size_nr(eb, slot);
  	ptr = btrfs_item_ptr(eb, slot, struct btrfs_dev_replace_item);
  
  	if (item_size != sizeof(struct btrfs_dev_replace_item)) {
efe120a06   Frank Holton   Btrfs: convert pr...
103
104
  		btrfs_warn(fs_info,
  			"dev_replace entry found has unexpected size, ignore entry");
e93c89c1a   Stefan Behrens   Btrfs: add new so...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  		goto no_valid_dev_replace_entry_found;
  	}
  
  	src_devid = btrfs_dev_replace_src_devid(eb, ptr);
  	dev_replace->cont_reading_from_srcdev_mode =
  		btrfs_dev_replace_cont_reading_from_srcdev_mode(eb, ptr);
  	dev_replace->replace_state = btrfs_dev_replace_replace_state(eb, ptr);
  	dev_replace->time_started = btrfs_dev_replace_time_started(eb, ptr);
  	dev_replace->time_stopped =
  		btrfs_dev_replace_time_stopped(eb, ptr);
  	atomic64_set(&dev_replace->num_write_errors,
  		     btrfs_dev_replace_num_write_errors(eb, ptr));
  	atomic64_set(&dev_replace->num_uncorrectable_read_errors,
  		     btrfs_dev_replace_num_uncorrectable_read_errors(eb, ptr));
  	dev_replace->cursor_left = btrfs_dev_replace_cursor_left(eb, ptr);
  	dev_replace->committed_cursor_left = dev_replace->cursor_left;
  	dev_replace->cursor_left_last_write_of_item = dev_replace->cursor_left;
  	dev_replace->cursor_right = btrfs_dev_replace_cursor_right(eb, ptr);
  	dev_replace->is_valid = 1;
  
  	dev_replace->item_needs_writeback = 0;
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
  		dev_replace->srcdev = NULL;
  		dev_replace->tgtdev = NULL;
  		break;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
  		dev_replace->srcdev = btrfs_find_device(fs_info, src_devid,
  							NULL, NULL);
  		dev_replace->tgtdev = btrfs_find_device(fs_info,
  							BTRFS_DEV_REPLACE_DEVID,
  							NULL, NULL);
  		/*
  		 * allow 'btrfs dev replace_cancel' if src/tgt device is
  		 * missing
  		 */
  		if (!dev_replace->srcdev &&
3cdde2240   Jeff Mahoney   btrfs: btrfs_test...
145
  		    !btrfs_test_opt(dev_root->fs_info, DEGRADED)) {
e93c89c1a   Stefan Behrens   Btrfs: add new so...
146
  			ret = -EIO;
efe120a06   Frank Holton   Btrfs: convert pr...
147
148
149
150
151
  			btrfs_warn(fs_info,
  			   "cannot mount because device replace operation is ongoing and");
  			btrfs_warn(fs_info,
  			   "srcdev (devid %llu) is missing, need to run 'btrfs dev scan'?",
  			   src_devid);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
152
153
  		}
  		if (!dev_replace->tgtdev &&
3cdde2240   Jeff Mahoney   btrfs: btrfs_test...
154
  		    !btrfs_test_opt(dev_root->fs_info, DEGRADED)) {
e93c89c1a   Stefan Behrens   Btrfs: add new so...
155
  			ret = -EIO;
efe120a06   Frank Holton   Btrfs: convert pr...
156
157
158
159
  			btrfs_warn(fs_info,
  			   "cannot mount because device replace operation is ongoing and");
  			btrfs_warn(fs_info,
  			   "tgtdev (devid %llu) is missing, need to run 'btrfs dev scan'?",
6e71c47af   Geert Uytterhoeven   Btrfs: Make BTRFS...
160
  				BTRFS_DEV_REPLACE_DEVID);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
161
162
163
164
165
166
167
  		}
  		if (dev_replace->tgtdev) {
  			if (dev_replace->srcdev) {
  				dev_replace->tgtdev->total_bytes =
  					dev_replace->srcdev->total_bytes;
  				dev_replace->tgtdev->disk_total_bytes =
  					dev_replace->srcdev->disk_total_bytes;
935e5cc93   Miao Xie   Btrfs: fix wrong ...
168
169
  				dev_replace->tgtdev->commit_total_bytes =
  					dev_replace->srcdev->commit_total_bytes;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
170
171
  				dev_replace->tgtdev->bytes_used =
  					dev_replace->srcdev->bytes_used;
ce7213c70   Miao Xie   Btrfs: fix wrong ...
172
173
  				dev_replace->tgtdev->commit_bytes_used =
  					dev_replace->srcdev->commit_bytes_used;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
174
175
176
177
178
179
180
181
182
  			}
  			dev_replace->tgtdev->is_tgtdev_for_dev_replace = 1;
  			btrfs_init_dev_replace_tgtdev_for_resume(fs_info,
  				dev_replace->tgtdev);
  		}
  		break;
  	}
  
  out:
527afb449   Tsutomu Itoh   Btrfs: cleanup: r...
183
  	btrfs_free_path(path);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  	return ret;
  }
  
  /*
   * called from commit_transaction. Writes changed device replace state to
   * disk.
   */
  int btrfs_run_dev_replace(struct btrfs_trans_handle *trans,
  			  struct btrfs_fs_info *fs_info)
  {
  	int ret;
  	struct btrfs_root *dev_root = fs_info->dev_root;
  	struct btrfs_path *path;
  	struct btrfs_key key;
  	struct extent_buffer *eb;
  	struct btrfs_dev_replace_item *ptr;
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
73beece9c   Liu Bo   Btrfs: fix lockde...
201
  	btrfs_dev_replace_lock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
202
203
  	if (!dev_replace->is_valid ||
  	    !dev_replace->item_needs_writeback) {
73beece9c   Liu Bo   Btrfs: fix lockde...
204
  		btrfs_dev_replace_unlock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
205
206
  		return 0;
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
207
  	btrfs_dev_replace_unlock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
208
209
210
211
212
213
214
215
216
217
218
219
  
  	key.objectid = 0;
  	key.type = BTRFS_DEV_REPLACE_KEY;
  	key.offset = 0;
  
  	path = btrfs_alloc_path();
  	if (!path) {
  		ret = -ENOMEM;
  		goto out;
  	}
  	ret = btrfs_search_slot(trans, dev_root, &key, path, -1, 1);
  	if (ret < 0) {
5d163e0e6   Jeff Mahoney   btrfs: unsplit pr...
220
221
222
  		btrfs_warn(fs_info,
  			   "error %d while searching for dev_replace item!",
  			   ret);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  		goto out;
  	}
  
  	if (ret == 0 &&
  	    btrfs_item_size_nr(path->nodes[0], path->slots[0]) < sizeof(*ptr)) {
  		/*
  		 * need to delete old one and insert a new one.
  		 * Since no attempt is made to recover any old state, if the
  		 * dev_replace state is 'running', the data on the target
  		 * drive is lost.
  		 * It would be possible to recover the state: just make sure
  		 * that the beginning of the item is never changed and always
  		 * contains all the essential information. Then read this
  		 * minimal set of information and use it as a base for the
  		 * new state.
  		 */
  		ret = btrfs_del_item(trans, dev_root, path);
  		if (ret != 0) {
5d163e0e6   Jeff Mahoney   btrfs: unsplit pr...
241
242
243
  			btrfs_warn(fs_info,
  				   "delete too small dev_replace item failed %d!",
  				   ret);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
244
245
246
247
248
249
250
251
252
253
254
  			goto out;
  		}
  		ret = 1;
  	}
  
  	if (ret == 1) {
  		/* need to insert a new item */
  		btrfs_release_path(path);
  		ret = btrfs_insert_empty_item(trans, dev_root, path,
  					      &key, sizeof(*ptr));
  		if (ret < 0) {
5d163e0e6   Jeff Mahoney   btrfs: unsplit pr...
255
256
  			btrfs_warn(fs_info,
  				   "insert dev_replace item failed %d!", ret);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
257
258
259
260
261
262
263
  			goto out;
  		}
  	}
  
  	eb = path->nodes[0];
  	ptr = btrfs_item_ptr(eb, path->slots[0],
  			     struct btrfs_dev_replace_item);
73beece9c   Liu Bo   Btrfs: fix lockde...
264
  	btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
  	if (dev_replace->srcdev)
  		btrfs_set_dev_replace_src_devid(eb, ptr,
  			dev_replace->srcdev->devid);
  	else
  		btrfs_set_dev_replace_src_devid(eb, ptr, (u64)-1);
  	btrfs_set_dev_replace_cont_reading_from_srcdev_mode(eb, ptr,
  		dev_replace->cont_reading_from_srcdev_mode);
  	btrfs_set_dev_replace_replace_state(eb, ptr,
  		dev_replace->replace_state);
  	btrfs_set_dev_replace_time_started(eb, ptr, dev_replace->time_started);
  	btrfs_set_dev_replace_time_stopped(eb, ptr, dev_replace->time_stopped);
  	btrfs_set_dev_replace_num_write_errors(eb, ptr,
  		atomic64_read(&dev_replace->num_write_errors));
  	btrfs_set_dev_replace_num_uncorrectable_read_errors(eb, ptr,
  		atomic64_read(&dev_replace->num_uncorrectable_read_errors));
  	dev_replace->cursor_left_last_write_of_item =
  		dev_replace->cursor_left;
  	btrfs_set_dev_replace_cursor_left(eb, ptr,
  		dev_replace->cursor_left_last_write_of_item);
  	btrfs_set_dev_replace_cursor_right(eb, ptr,
  		dev_replace->cursor_right);
  	dev_replace->item_needs_writeback = 0;
73beece9c   Liu Bo   Btrfs: fix lockde...
287
  	btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  
  	btrfs_mark_buffer_dirty(eb);
  
  out:
  	btrfs_free_path(path);
  
  	return ret;
  }
  
  void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info)
  {
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  
  	dev_replace->committed_cursor_left =
  		dev_replace->cursor_left_last_write_of_item;
  }
b5255456c   Anand Jain   btrfs: refactor b...
304
305
  int btrfs_dev_replace_start(struct btrfs_root *root, char *tgtdev_name,
  				u64 srcdevid, char *srcdev_name, int read_src)
e93c89c1a   Stefan Behrens   Btrfs: add new so...
306
307
308
309
310
311
312
  {
  	struct btrfs_trans_handle *trans;
  	struct btrfs_fs_info *fs_info = root->fs_info;
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  	int ret;
  	struct btrfs_device *tgt_device = NULL;
  	struct btrfs_device *src_device = NULL;
935e5cc93   Miao Xie   Btrfs: fix wrong ...
313
  	/* the disk copy procedure reuses the scrub code */
e93c89c1a   Stefan Behrens   Btrfs: add new so...
314
  	mutex_lock(&fs_info->volume_mutex);
b5255456c   Anand Jain   btrfs: refactor b...
315
316
  	ret = btrfs_find_device_by_devspec(root, srcdevid,
  					    srcdev_name, &src_device);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
317
  	if (ret) {
1c43366d3   Miao Xie   Btrfs: fix unprot...
318
319
  		mutex_unlock(&fs_info->volume_mutex);
  		return ret;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
320
  	}
b5255456c   Anand Jain   btrfs: refactor b...
321
  	ret = btrfs_init_dev_replace_tgtdev(root, tgtdev_name,
1c43366d3   Miao Xie   Btrfs: fix unprot...
322
323
324
325
  					    src_device, &tgt_device);
  	mutex_unlock(&fs_info->volume_mutex);
  	if (ret)
  		return ret;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
326

9e271ae27   Anand Jain   Btrfs: kernel ope...
327
328
329
330
331
332
333
334
335
336
337
338
  	/*
  	 * Here we commit the transaction to make sure commit_total_bytes
  	 * of all the devices are updated.
  	 */
  	trans = btrfs_attach_transaction(root);
  	if (!IS_ERR(trans)) {
  		ret = btrfs_commit_transaction(trans, root);
  		if (ret)
  			return ret;
  	} else if (PTR_ERR(trans) != -ENOENT) {
  		return PTR_ERR(trans);
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
339
  	btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
340
341
342
343
344
345
346
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
  		break;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
b5255456c   Anand Jain   btrfs: refactor b...
347
  		ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
348
349
  		goto leave;
  	}
b5255456c   Anand Jain   btrfs: refactor b...
350
  	dev_replace->cont_reading_from_srcdev_mode = read_src;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
351
352
353
354
  	WARN_ON(!src_device);
  	dev_replace->srcdev = src_device;
  	WARN_ON(!tgt_device);
  	dev_replace->tgtdev = tgt_device;
fc23c246d   Anand Jain   btrfs: use fs_inf...
355
  	btrfs_info_in_rcu(fs_info,
ecaeb14b9   David Sterba   btrfs: switch mes...
356
  		      "dev_replace from %s (devid %llu) to %s started",
e93c89c1a   Stefan Behrens   Btrfs: add new so...
357
358
359
360
  		      src_device->missing ? "<missing disk>" :
  		        rcu_str_deref(src_device->name),
  		      src_device->devid,
  		      rcu_str_deref(tgt_device->name));
e93c89c1a   Stefan Behrens   Btrfs: add new so...
361
362
363
364
365
  	/*
  	 * from now on, the writes to the srcdev are all duplicated to
  	 * go to the tgtdev as well (refer to btrfs_map_block()).
  	 */
  	dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED;
4546bcaeb   Zach Brown   btrfs: use get_se...
366
  	dev_replace->time_started = get_seconds();
e93c89c1a   Stefan Behrens   Btrfs: add new so...
367
368
369
370
371
372
  	dev_replace->cursor_left = 0;
  	dev_replace->committed_cursor_left = 0;
  	dev_replace->cursor_left_last_write_of_item = 0;
  	dev_replace->cursor_right = 0;
  	dev_replace->is_valid = 1;
  	dev_replace->item_needs_writeback = 1;
7ccefb98c   Yauhen Kharuzhy   btrfs: Reset IO e...
373
374
  	atomic64_set(&dev_replace->num_write_errors, 0);
  	atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0);
73beece9c   Liu Bo   Btrfs: fix lockde...
375
  	btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
376

73416dab2   Liu Bo   Btrfs: move kobj ...
377
378
  	ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device);
  	if (ret)
ab8d0fc48   Jeff Mahoney   btrfs: convert pr...
379
  		btrfs_err(fs_info, "kobj add dev failed %d", ret);
73416dab2   Liu Bo   Btrfs: move kobj ...
380

578def7c5   Filipe Manana   Btrfs: don't wait...
381
  	btrfs_wait_ordered_roots(root->fs_info, -1, 0, (u64)-1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
382
383
384
385
386
  
  	/* force writing the updated state information to disk */
  	trans = btrfs_start_transaction(root, 0);
  	if (IS_ERR(trans)) {
  		ret = PTR_ERR(trans);
73beece9c   Liu Bo   Btrfs: fix lockde...
387
  		btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
388
389
390
391
392
393
394
395
  		goto leave;
  	}
  
  	ret = btrfs_commit_transaction(trans, root);
  	WARN_ON(ret);
  
  	/* the disk copy procedure reuses the scrub code */
  	ret = btrfs_scrub_dev(fs_info, src_device->devid, 0,
7cc8e58d5   Miao Xie   Btrfs: fix unprot...
396
  			      btrfs_device_get_total_bytes(src_device),
e93c89c1a   Stefan Behrens   Btrfs: add new so...
397
  			      &dev_replace->scrub_progress, 0, 1);
fc23c246d   Anand Jain   btrfs: use fs_inf...
398
  	ret = btrfs_dev_replace_finishing(fs_info, ret);
2fc9f6baa   Eryu Guan   Btrfs: return fai...
399
  	if (ret == -EINPROGRESS) {
b5255456c   Anand Jain   btrfs: refactor b...
400
  		ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS;
2fc9f6baa   Eryu Guan   Btrfs: return fai...
401
402
403
  	} else {
  		WARN_ON(ret);
  	}
e93c89c1a   Stefan Behrens   Btrfs: add new so...
404

2fc9f6baa   Eryu Guan   Btrfs: return fai...
405
  	return ret;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
406
407
408
409
  
  leave:
  	dev_replace->srcdev = NULL;
  	dev_replace->tgtdev = NULL;
73beece9c   Liu Bo   Btrfs: fix lockde...
410
  	btrfs_dev_replace_unlock(dev_replace, 1);
1c43366d3   Miao Xie   Btrfs: fix unprot...
411
  	btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
412
413
  	return ret;
  }
b5255456c   Anand Jain   btrfs: refactor b...
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
  int btrfs_dev_replace_by_ioctl(struct btrfs_root *root,
  			    struct btrfs_ioctl_dev_replace_args *args)
  {
  	int ret;
  
  	switch (args->start.cont_reading_from_srcdev_mode) {
  	case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS:
  	case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID:
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') ||
  	    args->start.tgtdev_name[0] == '\0')
  		return -EINVAL;
  
  	ret = btrfs_dev_replace_start(root, args->start.tgtdev_name,
  					args->start.srcdevid,
  					args->start.srcdev_name,
  					args->start.cont_reading_from_srcdev_mode);
  	args->result = ret;
  	/* don't warn if EINPROGRESS, someone else might be running scrub */
  	if (ret == BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS)
  		ret = 0;
  
  	return ret;
  }
c404e0dc2   Miao Xie   Btrfs: fix use-af...
442
  /*
013276101   Nicholas D Steeves   btrfs: fix string...
443
   * blocked until all in-flight bios operations are finished.
c404e0dc2   Miao Xie   Btrfs: fix use-af...
444
445
446
   */
  static void btrfs_rm_dev_replace_blocked(struct btrfs_fs_info *fs_info)
  {
c404e0dc2   Miao Xie   Btrfs: fix use-af...
447
  	set_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state);
7653947fe   Zhao Lei   Btrfs: btrfs_rm_d...
448
449
  	wait_event(fs_info->replace_wait, !percpu_counter_sum(
  		   &fs_info->bio_counter));
c404e0dc2   Miao Xie   Btrfs: fix use-af...
450
451
452
453
454
455
456
457
  }
  
  /*
   * we have removed target device, it is safe to allow new bios request.
   */
  static void btrfs_rm_dev_replace_unblocked(struct btrfs_fs_info *fs_info)
  {
  	clear_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state);
730d9ec36   David Sterba   btrfs: remove wai...
458
  	wake_up(&fs_info->replace_wait);
c404e0dc2   Miao Xie   Btrfs: fix use-af...
459
  }
e93c89c1a   Stefan Behrens   Btrfs: add new so...
460
461
462
463
464
465
466
467
468
469
470
471
472
  static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
  				       int scrub_ret)
  {
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  	struct btrfs_device *tgt_device;
  	struct btrfs_device *src_device;
  	struct btrfs_root *root = fs_info->tree_root;
  	u8 uuid_tmp[BTRFS_UUID_SIZE];
  	struct btrfs_trans_handle *trans;
  	int ret = 0;
  
  	/* don't allow cancel or unmount to disturb the finishing procedure */
  	mutex_lock(&dev_replace->lock_finishing_cancel_unmount);
73beece9c   Liu Bo   Btrfs: fix lockde...
473
  	btrfs_dev_replace_lock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
474
475
476
  	/* was the operation canceled, or is it finished? */
  	if (dev_replace->replace_state !=
  	    BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
73beece9c   Liu Bo   Btrfs: fix lockde...
477
  		btrfs_dev_replace_unlock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
478
479
480
481
482
483
  		mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  		return 0;
  	}
  
  	tgt_device = dev_replace->tgtdev;
  	src_device = dev_replace->srcdev;
73beece9c   Liu Bo   Btrfs: fix lockde...
484
  	btrfs_dev_replace_unlock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
485

e93c89c1a   Stefan Behrens   Btrfs: add new so...
486
487
488
489
  	/*
  	 * flush all outstanding I/O and inode extent mappings before the
  	 * copy operation is declared as being finished
  	 */
6c255e67c   Miao Xie   Btrfs: don't flus...
490
  	ret = btrfs_start_delalloc_roots(root->fs_info, 0, -1);
3edb2a68c   Miao Xie   Btrfs: check the ...
491
492
493
494
  	if (ret) {
  		mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  		return ret;
  	}
578def7c5   Filipe Manana   Btrfs: don't wait...
495
  	btrfs_wait_ordered_roots(root->fs_info, -1, 0, (u64)-1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
496
497
498
499
500
501
502
503
  
  	trans = btrfs_start_transaction(root, 0);
  	if (IS_ERR(trans)) {
  		mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  		return PTR_ERR(trans);
  	}
  	ret = btrfs_commit_transaction(trans, root);
  	WARN_ON(ret);
67a2c45ee   Miao Xie   Btrfs: fix use-af...
504
  	mutex_lock(&uuid_mutex);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
505
506
  	/* keep away write_all_supers() during the finishing procedure */
  	mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
2196d6e8a   Miao Xie   Btrfs: Fix misuse...
507
  	mutex_lock(&root->fs_info->chunk_mutex);
73beece9c   Liu Bo   Btrfs: fix lockde...
508
  	btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
509
510
511
512
513
  	dev_replace->replace_state =
  		scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED
  			  : BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED;
  	dev_replace->tgtdev = NULL;
  	dev_replace->srcdev = NULL;
4546bcaeb   Zach Brown   btrfs: use get_se...
514
  	dev_replace->time_stopped = get_seconds();
e93c89c1a   Stefan Behrens   Btrfs: add new so...
515
  	dev_replace->item_needs_writeback = 1;
c404e0dc2   Miao Xie   Btrfs: fix use-af...
516
517
518
519
520
521
  	/* replace old device with new one in mapping tree */
  	if (!scrub_ret) {
  		btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
  								src_device,
  								tgt_device);
  	} else {
ecaeb14b9   David Sterba   btrfs: switch mes...
522
523
  		btrfs_err_in_rcu(root->fs_info,
  			      "btrfs_scrub_dev(%s, %llu, %s) failed %d",
e93c89c1a   Stefan Behrens   Btrfs: add new so...
524
525
526
527
  			      src_device->missing ? "<missing disk>" :
  			        rcu_str_deref(src_device->name),
  			      src_device->devid,
  			      rcu_str_deref(tgt_device->name), scrub_ret);
73beece9c   Liu Bo   Btrfs: fix lockde...
528
  		btrfs_dev_replace_unlock(dev_replace, 1);
391cd9df8   Miao Xie   Btrfs: fix unprot...
529
  		mutex_unlock(&root->fs_info->chunk_mutex);
2196d6e8a   Miao Xie   Btrfs: Fix misuse...
530
  		mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
67a2c45ee   Miao Xie   Btrfs: fix use-af...
531
  		mutex_unlock(&uuid_mutex);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
532
533
534
  		if (tgt_device)
  			btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
  		mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
2fc9f6baa   Eryu Guan   Btrfs: return fai...
535
  		return scrub_ret;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
536
  	}
ecaeb14b9   David Sterba   btrfs: switch mes...
537
538
  	btrfs_info_in_rcu(root->fs_info,
  		      "dev_replace from %s (devid %llu) to %s finished",
e93c89c1a   Stefan Behrens   Btrfs: add new so...
539
540
541
542
543
544
545
  		      src_device->missing ? "<missing disk>" :
  		        rcu_str_deref(src_device->name),
  		      src_device->devid,
  		      rcu_str_deref(tgt_device->name));
  	tgt_device->is_tgtdev_for_dev_replace = 0;
  	tgt_device->devid = src_device->devid;
  	src_device->devid = BTRFS_DEV_REPLACE_DEVID;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
546
547
548
  	memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp));
  	memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid));
  	memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid));
7cc8e58d5   Miao Xie   Btrfs: fix unprot...
549
550
551
552
  	btrfs_device_set_total_bytes(tgt_device, src_device->total_bytes);
  	btrfs_device_set_disk_total_bytes(tgt_device,
  					  src_device->disk_total_bytes);
  	btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
935e5cc93   Miao Xie   Btrfs: fix wrong ...
553
554
  	ASSERT(list_empty(&src_device->resized_list));
  	tgt_device->commit_total_bytes = src_device->commit_total_bytes;
ce7213c70   Miao Xie   Btrfs: fix wrong ...
555
  	tgt_device->commit_bytes_used = src_device->bytes_used;
88acff64c   Anand Jain   btrfs: cleanup as...
556
557
  
  	btrfs_assign_next_active_device(fs_info, src_device, tgt_device);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
558
  	list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
82372bc81   Miao Xie   Btrfs: make the l...
559
  	fs_info->fs_devices->rw_devices++;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
560

73beece9c   Liu Bo   Btrfs: fix lockde...
561
  	btrfs_dev_replace_unlock(dev_replace, 1);
12b894cb2   Qu Wenruo   btrfs: Fix a dead...
562

c404e0dc2   Miao Xie   Btrfs: fix use-af...
563
  	btrfs_rm_dev_replace_blocked(fs_info);
084b6e7c7   Qu Wenruo   btrfs: Fix a lock...
564
  	btrfs_rm_dev_replace_remove_srcdev(fs_info, src_device);
1357272fc   Ilya Dryomov   Btrfs: fix a use-...
565

c404e0dc2   Miao Xie   Btrfs: fix use-af...
566
  	btrfs_rm_dev_replace_unblocked(fs_info);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
567
568
569
570
571
572
573
  	/*
  	 * this is again a consistent state where no dev_replace procedure
  	 * is running, the target device is part of the filesystem, the
  	 * source device is not part of the filesystem anymore and its 1st
  	 * superblock is scratched out so that it is no longer marked to
  	 * belong to this filesystem.
  	 */
391cd9df8   Miao Xie   Btrfs: fix unprot...
574
  	mutex_unlock(&root->fs_info->chunk_mutex);
2196d6e8a   Miao Xie   Btrfs: Fix misuse...
575
  	mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
67a2c45ee   Miao Xie   Btrfs: fix use-af...
576
  	mutex_unlock(&uuid_mutex);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
577

084b6e7c7   Qu Wenruo   btrfs: Fix a lock...
578
  	/* replace the sysfs entry */
325760404   Anand Jain   Btrfs: rename btr...
579
  	btrfs_sysfs_rm_device_link(fs_info->fs_devices, src_device);
084b6e7c7   Qu Wenruo   btrfs: Fix a lock...
580
  	btrfs_rm_dev_replace_free_srcdev(fs_info, src_device);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
  	/* write back the superblocks */
  	trans = btrfs_start_transaction(root, 0);
  	if (!IS_ERR(trans))
  		btrfs_commit_transaction(trans, root);
  
  	mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  
  	return 0;
  }
  
  static void btrfs_dev_replace_update_device_in_mapping_tree(
  						struct btrfs_fs_info *fs_info,
  						struct btrfs_device *srcdev,
  						struct btrfs_device *tgtdev)
  {
  	struct extent_map_tree *em_tree = &fs_info->mapping_tree.map_tree;
  	struct extent_map *em;
  	struct map_lookup *map;
  	u64 start = 0;
  	int i;
  
  	write_lock(&em_tree->lock);
  	do {
  		em = lookup_extent_mapping(em_tree, start, (u64)-1);
  		if (!em)
  			break;
95617d693   Jeff Mahoney   btrfs: cleanup, s...
607
  		map = em->map_lookup;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
608
609
610
611
612
613
614
615
  		for (i = 0; i < map->num_stripes; i++)
  			if (srcdev == map->stripes[i].dev)
  				map->stripes[i].dev = tgtdev;
  		start = em->start + em->len;
  		free_extent_map(em);
  	} while (start);
  	write_unlock(&em_tree->lock);
  }
e93c89c1a   Stefan Behrens   Btrfs: add new so...
616
617
618
619
  void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
  			      struct btrfs_ioctl_dev_replace_args *args)
  {
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
7cc8e58d5   Miao Xie   Btrfs: fix unprot...
620
  	struct btrfs_device *srcdev;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
621

73beece9c   Liu Bo   Btrfs: fix lockde...
622
  	btrfs_dev_replace_lock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
  	/* even if !dev_replace_is_valid, the values are good enough for
  	 * the replace_status ioctl */
  	args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
  	args->status.replace_state = dev_replace->replace_state;
  	args->status.time_started = dev_replace->time_started;
  	args->status.time_stopped = dev_replace->time_stopped;
  	args->status.num_write_errors =
  		atomic64_read(&dev_replace->num_write_errors);
  	args->status.num_uncorrectable_read_errors =
  		atomic64_read(&dev_replace->num_uncorrectable_read_errors);
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
  		args->status.progress_1000 = 0;
  		break;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  		args->status.progress_1000 = 1000;
  		break;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
7cc8e58d5   Miao Xie   Btrfs: fix unprot...
643
  		srcdev = dev_replace->srcdev;
f8c269d72   David Sterba   btrfs: cleanup 64...
644
645
  		args->status.progress_1000 = div_u64(dev_replace->cursor_left,
  			div_u64(btrfs_device_get_total_bytes(srcdev), 1000));
e93c89c1a   Stefan Behrens   Btrfs: add new so...
646
647
  		break;
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
648
  	btrfs_dev_replace_unlock(dev_replace, 0);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
  }
  
  int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info,
  			     struct btrfs_ioctl_dev_replace_args *args)
  {
  	args->result = __btrfs_dev_replace_cancel(fs_info);
  	return 0;
  }
  
  static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info)
  {
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  	struct btrfs_device *tgt_device = NULL;
  	struct btrfs_trans_handle *trans;
  	struct btrfs_root *root = fs_info->tree_root;
  	u64 result;
  	int ret;
e649e587c   Ilya Dryomov   Btrfs: disallow '...
666
667
  	if (fs_info->sb->s_flags & MS_RDONLY)
  		return -EROFS;
e93c89c1a   Stefan Behrens   Btrfs: add new so...
668
  	mutex_lock(&dev_replace->lock_finishing_cancel_unmount);
73beece9c   Liu Bo   Btrfs: fix lockde...
669
  	btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
670
671
672
673
674
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
  		result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED;
73beece9c   Liu Bo   Btrfs: fix lockde...
675
  		btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
676
677
678
679
680
681
682
683
684
685
  		goto leave;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
  		result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
  		tgt_device = dev_replace->tgtdev;
  		dev_replace->tgtdev = NULL;
  		dev_replace->srcdev = NULL;
  		break;
  	}
  	dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED;
4546bcaeb   Zach Brown   btrfs: use get_se...
686
  	dev_replace->time_stopped = get_seconds();
e93c89c1a   Stefan Behrens   Btrfs: add new so...
687
  	dev_replace->item_needs_writeback = 1;
73beece9c   Liu Bo   Btrfs: fix lockde...
688
  	btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
  	btrfs_scrub_cancel(fs_info);
  
  	trans = btrfs_start_transaction(root, 0);
  	if (IS_ERR(trans)) {
  		mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  		return PTR_ERR(trans);
  	}
  	ret = btrfs_commit_transaction(trans, root);
  	WARN_ON(ret);
  	if (tgt_device)
  		btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
  
  leave:
  	mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  	return result;
  }
  
  void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info)
  {
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  
  	mutex_lock(&dev_replace->lock_finishing_cancel_unmount);
73beece9c   Liu Bo   Btrfs: fix lockde...
711
  	btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
712
713
714
715
716
717
718
719
720
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
  		break;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  		dev_replace->replace_state =
  			BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED;
4546bcaeb   Zach Brown   btrfs: use get_se...
721
  		dev_replace->time_stopped = get_seconds();
e93c89c1a   Stefan Behrens   Btrfs: add new so...
722
  		dev_replace->item_needs_writeback = 1;
efe120a06   Frank Holton   Btrfs: convert pr...
723
  		btrfs_info(fs_info, "suspending dev_replace for unmount");
e93c89c1a   Stefan Behrens   Btrfs: add new so...
724
725
  		break;
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
726
  	btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
727
728
729
730
731
732
733
734
  	mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
  }
  
  /* resume dev_replace procedure that was interrupted by unmount */
  int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info)
  {
  	struct task_struct *task;
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
73beece9c   Liu Bo   Btrfs: fix lockde...
735
  	btrfs_dev_replace_lock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
736
737
738
739
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
73beece9c   Liu Bo   Btrfs: fix lockde...
740
  		btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
741
742
743
744
745
746
747
748
749
  		return 0;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  		break;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
  		dev_replace->replace_state =
  			BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED;
  		break;
  	}
  	if (!dev_replace->tgtdev || !dev_replace->tgtdev->bdev) {
efe120a06   Frank Holton   Btrfs: convert pr...
750
  		btrfs_info(fs_info,
5d163e0e6   Jeff Mahoney   btrfs: unsplit pr...
751
752
753
  			   "cannot continue dev_replace, tgtdev is missing");
  		btrfs_info(fs_info,
  			   "you may cancel the operation after 'mount -o degraded'");
73beece9c   Liu Bo   Btrfs: fix lockde...
754
  		btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
755
756
  		return 0;
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
757
  	btrfs_dev_replace_unlock(dev_replace, 1);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
758
759
760
761
  
  	WARN_ON(atomic_xchg(
  		&fs_info->mutually_exclusive_operation_running, 1));
  	task = kthread_run(btrfs_dev_replace_kthread, fs_info, "btrfs-devrepl");
8c6ffba0e   Rusty Russell   PTR_RET is now PT...
762
  	return PTR_ERR_OR_ZERO(task);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
763
764
765
766
767
768
769
770
  }
  
  static int btrfs_dev_replace_kthread(void *data)
  {
  	struct btrfs_fs_info *fs_info = data;
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  	struct btrfs_ioctl_dev_replace_args *status_args;
  	u64 progress;
58c4e1738   David Sterba   btrfs: scrub: use...
771
  	status_args = kzalloc(sizeof(*status_args), GFP_KERNEL);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
772
773
774
775
  	if (status_args) {
  		btrfs_dev_replace_status(fs_info, status_args);
  		progress = status_args->status.progress_1000;
  		kfree(status_args);
f8c269d72   David Sterba   btrfs: cleanup 64...
776
  		progress = div_u64(progress, 10);
ecaeb14b9   David Sterba   btrfs: switch mes...
777
778
  		btrfs_info_in_rcu(fs_info,
  			"continuing dev_replace from %s (devid %llu) to %s @%u%%",
efe120a06   Frank Holton   Btrfs: convert pr...
779
780
781
782
783
784
785
  			dev_replace->srcdev->missing ? "<missing disk>" :
  			rcu_str_deref(dev_replace->srcdev->name),
  			dev_replace->srcdev->devid,
  			dev_replace->tgtdev ?
  			rcu_str_deref(dev_replace->tgtdev->name) :
  			"<missing target disk>",
  			(unsigned int)progress);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
786
787
788
789
790
791
792
793
794
795
796
797
798
799
  	}
  	btrfs_dev_replace_continue_on_mount(fs_info);
  	atomic_set(&fs_info->mutually_exclusive_operation_running, 0);
  
  	return 0;
  }
  
  static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info)
  {
  	struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
  	int ret;
  
  	ret = btrfs_scrub_dev(fs_info, dev_replace->srcdev->devid,
  			      dev_replace->committed_cursor_left,
7cc8e58d5   Miao Xie   Btrfs: fix unprot...
800
  			      btrfs_device_get_total_bytes(dev_replace->srcdev),
e93c89c1a   Stefan Behrens   Btrfs: add new so...
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
  			      &dev_replace->scrub_progress, 0, 1);
  	ret = btrfs_dev_replace_finishing(fs_info, ret);
  	WARN_ON(ret);
  	return 0;
  }
  
  int btrfs_dev_replace_is_ongoing(struct btrfs_dev_replace *dev_replace)
  {
  	if (!dev_replace->is_valid)
  		return 0;
  
  	switch (dev_replace->replace_state) {
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
  		return 0;
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
  	case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
  		/*
  		 * return true even if tgtdev is missing (this is
  		 * something that can happen if the dev_replace
  		 * procedure is suspended by an umount and then
  		 * the tgtdev is missing (or "btrfs dev scan") was
  		 * not called and the the filesystem is remounted
  		 * in degraded state. This does not stop the
  		 * dev_replace procedure. It needs to be canceled
bb7ab3b92   Adam Buchbinder   btrfs: Fix misspe...
827
  		 * manually if the cancellation is wanted.
e93c89c1a   Stefan Behrens   Btrfs: add new so...
828
829
830
831
832
  		 */
  		break;
  	}
  	return 1;
  }
73beece9c   Liu Bo   Btrfs: fix lockde...
833
  void btrfs_dev_replace_lock(struct btrfs_dev_replace *dev_replace, int rw)
e93c89c1a   Stefan Behrens   Btrfs: add new so...
834
  {
73beece9c   Liu Bo   Btrfs: fix lockde...
835
836
837
838
839
840
841
842
843
844
845
846
847
  	if (rw == 1) {
  		/* write */
  again:
  		wait_event(dev_replace->read_lock_wq,
  			   atomic_read(&dev_replace->blocking_readers) == 0);
  		write_lock(&dev_replace->lock);
  		if (atomic_read(&dev_replace->blocking_readers)) {
  			write_unlock(&dev_replace->lock);
  			goto again;
  		}
  	} else {
  		read_lock(&dev_replace->lock);
  		atomic_inc(&dev_replace->read_locks);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
848
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
849
  }
e93c89c1a   Stefan Behrens   Btrfs: add new so...
850

73beece9c   Liu Bo   Btrfs: fix lockde...
851
852
853
854
855
856
857
858
859
860
  void btrfs_dev_replace_unlock(struct btrfs_dev_replace *dev_replace, int rw)
  {
  	if (rw == 1) {
  		/* write */
  		ASSERT(atomic_read(&dev_replace->blocking_readers) == 0);
  		write_unlock(&dev_replace->lock);
  	} else {
  		ASSERT(atomic_read(&dev_replace->read_locks) > 0);
  		atomic_dec(&dev_replace->read_locks);
  		read_unlock(&dev_replace->lock);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
861
  	}
73beece9c   Liu Bo   Btrfs: fix lockde...
862
  }
e93c89c1a   Stefan Behrens   Btrfs: add new so...
863

73beece9c   Liu Bo   Btrfs: fix lockde...
864
865
866
867
868
869
870
871
  /* inc blocking cnt and release read lock */
  void btrfs_dev_replace_set_lock_blocking(
  					struct btrfs_dev_replace *dev_replace)
  {
  	/* only set blocking for read lock */
  	ASSERT(atomic_read(&dev_replace->read_locks) > 0);
  	atomic_inc(&dev_replace->blocking_readers);
  	read_unlock(&dev_replace->lock);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
872
  }
73beece9c   Liu Bo   Btrfs: fix lockde...
873
874
875
  /* acquire read lock and dec blocking cnt */
  void btrfs_dev_replace_clear_lock_blocking(
  					struct btrfs_dev_replace *dev_replace)
e93c89c1a   Stefan Behrens   Btrfs: add new so...
876
  {
73beece9c   Liu Bo   Btrfs: fix lockde...
877
878
879
880
881
882
883
  	/* only set blocking for read lock */
  	ASSERT(atomic_read(&dev_replace->read_locks) > 0);
  	ASSERT(atomic_read(&dev_replace->blocking_readers) > 0);
  	read_lock(&dev_replace->lock);
  	if (atomic_dec_and_test(&dev_replace->blocking_readers) &&
  	    waitqueue_active(&dev_replace->read_lock_wq))
  		wake_up(&dev_replace->read_lock_wq);
e93c89c1a   Stefan Behrens   Btrfs: add new so...
884
  }
c404e0dc2   Miao Xie   Btrfs: fix use-af...
885
886
887
888
889
  
  void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info)
  {
  	percpu_counter_inc(&fs_info->bio_counter);
  }
4245215d6   Miao Xie   Btrfs, raid56: fi...
890
  void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount)
c404e0dc2   Miao Xie   Btrfs: fix use-af...
891
  {
4245215d6   Miao Xie   Btrfs, raid56: fi...
892
  	percpu_counter_sub(&fs_info->bio_counter, amount);
c404e0dc2   Miao Xie   Btrfs: fix use-af...
893
894
895
896
897
898
899
  
  	if (waitqueue_active(&fs_info->replace_wait))
  		wake_up(&fs_info->replace_wait);
  }
  
  void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info)
  {
09dd7a01c   Zhao Lei   Btrfs: Cleanup bt...
900
901
902
903
904
  	while (1) {
  		percpu_counter_inc(&fs_info->bio_counter);
  		if (likely(!test_bit(BTRFS_FS_STATE_DEV_REPLACING,
  				     &fs_info->fs_state)))
  			break;
c404e0dc2   Miao Xie   Btrfs: fix use-af...
905
906
907
908
  		btrfs_bio_counter_dec(fs_info);
  		wait_event(fs_info->replace_wait,
  			   !test_bit(BTRFS_FS_STATE_DEV_REPLACING,
  				     &fs_info->fs_state));
c404e0dc2   Miao Xie   Btrfs: fix use-af...
909
  	}
c404e0dc2   Miao Xie   Btrfs: fix use-af...
910
  }