1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Code for moving data off a device. |
4 | */ |
5 | |
6 | #include "bcachefs.h" |
7 | #include "bkey_buf.h" |
8 | #include "btree_update.h" |
9 | #include "btree_update_interior.h" |
10 | #include "buckets.h" |
11 | #include "errcode.h" |
12 | #include "extents.h" |
13 | #include "io_write.h" |
14 | #include "journal.h" |
15 | #include "keylist.h" |
16 | #include "migrate.h" |
17 | #include "move.h" |
18 | #include "replicas.h" |
19 | #include "super-io.h" |
20 | |
21 | static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s k, |
22 | unsigned dev_idx, int flags, bool metadata) |
23 | { |
24 | unsigned replicas = metadata ? c->opts.metadata_replicas : c->opts.data_replicas; |
25 | unsigned lost = metadata ? BCH_FORCE_IF_METADATA_LOST : BCH_FORCE_IF_DATA_LOST; |
26 | unsigned degraded = metadata ? BCH_FORCE_IF_METADATA_DEGRADED : BCH_FORCE_IF_DATA_DEGRADED; |
27 | unsigned nr_good; |
28 | |
29 | bch2_bkey_drop_device(k, dev_idx); |
30 | |
31 | nr_good = bch2_bkey_durability(c, k.s_c); |
32 | if ((!nr_good && !(flags & lost)) || |
33 | (nr_good < replicas && !(flags & degraded))) |
34 | return -BCH_ERR_remove_would_lose_data; |
35 | |
36 | return 0; |
37 | } |
38 | |
39 | static int bch2_dev_usrdata_drop_key(struct btree_trans *trans, |
40 | struct btree_iter *iter, |
41 | struct bkey_s_c k, |
42 | unsigned dev_idx, |
43 | int flags) |
44 | { |
45 | struct bch_fs *c = trans->c; |
46 | struct bkey_i *n; |
47 | int ret; |
48 | |
49 | if (!bch2_bkey_has_device_c(k, dev_idx)) |
50 | return 0; |
51 | |
52 | n = bch2_bkey_make_mut(trans, iter, k: &k, BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE); |
53 | ret = PTR_ERR_OR_ZERO(ptr: n); |
54 | if (ret) |
55 | return ret; |
56 | |
57 | ret = drop_dev_ptrs(c, k: bkey_i_to_s(k: n), dev_idx, flags, metadata: false); |
58 | if (ret) |
59 | return ret; |
60 | |
61 | /* |
62 | * If the new extent no longer has any pointers, bch2_extent_normalize() |
63 | * will do the appropriate thing with it (turning it into a |
64 | * KEY_TYPE_error key, or just a discard if it was a cached extent) |
65 | */ |
66 | bch2_extent_normalize(c, bkey_i_to_s(k: n)); |
67 | |
68 | /* |
69 | * Since we're not inserting through an extent iterator |
70 | * (BTREE_ITER_ALL_SNAPSHOTS iterators aren't extent iterators), |
71 | * we aren't using the extent overwrite path to delete, we're |
72 | * just using the normal key deletion path: |
73 | */ |
74 | if (bkey_deleted(&n->k)) |
75 | n->k.size = 0; |
76 | return 0; |
77 | } |
78 | |
79 | static int bch2_dev_usrdata_drop(struct bch_fs *c, unsigned dev_idx, int flags) |
80 | { |
81 | struct btree_trans *trans = bch2_trans_get(c); |
82 | enum btree_id id; |
83 | int ret = 0; |
84 | |
85 | for (id = 0; id < BTREE_ID_NR; id++) { |
86 | if (!btree_type_has_ptrs(id)) |
87 | continue; |
88 | |
89 | ret = for_each_btree_key_commit(trans, iter, id, POS_MIN, |
90 | BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k, |
91 | NULL, NULL, BCH_TRANS_COMMIT_no_enospc, |
92 | bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags)); |
93 | if (ret) |
94 | break; |
95 | } |
96 | |
97 | bch2_trans_put(trans); |
98 | |
99 | return ret; |
100 | } |
101 | |
102 | static int bch2_dev_metadata_drop(struct bch_fs *c, unsigned dev_idx, int flags) |
103 | { |
104 | struct btree_trans *trans; |
105 | struct btree_iter iter; |
106 | struct closure cl; |
107 | struct btree *b; |
108 | struct bkey_buf k; |
109 | unsigned id; |
110 | int ret; |
111 | |
112 | /* don't handle this yet: */ |
113 | if (flags & BCH_FORCE_IF_METADATA_LOST) |
114 | return -BCH_ERR_remove_with_metadata_missing_unimplemented; |
115 | |
116 | trans = bch2_trans_get(c); |
117 | bch2_bkey_buf_init(s: &k); |
118 | closure_init_stack(cl: &cl); |
119 | |
120 | for (id = 0; id < BTREE_ID_NR; id++) { |
121 | bch2_trans_node_iter_init(trans, &iter, id, POS_MIN, 0, 0, |
122 | BTREE_ITER_PREFETCH); |
123 | retry: |
124 | ret = 0; |
125 | while (bch2_trans_begin(trans), |
126 | (b = bch2_btree_iter_peek_node(&iter)) && |
127 | !(ret = PTR_ERR_OR_ZERO(ptr: b))) { |
128 | if (!bch2_bkey_has_device_c(bkey_i_to_s_c(k: &b->key), dev_idx)) |
129 | goto next; |
130 | |
131 | bch2_bkey_buf_copy(s: &k, c, src: &b->key); |
132 | |
133 | ret = drop_dev_ptrs(c, k: bkey_i_to_s(k: k.k), |
134 | dev_idx, flags, metadata: true); |
135 | if (ret) |
136 | break; |
137 | |
138 | ret = bch2_btree_node_update_key(trans, &iter, b, k.k, 0, false); |
139 | if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) { |
140 | ret = 0; |
141 | continue; |
142 | } |
143 | |
144 | bch_err_msg(c, ret, "updating btree node key" ); |
145 | if (ret) |
146 | break; |
147 | next: |
148 | bch2_btree_iter_next_node(&iter); |
149 | } |
150 | if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) |
151 | goto retry; |
152 | |
153 | bch2_trans_iter_exit(trans, &iter); |
154 | |
155 | if (ret) |
156 | goto err; |
157 | } |
158 | |
159 | bch2_btree_interior_updates_flush(c); |
160 | ret = 0; |
161 | err: |
162 | bch2_bkey_buf_exit(s: &k, c); |
163 | bch2_trans_put(trans); |
164 | |
165 | BUG_ON(bch2_err_matches(ret, BCH_ERR_transaction_restart)); |
166 | |
167 | return ret; |
168 | } |
169 | |
170 | int bch2_dev_data_drop(struct bch_fs *c, unsigned dev_idx, int flags) |
171 | { |
172 | return bch2_dev_usrdata_drop(c, dev_idx, flags) ?: |
173 | bch2_dev_metadata_drop(c, dev_idx, flags); |
174 | } |
175 | |