summaryrefslogtreecommitdiff
path: root/AtomicRenames.mdwn
blob: 94ff1210b4a36eed1ce71caf88ab5b737d05f337 (plain)
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
103
104
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
[[!format  c """

struct btree_insert_multi {
	struct btree_iter	iter;
	struct bkey_i		*k;
};

static void multi_lock_write(struct btree_insert_multi *m, unsigned nr)
{
	struct btree *b = m[nr].iter.nodes[0];
	unsigned i;

	for (i = 0; i < nr; i++)
		if (b == m[i].iter.nodes[0])
			return; /* already locked */

	btree_node_lock_for_insert(b, &m[nr].iter);
}

static void multi_unlock_write(struct btree_insert_multi *m, unsigned nr)
{
	struct btree *b = m[nr].iter.nodes[0];
	unsigned i;

	for (i = 0; i < nr; i++)
		if (b == m[i].iter.nodes[0])
			return; /* already locked */

	btree_node_unlock_write(b, &m[nr].iter);
}

int bch_btree_insert_at_multi(struct btree_insert_multi *m, unsigned nr,
			      u64 *journal_seq, unsigned flags)
{
	struct cache_set *c = m[0].iter.c;
	struct journal_res res = { 0, 0 };
	struct btree_insert_multi *i;
	struct btree_iter *split;
	unsigned u64s = 0;

	for (i = m; i < m + nr; i++)
		u64s += jset_u64s(i->k->k.u64s);
retry:
	if (bch_journal_res_get(&c->journal, &res, u64s, u64s))
		return -EIO;

	for (i = m; i < m + nr; i++) {
		struct btree *b = i->iter.nodes[0];

		multi_lock_write(m, i - m);

		if (!__have_enough_space(b, i->k->k.u64s))
			goto split;
	}

	for (i = m; i < m + nr; i++)
		BUG_ON(!btree_insert_key(&i->iter, i->iter.nodes[0],
					 &i->iter.node_iters[0],
					 &keylist_single(i->k), NULL,
					 &res, journal_seq, flags));

	do {
		multi_unlock_write(m, --i - m);
	} while (i != m);

	bch_journal_res_put(&c->journal, &res);

	for (i = m; i < m + nr; i++)
		bch_btree_node_write_lazy(i->iter.nodes[0], &i->iter);

	return 0;
split:
	split = &i->iter;
	do {
		multi_unlock_write(m, i - m);
	} while (i-- != m);

	/*
	 * XXX: Do we need to drop our journal res for the split?
	 */
	bch_journal_res_put(&c->journal, &res);

	{
		struct btree *b = split->nodes[0];
		struct btree_split_state state;
		int ret;

		closure_init_stack(&state.stack_cl);
		bch_keylist_init(&state.parent_keys,
				 state.inline_keys,
				 ARRAY_SIZE(state.inline_keys));

		/*
		 * XXX: figure out how far we might need to split,
		 * instead of locking/reserving all the way to the root:
		 */
		split->locks_want = BTREE_MAX_DEPTH;
		if (!bch_btree_iter_upgrade(split))
			return -EINTR;

		state.reserve = bch_btree_reserve_get(c, b, split, 0,
					    !(flags & BTREE_INSERT_NOFAIL));
		if (IS_ERR(state.reserve))
			return PTR_ERR(state.reserve);

		ret = btree_split(b, split, NULL, flags, &state);

		bch_btree_reserve_put(c, state.reserve);

		if (ret)
			return ret;
		goto retry;
	}
}

int bch_dirent_rename(struct cache_set *c,
		      u64 src_dir, const struct qstr *src_name,
		      u64 dst_dir, const struct qstr *dst_name,
		      u64 *journal_seq, bool overwriting)
{
	struct btree_insert_multi trans[2];
	struct btree_iter *src_iter = &trans[0].iter;
	struct btree_iter *dst_iter = &trans[1].iter;
	struct bkey_s_c k;
	struct bkey_s_c_dirent src;
	struct bkey_i_dirent *dst;
	struct bkey_i delete;
	struct keylist keys;
	int ret;

	dst = dirent_create_key(&keys, 0, dst_name, 0);
	if (!dst)
		return -ENOMEM;

	bch_btree_iter_init_intent(src_iter, c, BTREE_ID_DIRENTS,
				   POS(src_dir, bch_dirent_hash(src_name)));
	bch_btree_iter_init_intent(dst_iter, c, BTREE_ID_DIRENTS,
				   POS(dst_dir, bch_dirent_hash(dst_name)));
	bch_btree_iter_link(src_iter, dst_iter);

	do {
		k = __dirent_find(src_iter, src_dir, src_name);
		if (IS_ERR(k.k)) {
			ret = PTR_ERR(k.k);
			goto err;
		}

		src = bkey_s_c_to_dirent(k);

		bkey_init(&delete.k);
		delete.k.p = src.k->p;
		delete.k.type = BCH_DIRENT_WHITEOUT;

		k = overwriting
			? __dirent_find(dst_iter, dst_dir, dst_name)
			: __dirent_find_hole(dst_iter, dst_dir, dst_name);
		if (IS_ERR(k.k)) {
			ret = PTR_ERR(k.k);
			goto err;
		}

		dst->v.d_inum = src.v->d_inum;
		dst->v.d_type = src.v->d_type;
		dst->k.p = k.k->p;

		trans[0].k = &delete;
		trans[1].k = &dst->k_i;

		ret = bch_btree_insert_at_multi(trans, ARRAY_SIZE(trans),
						journal_seq, 0);
		bch_btree_iter_unlock(src_iter);
		bch_btree_iter_unlock(dst_iter);
	} while (ret == -EINTR);

	bch_keylist_free(&keys);
	return 0;
err:
	ret = bch_btree_iter_unlock(src_iter) ?: ret;
	ret = bch_btree_iter_unlock(dst_iter) ?: ret;
	bch_keylist_free(&keys);
	return ret;
}

"""]]