brintos

brintos / linux-shallow public Read only

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