Commit | Line | Data |
---|---|---|
1809b8cb KO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * io_misc.c - fallocate, fpunch, truncate: | |
4 | */ | |
5 | ||
6 | #include "bcachefs.h" | |
7 | #include "alloc_foreground.h" | |
8 | #include "bkey_buf.h" | |
9 | #include "btree_update.h" | |
10 | #include "buckets.h" | |
11 | #include "clock.h" | |
5902cc28 | 12 | #include "error.h" |
1809b8cb | 13 | #include "extents.h" |
5902cc28 KO |
14 | #include "extent_update.h" |
15 | #include "inode.h" | |
1809b8cb KO |
16 | #include "io_misc.h" |
17 | #include "io_write.h" | |
b030e262 | 18 | #include "logged_ops.h" |
1809b8cb KO |
19 | #include "subvolume.h" |
20 | ||
21 | /* Overwrites whatever was present with zeroes: */ | |
22 | int bch2_extent_fallocate(struct btree_trans *trans, | |
23 | subvol_inum inum, | |
24 | struct btree_iter *iter, | |
25 | unsigned sectors, | |
26 | struct bch_io_opts opts, | |
27 | s64 *i_sectors_delta, | |
28 | struct write_point_specifier write_point) | |
29 | { | |
30 | struct bch_fs *c = trans->c; | |
31 | struct disk_reservation disk_res = { 0 }; | |
32 | struct closure cl; | |
33 | struct open_buckets open_buckets = { 0 }; | |
34 | struct bkey_s_c k; | |
35 | struct bkey_buf old, new; | |
36 | unsigned sectors_allocated = 0; | |
37 | bool have_reservation = false; | |
38 | bool unwritten = opts.nocow && | |
39 | c->sb.version >= bcachefs_metadata_version_unwritten_extents; | |
40 | int ret; | |
41 | ||
42 | bch2_bkey_buf_init(&old); | |
43 | bch2_bkey_buf_init(&new); | |
44 | closure_init_stack(&cl); | |
45 | ||
46 | k = bch2_btree_iter_peek_slot(iter); | |
47 | ret = bkey_err(k); | |
48 | if (ret) | |
49 | return ret; | |
50 | ||
51 | sectors = min_t(u64, sectors, k.k->p.offset - iter->pos.offset); | |
52 | ||
53 | if (!have_reservation) { | |
54 | unsigned new_replicas = | |
55 | max(0, (int) opts.data_replicas - | |
56 | (int) bch2_bkey_nr_ptrs_fully_allocated(k)); | |
57 | /* | |
58 | * Get a disk reservation before (in the nocow case) calling | |
59 | * into the allocator: | |
60 | */ | |
61 | ret = bch2_disk_reservation_get(c, &disk_res, sectors, new_replicas, 0); | |
62 | if (unlikely(ret)) | |
63 | goto err; | |
64 | ||
65 | bch2_bkey_buf_reassemble(&old, c, k); | |
66 | } | |
67 | ||
68 | if (have_reservation) { | |
69 | if (!bch2_extents_match(k, bkey_i_to_s_c(old.k))) | |
70 | goto err; | |
71 | ||
72 | bch2_key_resize(&new.k->k, sectors); | |
73 | } else if (!unwritten) { | |
74 | struct bkey_i_reservation *reservation; | |
75 | ||
76 | bch2_bkey_buf_realloc(&new, c, sizeof(*reservation) / sizeof(u64)); | |
77 | reservation = bkey_reservation_init(new.k); | |
78 | reservation->k.p = iter->pos; | |
79 | bch2_key_resize(&reservation->k, sectors); | |
80 | reservation->v.nr_replicas = opts.data_replicas; | |
81 | } else { | |
82 | struct bkey_i_extent *e; | |
83 | struct bch_devs_list devs_have; | |
84 | struct write_point *wp; | |
85 | struct bch_extent_ptr *ptr; | |
86 | ||
87 | devs_have.nr = 0; | |
88 | ||
89 | bch2_bkey_buf_realloc(&new, c, BKEY_EXTENT_U64s_MAX); | |
90 | ||
91 | e = bkey_extent_init(new.k); | |
92 | e->k.p = iter->pos; | |
93 | ||
94 | ret = bch2_alloc_sectors_start_trans(trans, | |
95 | opts.foreground_target, | |
96 | false, | |
97 | write_point, | |
98 | &devs_have, | |
99 | opts.data_replicas, | |
100 | opts.data_replicas, | |
101 | BCH_WATERMARK_normal, 0, &cl, &wp); | |
102 | if (bch2_err_matches(ret, BCH_ERR_operation_blocked)) | |
103 | ret = -BCH_ERR_transaction_restart_nested; | |
104 | if (ret) | |
105 | goto err; | |
106 | ||
107 | sectors = min(sectors, wp->sectors_free); | |
108 | sectors_allocated = sectors; | |
109 | ||
110 | bch2_key_resize(&e->k, sectors); | |
111 | ||
112 | bch2_open_bucket_get(c, wp, &open_buckets); | |
113 | bch2_alloc_sectors_append_ptrs(c, wp, &e->k_i, sectors, false); | |
114 | bch2_alloc_sectors_done(c, wp); | |
115 | ||
116 | extent_for_each_ptr(extent_i_to_s(e), ptr) | |
117 | ptr->unwritten = true; | |
118 | } | |
119 | ||
120 | have_reservation = true; | |
121 | ||
122 | ret = bch2_extent_update(trans, inum, iter, new.k, &disk_res, | |
123 | 0, i_sectors_delta, true); | |
124 | err: | |
125 | if (!ret && sectors_allocated) | |
126 | bch2_increment_clock(c, sectors_allocated, WRITE); | |
127 | ||
128 | bch2_open_buckets_put(c, &open_buckets); | |
129 | bch2_disk_reservation_put(c, &disk_res); | |
130 | bch2_bkey_buf_exit(&new, c); | |
131 | bch2_bkey_buf_exit(&old, c); | |
132 | ||
133 | if (closure_nr_remaining(&cl) != 1) { | |
134 | bch2_trans_unlock(trans); | |
135 | closure_sync(&cl); | |
136 | } | |
137 | ||
138 | return ret; | |
139 | } | |
140 | ||
141 | /* | |
142 | * Returns -BCH_ERR_transacton_restart if we had to drop locks: | |
143 | */ | |
144 | int bch2_fpunch_at(struct btree_trans *trans, struct btree_iter *iter, | |
145 | subvol_inum inum, u64 end, | |
146 | s64 *i_sectors_delta) | |
147 | { | |
148 | struct bch_fs *c = trans->c; | |
149 | unsigned max_sectors = KEY_SIZE_MAX & (~0 << c->block_bits); | |
150 | struct bpos end_pos = POS(inum.inum, end); | |
151 | struct bkey_s_c k; | |
152 | int ret = 0, ret2 = 0; | |
153 | u32 snapshot; | |
154 | ||
155 | while (!ret || | |
156 | bch2_err_matches(ret, BCH_ERR_transaction_restart)) { | |
157 | struct disk_reservation disk_res = | |
158 | bch2_disk_reservation_init(c, 0); | |
159 | struct bkey_i delete; | |
160 | ||
161 | if (ret) | |
162 | ret2 = ret; | |
163 | ||
164 | bch2_trans_begin(trans); | |
165 | ||
166 | ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot); | |
167 | if (ret) | |
168 | continue; | |
169 | ||
170 | bch2_btree_iter_set_snapshot(iter, snapshot); | |
171 | ||
172 | /* | |
173 | * peek_upto() doesn't have ideal semantics for extents: | |
174 | */ | |
175 | k = bch2_btree_iter_peek_upto(iter, end_pos); | |
176 | if (!k.k) | |
177 | break; | |
178 | ||
179 | ret = bkey_err(k); | |
180 | if (ret) | |
181 | continue; | |
182 | ||
183 | bkey_init(&delete.k); | |
184 | delete.k.p = iter->pos; | |
185 | ||
186 | /* create the biggest key we can */ | |
187 | bch2_key_resize(&delete.k, max_sectors); | |
188 | bch2_cut_back(end_pos, &delete); | |
189 | ||
190 | ret = bch2_extent_update(trans, inum, iter, &delete, | |
191 | &disk_res, 0, i_sectors_delta, false); | |
192 | bch2_disk_reservation_put(c, &disk_res); | |
193 | } | |
194 | ||
195 | return ret ?: ret2; | |
196 | } | |
197 | ||
198 | int bch2_fpunch(struct bch_fs *c, subvol_inum inum, u64 start, u64 end, | |
199 | s64 *i_sectors_delta) | |
200 | { | |
6bd68ec2 | 201 | struct btree_trans *trans = bch2_trans_get(c); |
1809b8cb KO |
202 | struct btree_iter iter; |
203 | int ret; | |
204 | ||
6bd68ec2 | 205 | bch2_trans_iter_init(trans, &iter, BTREE_ID_extents, |
1809b8cb KO |
206 | POS(inum.inum, start), |
207 | BTREE_ITER_INTENT); | |
208 | ||
6bd68ec2 | 209 | ret = bch2_fpunch_at(trans, &iter, inum, end, i_sectors_delta); |
1809b8cb | 210 | |
6bd68ec2 KO |
211 | bch2_trans_iter_exit(trans, &iter); |
212 | bch2_trans_put(trans); | |
1809b8cb KO |
213 | |
214 | if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) | |
215 | ret = 0; | |
216 | ||
217 | return ret; | |
218 | } | |
5902cc28 | 219 | |
b030e262 KO |
220 | /* truncate: */ |
221 | ||
222 | void bch2_logged_op_truncate_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) | |
223 | { | |
224 | struct bkey_s_c_logged_op_truncate op = bkey_s_c_to_logged_op_truncate(k); | |
225 | ||
226 | prt_printf(out, "subvol=%u", le32_to_cpu(op.v->subvol)); | |
227 | prt_printf(out, " inum=%llu", le64_to_cpu(op.v->inum)); | |
228 | prt_printf(out, " new_i_size=%llu", le64_to_cpu(op.v->new_i_size)); | |
229 | } | |
230 | ||
5902cc28 KO |
231 | static int truncate_set_isize(struct btree_trans *trans, |
232 | subvol_inum inum, | |
233 | u64 new_i_size) | |
234 | { | |
235 | struct btree_iter iter = { NULL }; | |
236 | struct bch_inode_unpacked inode_u; | |
237 | int ret; | |
238 | ||
239 | ret = bch2_inode_peek(trans, &iter, &inode_u, inum, BTREE_ITER_INTENT) ?: | |
240 | (inode_u.bi_size = new_i_size, 0) ?: | |
241 | bch2_inode_write(trans, &iter, &inode_u); | |
242 | ||
243 | bch2_trans_iter_exit(trans, &iter); | |
244 | return ret; | |
245 | } | |
246 | ||
b030e262 KO |
247 | static int __bch2_resume_logged_op_truncate(struct btree_trans *trans, |
248 | struct bkey_i *op_k, | |
249 | u64 *i_sectors_delta) | |
5902cc28 | 250 | { |
b030e262 | 251 | struct bch_fs *c = trans->c; |
5902cc28 | 252 | struct btree_iter fpunch_iter; |
b030e262 KO |
253 | struct bkey_i_logged_op_truncate *op = bkey_i_to_logged_op_truncate(op_k); |
254 | subvol_inum inum = { le32_to_cpu(op->v.subvol), le64_to_cpu(op->v.inum) }; | |
255 | u64 new_i_size = le64_to_cpu(op->v.new_i_size); | |
5902cc28 KO |
256 | int ret; |
257 | ||
b030e262 KO |
258 | ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, |
259 | truncate_set_isize(trans, inum, new_i_size)); | |
5902cc28 KO |
260 | if (ret) |
261 | goto err; | |
262 | ||
b030e262 KO |
263 | bch2_trans_iter_init(trans, &fpunch_iter, BTREE_ID_extents, |
264 | POS(inum.inum, round_up(new_i_size, block_bytes(c)) >> 9), | |
265 | BTREE_ITER_INTENT); | |
266 | ret = bch2_fpunch_at(trans, &fpunch_iter, inum, U64_MAX, i_sectors_delta); | |
267 | bch2_trans_iter_exit(trans, &fpunch_iter); | |
268 | ||
5902cc28 KO |
269 | if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) |
270 | ret = 0; | |
5902cc28 | 271 | err: |
b030e262 | 272 | bch2_logged_op_finish(trans, op_k); |
5902cc28 KO |
273 | return ret; |
274 | } | |
275 | ||
b030e262 KO |
276 | int bch2_resume_logged_op_truncate(struct btree_trans *trans, struct bkey_i *op_k) |
277 | { | |
278 | return __bch2_resume_logged_op_truncate(trans, op_k, NULL); | |
279 | } | |
280 | ||
281 | int bch2_truncate(struct bch_fs *c, subvol_inum inum, u64 new_i_size, u64 *i_sectors_delta) | |
282 | { | |
283 | struct bkey_i_logged_op_truncate op; | |
284 | ||
285 | bkey_logged_op_truncate_init(&op.k_i); | |
286 | op.v.subvol = cpu_to_le32(inum.subvol); | |
287 | op.v.inum = cpu_to_le64(inum.inum); | |
288 | op.v.new_i_size = cpu_to_le64(new_i_size); | |
289 | ||
37fad949 KO |
290 | /* |
291 | * Logged ops aren't atomic w.r.t. snapshot creation: creating a | |
292 | * snapshot while they're in progress, then crashing, will result in the | |
293 | * resume only proceeding in one of the snapshots | |
294 | */ | |
295 | down_read(&c->snapshot_create_lock); | |
296 | int ret = bch2_trans_run(c, | |
6bd68ec2 KO |
297 | bch2_logged_op_start(trans, &op.k_i) ?: |
298 | __bch2_resume_logged_op_truncate(trans, &op.k_i, i_sectors_delta)); | |
37fad949 KO |
299 | up_read(&c->snapshot_create_lock); |
300 | ||
301 | return ret; | |
b030e262 KO |
302 | } |
303 | ||
f3e374ef KO |
304 | /* finsert/fcollapse: */ |
305 | ||
306 | void bch2_logged_op_finsert_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) | |
307 | { | |
308 | struct bkey_s_c_logged_op_finsert op = bkey_s_c_to_logged_op_finsert(k); | |
309 | ||
310 | prt_printf(out, "subvol=%u", le32_to_cpu(op.v->subvol)); | |
311 | prt_printf(out, " inum=%llu", le64_to_cpu(op.v->inum)); | |
312 | prt_printf(out, " dst_offset=%lli", le64_to_cpu(op.v->dst_offset)); | |
313 | prt_printf(out, " src_offset=%llu", le64_to_cpu(op.v->src_offset)); | |
314 | } | |
315 | ||
5902cc28 KO |
316 | static int adjust_i_size(struct btree_trans *trans, subvol_inum inum, u64 offset, s64 len) |
317 | { | |
318 | struct btree_iter iter; | |
319 | struct bch_inode_unpacked inode_u; | |
320 | int ret; | |
321 | ||
322 | offset <<= 9; | |
323 | len <<= 9; | |
324 | ||
325 | ret = bch2_inode_peek(trans, &iter, &inode_u, inum, BTREE_ITER_INTENT); | |
326 | if (ret) | |
327 | return ret; | |
328 | ||
329 | if (len > 0) { | |
330 | if (MAX_LFS_FILESIZE - inode_u.bi_size < len) { | |
331 | ret = -EFBIG; | |
332 | goto err; | |
333 | } | |
334 | ||
335 | if (offset >= inode_u.bi_size) { | |
336 | ret = -EINVAL; | |
337 | goto err; | |
338 | } | |
339 | } | |
340 | ||
341 | inode_u.bi_size += len; | |
342 | inode_u.bi_mtime = inode_u.bi_ctime = bch2_current_time(trans->c); | |
343 | ||
344 | ret = bch2_inode_write(trans, &iter, &inode_u); | |
345 | err: | |
346 | bch2_trans_iter_exit(trans, &iter); | |
347 | return ret; | |
348 | } | |
349 | ||
f3e374ef KO |
350 | static int __bch2_resume_logged_op_finsert(struct btree_trans *trans, |
351 | struct bkey_i *op_k, | |
352 | u64 *i_sectors_delta) | |
5902cc28 | 353 | { |
f3e374ef KO |
354 | struct bch_fs *c = trans->c; |
355 | struct btree_iter iter; | |
356 | struct bkey_i_logged_op_finsert *op = bkey_i_to_logged_op_finsert(op_k); | |
357 | subvol_inum inum = { le32_to_cpu(op->v.subvol), le64_to_cpu(op->v.inum) }; | |
358 | u64 dst_offset = le64_to_cpu(op->v.dst_offset); | |
359 | u64 src_offset = le64_to_cpu(op->v.src_offset); | |
360 | s64 shift = dst_offset - src_offset; | |
361 | u64 len = abs(shift); | |
362 | u64 pos = le64_to_cpu(op->v.pos); | |
363 | bool insert = shift > 0; | |
5902cc28 KO |
364 | int ret = 0; |
365 | ||
f3e374ef KO |
366 | bch2_trans_iter_init(trans, &iter, BTREE_ID_extents, |
367 | POS(inum.inum, 0), | |
5902cc28 | 368 | BTREE_ITER_INTENT); |
f3e374ef KO |
369 | |
370 | switch (op->v.state) { | |
371 | case LOGGED_OP_FINSERT_start: | |
372 | op->v.state = LOGGED_OP_FINSERT_shift_extents; | |
5902cc28 KO |
373 | |
374 | if (insert) { | |
f3e374ef KO |
375 | ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, |
376 | adjust_i_size(trans, inum, src_offset, len) ?: | |
377 | bch2_logged_op_update(trans, &op->k_i)); | |
5902cc28 KO |
378 | if (ret) |
379 | goto err; | |
380 | } else { | |
f3e374ef | 381 | bch2_btree_iter_set_pos(&iter, POS(inum.inum, src_offset)); |
5902cc28 | 382 | |
f3e374ef | 383 | ret = bch2_fpunch_at(trans, &iter, inum, src_offset + len, i_sectors_delta); |
5902cc28 KO |
384 | if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart)) |
385 | goto err; | |
386 | ||
f3e374ef KO |
387 | ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, |
388 | bch2_logged_op_update(trans, &op->k_i)); | |
5902cc28 KO |
389 | } |
390 | ||
f3e374ef KO |
391 | fallthrough; |
392 | case LOGGED_OP_FINSERT_shift_extents: | |
393 | while (1) { | |
5902cc28 KO |
394 | struct disk_reservation disk_res = |
395 | bch2_disk_reservation_init(c, 0); | |
f3e374ef | 396 | struct bkey_i delete, *copy; |
5902cc28 | 397 | struct bkey_s_c k; |
f3e374ef | 398 | struct bpos src_pos = POS(inum.inum, src_offset); |
5902cc28 KO |
399 | u32 snapshot; |
400 | ||
f3e374ef | 401 | bch2_trans_begin(trans); |
5902cc28 | 402 | |
f3e374ef | 403 | ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot); |
5902cc28 | 404 | if (ret) |
f3e374ef | 405 | goto btree_err; |
5902cc28 | 406 | |
f3e374ef KO |
407 | bch2_btree_iter_set_snapshot(&iter, snapshot); |
408 | bch2_btree_iter_set_pos(&iter, SPOS(inum.inum, pos, snapshot)); | |
5902cc28 KO |
409 | |
410 | k = insert | |
f3e374ef KO |
411 | ? bch2_btree_iter_peek_prev(&iter) |
412 | : bch2_btree_iter_peek_upto(&iter, POS(inum.inum, U64_MAX)); | |
5902cc28 | 413 | if ((ret = bkey_err(k))) |
f3e374ef | 414 | goto btree_err; |
5902cc28 | 415 | |
f3e374ef KO |
416 | if (!k.k || |
417 | k.k->p.inode != inum.inum || | |
418 | bkey_le(k.k->p, POS(inum.inum, src_offset))) | |
5902cc28 KO |
419 | break; |
420 | ||
f3e374ef KO |
421 | copy = bch2_bkey_make_mut_noupdate(trans, k); |
422 | if ((ret = PTR_ERR_OR_ZERO(copy))) | |
423 | goto btree_err; | |
5902cc28 KO |
424 | |
425 | if (insert && | |
f3e374ef KO |
426 | bkey_lt(bkey_start_pos(k.k), src_pos)) { |
427 | bch2_cut_front(src_pos, copy); | |
5902cc28 | 428 | |
f3e374ef KO |
429 | /* Splitting compressed extent? */ |
430 | bch2_disk_reservation_add(c, &disk_res, | |
431 | copy->k.size * | |
432 | bch2_bkey_nr_ptrs_allocated(bkey_i_to_s_c(copy)), | |
433 | BCH_DISK_RESERVATION_NOFAIL); | |
5902cc28 KO |
434 | } |
435 | ||
436 | bkey_init(&delete.k); | |
f3e374ef KO |
437 | delete.k.p = copy->k.p; |
438 | delete.k.p.snapshot = snapshot; | |
439 | delete.k.size = copy->k.size; | |
5902cc28 | 440 | |
f3e374ef KO |
441 | copy->k.p.offset += shift; |
442 | copy->k.p.snapshot = snapshot; | |
5902cc28 | 443 | |
f3e374ef | 444 | op->v.pos = cpu_to_le64(insert ? bkey_start_offset(&delete.k) : delete.k.p.offset); |
5902cc28 | 445 | |
f3e374ef KO |
446 | ret = bch2_btree_insert_trans(trans, BTREE_ID_extents, &delete, 0) ?: |
447 | bch2_btree_insert_trans(trans, BTREE_ID_extents, copy, 0) ?: | |
448 | bch2_logged_op_update(trans, &op->k_i) ?: | |
449 | bch2_trans_commit(trans, &disk_res, NULL, BTREE_INSERT_NOFAIL); | |
450 | btree_err: | |
5902cc28 KO |
451 | bch2_disk_reservation_put(c, &disk_res); |
452 | ||
f3e374ef KO |
453 | if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) |
454 | continue; | |
455 | if (ret) | |
456 | goto err; | |
457 | ||
458 | pos = le64_to_cpu(op->v.pos); | |
5902cc28 KO |
459 | } |
460 | ||
f3e374ef | 461 | op->v.state = LOGGED_OP_FINSERT_finish; |
5902cc28 KO |
462 | |
463 | if (!insert) { | |
f3e374ef KO |
464 | ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, |
465 | adjust_i_size(trans, inum, src_offset, shift) ?: | |
466 | bch2_logged_op_update(trans, &op->k_i)); | |
5902cc28 KO |
467 | } else { |
468 | /* We need an inode update to update bi_journal_seq for fsync: */ | |
f3e374ef KO |
469 | ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, |
470 | adjust_i_size(trans, inum, 0, 0) ?: | |
471 | bch2_logged_op_update(trans, &op->k_i)); | |
472 | } | |
473 | ||
265cc423 | 474 | break; |
f3e374ef | 475 | case LOGGED_OP_FINSERT_finish: |
265cc423 | 476 | break; |
5902cc28 KO |
477 | } |
478 | err: | |
f3e374ef KO |
479 | bch2_logged_op_finish(trans, op_k); |
480 | bch2_trans_iter_exit(trans, &iter); | |
5902cc28 KO |
481 | return ret; |
482 | } | |
f3e374ef KO |
483 | |
484 | int bch2_resume_logged_op_finsert(struct btree_trans *trans, struct bkey_i *op_k) | |
485 | { | |
486 | return __bch2_resume_logged_op_finsert(trans, op_k, NULL); | |
487 | } | |
488 | ||
489 | int bch2_fcollapse_finsert(struct bch_fs *c, subvol_inum inum, | |
490 | u64 offset, u64 len, bool insert, | |
491 | s64 *i_sectors_delta) | |
492 | { | |
493 | struct bkey_i_logged_op_finsert op; | |
494 | s64 shift = insert ? len : -len; | |
495 | ||
496 | bkey_logged_op_finsert_init(&op.k_i); | |
497 | op.v.subvol = cpu_to_le32(inum.subvol); | |
498 | op.v.inum = cpu_to_le64(inum.inum); | |
499 | op.v.dst_offset = cpu_to_le64(offset + shift); | |
500 | op.v.src_offset = cpu_to_le64(offset); | |
501 | op.v.pos = cpu_to_le64(insert ? U64_MAX : offset); | |
502 | ||
37fad949 KO |
503 | /* |
504 | * Logged ops aren't atomic w.r.t. snapshot creation: creating a | |
505 | * snapshot while they're in progress, then crashing, will result in the | |
506 | * resume only proceeding in one of the snapshots | |
507 | */ | |
508 | down_read(&c->snapshot_create_lock); | |
509 | int ret = bch2_trans_run(c, | |
6bd68ec2 KO |
510 | bch2_logged_op_start(trans, &op.k_i) ?: |
511 | __bch2_resume_logged_op_finsert(trans, &op.k_i, i_sectors_delta)); | |
37fad949 KO |
512 | up_read(&c->snapshot_create_lock); |
513 | ||
514 | return ret; | |
f3e374ef | 515 | } |