blob: f34f61dd0d0be216d89c3b1cdecc7d2ecc125177 [file] [log] [blame]
Christoph Hellwig80672532014-09-10 08:23:34 -07001/*
2 * Copyright (c) 2014 Christoph Hellwig.
3 */
4
5#include "blocklayout.h"
6
7#define NFSDBG_FACILITY NFSDBG_PNFS_LD
8
9static inline struct pnfs_block_extent *
10ext_node(struct rb_node *node)
11{
12 return rb_entry(node, struct pnfs_block_extent, be_node);
13}
14
15static struct pnfs_block_extent *
16ext_tree_first(struct rb_root *root)
17{
18 struct rb_node *node = rb_first(root);
19 return node ? ext_node(node) : NULL;
20}
21
22static struct pnfs_block_extent *
23ext_tree_prev(struct pnfs_block_extent *be)
24{
25 struct rb_node *node = rb_prev(&be->be_node);
26 return node ? ext_node(node) : NULL;
27}
28
29static struct pnfs_block_extent *
30ext_tree_next(struct pnfs_block_extent *be)
31{
32 struct rb_node *node = rb_next(&be->be_node);
33 return node ? ext_node(node) : NULL;
34}
35
36static inline sector_t
37ext_f_end(struct pnfs_block_extent *be)
38{
39 return be->be_f_offset + be->be_length;
40}
41
42static struct pnfs_block_extent *
43__ext_tree_search(struct rb_root *root, sector_t start)
44{
45 struct rb_node *node = root->rb_node;
46 struct pnfs_block_extent *be = NULL;
47
48 while (node) {
49 be = ext_node(node);
50 if (start < be->be_f_offset)
51 node = node->rb_left;
52 else if (start >= ext_f_end(be))
53 node = node->rb_right;
54 else
55 return be;
56 }
57
58 if (be) {
59 if (start < be->be_f_offset)
60 return be;
61
62 if (start >= ext_f_end(be))
63 return ext_tree_next(be);
64 }
65
66 return NULL;
67}
68
69static bool
70ext_can_merge(struct pnfs_block_extent *be1, struct pnfs_block_extent *be2)
71{
72 if (be1->be_state != be2->be_state)
73 return false;
Christoph Hellwig20d655d2014-09-02 21:28:00 -070074 if (be1->be_device != be2->be_device)
Christoph Hellwig80672532014-09-10 08:23:34 -070075 return false;
76
77 if (be1->be_f_offset + be1->be_length != be2->be_f_offset)
78 return false;
79
80 if (be1->be_state != PNFS_BLOCK_NONE_DATA &&
81 (be1->be_v_offset + be1->be_length != be2->be_v_offset))
82 return false;
83
84 if (be1->be_state == PNFS_BLOCK_INVALID_DATA &&
85 be1->be_tag != be2->be_tag)
86 return false;
87
88 return true;
89}
90
91static struct pnfs_block_extent *
92ext_try_to_merge_left(struct rb_root *root, struct pnfs_block_extent *be)
93{
94 struct pnfs_block_extent *left = ext_tree_prev(be);
95
96 if (left && ext_can_merge(left, be)) {
97 left->be_length += be->be_length;
98 rb_erase(&be->be_node, root);
Christoph Hellwig20d655d2014-09-02 21:28:00 -070099 nfs4_put_deviceid_node(be->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700100 kfree(be);
101 return left;
102 }
103
104 return be;
105}
106
107static struct pnfs_block_extent *
108ext_try_to_merge_right(struct rb_root *root, struct pnfs_block_extent *be)
109{
110 struct pnfs_block_extent *right = ext_tree_next(be);
111
112 if (right && ext_can_merge(be, right)) {
113 be->be_length += right->be_length;
114 rb_erase(&right->be_node, root);
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700115 nfs4_put_deviceid_node(right->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700116 kfree(right);
117 }
118
119 return be;
120}
121
122static void
123__ext_tree_insert(struct rb_root *root,
124 struct pnfs_block_extent *new, bool merge_ok)
125{
126 struct rb_node **p = &root->rb_node, *parent = NULL;
127 struct pnfs_block_extent *be;
128
129 while (*p) {
130 parent = *p;
131 be = ext_node(parent);
132
133 if (new->be_f_offset < be->be_f_offset) {
134 if (merge_ok && ext_can_merge(new, be)) {
135 be->be_f_offset = new->be_f_offset;
136 if (be->be_state != PNFS_BLOCK_NONE_DATA)
137 be->be_v_offset = new->be_v_offset;
138 be->be_length += new->be_length;
139 be = ext_try_to_merge_left(root, be);
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700140 goto free_new;
Christoph Hellwig80672532014-09-10 08:23:34 -0700141 }
142 p = &(*p)->rb_left;
143 } else if (new->be_f_offset >= ext_f_end(be)) {
144 if (merge_ok && ext_can_merge(be, new)) {
145 be->be_length += new->be_length;
146 be = ext_try_to_merge_right(root, be);
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700147 goto free_new;
Christoph Hellwig80672532014-09-10 08:23:34 -0700148 }
149 p = &(*p)->rb_right;
150 } else {
151 BUG();
152 }
153 }
154
155 rb_link_node(&new->be_node, parent, p);
156 rb_insert_color(&new->be_node, root);
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700157 return;
158free_new:
159 nfs4_put_deviceid_node(new->be_device);
160 kfree(new);
Christoph Hellwig80672532014-09-10 08:23:34 -0700161}
162
163static int
164__ext_tree_remove(struct rb_root *root, sector_t start, sector_t end)
165{
166 struct pnfs_block_extent *be;
167 sector_t len1 = 0, len2 = 0;
168 sector_t orig_f_offset;
169 sector_t orig_v_offset;
170 sector_t orig_len;
171
172 be = __ext_tree_search(root, start);
173 if (!be)
174 return 0;
175 if (be->be_f_offset >= end)
176 return 0;
177
178 orig_f_offset = be->be_f_offset;
179 orig_v_offset = be->be_v_offset;
180 orig_len = be->be_length;
181
182 if (start > be->be_f_offset)
183 len1 = start - be->be_f_offset;
184 if (ext_f_end(be) > end)
185 len2 = ext_f_end(be) - end;
186
187 if (len2 > 0) {
188 if (len1 > 0) {
189 struct pnfs_block_extent *new;
190
191 new = kzalloc(sizeof(*new), GFP_ATOMIC);
192 if (!new)
193 return -ENOMEM;
194
195 be->be_length = len1;
196
197 new->be_f_offset = end;
198 if (be->be_state != PNFS_BLOCK_NONE_DATA) {
199 new->be_v_offset =
200 orig_v_offset + orig_len - len2;
201 }
202 new->be_length = len2;
203 new->be_state = be->be_state;
204 new->be_tag = be->be_tag;
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700205 new->be_device = nfs4_get_deviceid(be->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700206
207 __ext_tree_insert(root, new, true);
208 } else {
209 be->be_f_offset = end;
210 if (be->be_state != PNFS_BLOCK_NONE_DATA) {
211 be->be_v_offset =
212 orig_v_offset + orig_len - len2;
213 }
214 be->be_length = len2;
215 }
216 } else {
217 if (len1 > 0) {
218 be->be_length = len1;
219 be = ext_tree_next(be);
220 }
221
222 while (be && ext_f_end(be) <= end) {
223 struct pnfs_block_extent *next = ext_tree_next(be);
224
225 rb_erase(&be->be_node, root);
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700226 nfs4_put_deviceid_node(be->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700227 kfree(be);
228 be = next;
229 }
230
231 if (be && be->be_f_offset < end) {
232 len1 = ext_f_end(be) - end;
233 be->be_f_offset = end;
234 if (be->be_state != PNFS_BLOCK_NONE_DATA)
235 be->be_v_offset += be->be_length - len1;
236 be->be_length = len1;
237 }
238 }
239
240 return 0;
241}
242
243int
244ext_tree_insert(struct pnfs_block_layout *bl, struct pnfs_block_extent *new)
245{
246 struct pnfs_block_extent *be;
247 struct rb_root *root;
248 int err = 0;
249
250 switch (new->be_state) {
251 case PNFS_BLOCK_READWRITE_DATA:
252 case PNFS_BLOCK_INVALID_DATA:
253 root = &bl->bl_ext_rw;
254 break;
255 case PNFS_BLOCK_READ_DATA:
256 case PNFS_BLOCK_NONE_DATA:
257 root = &bl->bl_ext_ro;
258 break;
259 default:
260 dprintk("invalid extent type\n");
261 return -EINVAL;
262 }
263
264 spin_lock(&bl->bl_ext_lock);
265retry:
266 be = __ext_tree_search(root, new->be_f_offset);
267 if (!be || be->be_f_offset >= ext_f_end(new)) {
268 __ext_tree_insert(root, new, true);
269 } else if (new->be_f_offset >= be->be_f_offset) {
270 if (ext_f_end(new) <= ext_f_end(be)) {
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700271 nfs4_put_deviceid_node(new->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700272 kfree(new);
273 } else {
274 sector_t new_len = ext_f_end(new) - ext_f_end(be);
275 sector_t diff = new->be_length - new_len;
276
277 new->be_f_offset += diff;
278 new->be_v_offset += diff;
279 new->be_length = new_len;
280 goto retry;
281 }
282 } else if (ext_f_end(new) <= ext_f_end(be)) {
283 new->be_length = be->be_f_offset - new->be_f_offset;
284 __ext_tree_insert(root, new, true);
285 } else {
286 struct pnfs_block_extent *split;
287 sector_t new_len = ext_f_end(new) - ext_f_end(be);
288 sector_t diff = new->be_length - new_len;
289
290 split = kmemdup(new, sizeof(*new), GFP_ATOMIC);
291 if (!split) {
292 err = -EINVAL;
293 goto out;
294 }
295
296 split->be_length = be->be_f_offset - split->be_f_offset;
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700297 split->be_device = nfs4_get_deviceid(new->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700298 __ext_tree_insert(root, split, true);
299
300 new->be_f_offset += diff;
301 new->be_v_offset += diff;
302 new->be_length = new_len;
303 goto retry;
304 }
305out:
306 spin_unlock(&bl->bl_ext_lock);
307 return err;
308}
309
310static bool
311__ext_tree_lookup(struct rb_root *root, sector_t isect,
312 struct pnfs_block_extent *ret)
313{
314 struct rb_node *node;
315 struct pnfs_block_extent *be;
316
317 node = root->rb_node;
318 while (node) {
319 be = ext_node(node);
320 if (isect < be->be_f_offset)
321 node = node->rb_left;
322 else if (isect >= ext_f_end(be))
323 node = node->rb_right;
324 else {
325 *ret = *be;
326 return true;
327 }
328 }
329
330 return false;
331}
332
333bool
334ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect,
335 struct pnfs_block_extent *ret, bool rw)
336{
337 bool found = false;
338
339 spin_lock(&bl->bl_ext_lock);
340 if (!rw)
341 found = __ext_tree_lookup(&bl->bl_ext_ro, isect, ret);
342 if (!found)
343 found = __ext_tree_lookup(&bl->bl_ext_rw, isect, ret);
344 spin_unlock(&bl->bl_ext_lock);
345
346 return found;
347}
348
349int ext_tree_remove(struct pnfs_block_layout *bl, bool rw,
350 sector_t start, sector_t end)
351{
352 int err, err2;
353
354 spin_lock(&bl->bl_ext_lock);
355 err = __ext_tree_remove(&bl->bl_ext_ro, start, end);
356 if (rw) {
357 err2 = __ext_tree_remove(&bl->bl_ext_rw, start, end);
358 if (!err)
359 err = err2;
360 }
361 spin_unlock(&bl->bl_ext_lock);
362
363 return err;
364}
365
366static int
367ext_tree_split(struct rb_root *root, struct pnfs_block_extent *be,
368 sector_t split)
369{
370 struct pnfs_block_extent *new;
371 sector_t orig_len = be->be_length;
372
373 dprintk("%s: need split for 0x%lx:0x%lx at 0x%lx\n",
374 __func__, be->be_f_offset, ext_f_end(be), split);
375
376 new = kzalloc(sizeof(*new), GFP_ATOMIC);
377 if (!new)
378 return -ENOMEM;
379
380 be->be_length = split - be->be_f_offset;
381
382 new->be_f_offset = split;
383 if (be->be_state != PNFS_BLOCK_NONE_DATA)
384 new->be_v_offset = be->be_v_offset + be->be_length;
385 new->be_length = orig_len - be->be_length;
386 new->be_state = be->be_state;
387 new->be_tag = be->be_tag;
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700388 new->be_device = nfs4_get_deviceid(be->be_device);
Christoph Hellwig80672532014-09-10 08:23:34 -0700389
390 dprintk("%s: got 0x%lx:0x%lx!\n",
391 __func__, be->be_f_offset, ext_f_end(be));
392 dprintk("%s: got 0x%lx:0x%lx!\n",
393 __func__, new->be_f_offset, ext_f_end(new));
394
395 __ext_tree_insert(root, new, false);
396 return 0;
397}
398
399int
400ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
401 sector_t len)
402{
403 struct rb_root *root = &bl->bl_ext_rw;
404 sector_t end = start + len;
405 struct pnfs_block_extent *be;
406 int err = 0;
407
408 spin_lock(&bl->bl_ext_lock);
409 /*
410 * First remove all COW extents or holes from written to range.
411 */
412 err = __ext_tree_remove(&bl->bl_ext_ro, start, end);
413 if (err)
414 goto out;
415
416 /*
417 * Then mark all invalid extents in the range as written to.
418 */
419 for (be = __ext_tree_search(root, start); be; be = ext_tree_next(be)) {
420 if (be->be_f_offset >= end)
421 break;
422
423 if (be->be_state != PNFS_BLOCK_INVALID_DATA || be->be_tag)
424 continue;
425
426 if (be->be_f_offset < start) {
427 struct pnfs_block_extent *left = ext_tree_prev(be);
428
429 if (left && ext_can_merge(left, be)) {
430 sector_t diff = start - be->be_f_offset;
431
432 left->be_length += diff;
433
434 be->be_f_offset += diff;
435 be->be_v_offset += diff;
436 be->be_length -= diff;
437 } else {
438 err = ext_tree_split(root, be, start);
439 if (err)
440 goto out;
441 }
442 }
443
444 if (ext_f_end(be) > end) {
445 struct pnfs_block_extent *right = ext_tree_next(be);
446
447 if (right && ext_can_merge(be, right)) {
448 sector_t diff = end - be->be_f_offset;
449
450 be->be_length -= diff;
451
452 right->be_f_offset -= diff;
453 right->be_v_offset -= diff;
454 right->be_length += diff;
455 } else {
456 err = ext_tree_split(root, be, end);
457 if (err)
458 goto out;
459 }
460 }
461
462 if (be->be_f_offset >= start && ext_f_end(be) <= end) {
463 be->be_tag = EXTENT_WRITTEN;
464 be = ext_try_to_merge_left(root, be);
465 be = ext_try_to_merge_right(root, be);
466 }
467 }
468out:
469 spin_unlock(&bl->bl_ext_lock);
470 return err;
471}
472
473int
474ext_tree_encode_commit(struct pnfs_block_layout *bl, struct xdr_stream *xdr)
475{
476 struct pnfs_block_extent *be;
477 unsigned int count = 0;
478 __be32 *p, *xdr_start;
479 int ret = 0;
480
481 dprintk("%s enter\n", __func__);
482
483 xdr_start = xdr_reserve_space(xdr, 8);
484 if (!xdr_start)
485 return -ENOSPC;
486
487 spin_lock(&bl->bl_ext_lock);
488 for (be = ext_tree_first(&bl->bl_ext_rw); be; be = ext_tree_next(be)) {
489 if (be->be_state != PNFS_BLOCK_INVALID_DATA ||
490 be->be_tag != EXTENT_WRITTEN)
491 continue;
492
493 p = xdr_reserve_space(xdr, 7 * sizeof(__be32) +
494 NFS4_DEVICEID4_SIZE);
495 if (!p) {
496 printk("%s: out of space for extent list\n", __func__);
497 ret = -ENOSPC;
498 break;
499 }
500
Christoph Hellwig20d655d2014-09-02 21:28:00 -0700501 p = xdr_encode_opaque_fixed(p, be->be_device->deviceid.data,
Christoph Hellwig80672532014-09-10 08:23:34 -0700502 NFS4_DEVICEID4_SIZE);
503 p = xdr_encode_hyper(p, be->be_f_offset << SECTOR_SHIFT);
504 p = xdr_encode_hyper(p, be->be_length << SECTOR_SHIFT);
505 p = xdr_encode_hyper(p, 0LL);
506 *p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA);
507
508 be->be_tag = EXTENT_COMMITTING;
509 count++;
510 }
511 spin_unlock(&bl->bl_ext_lock);
512
513 xdr_start[0] = cpu_to_be32((xdr->p - xdr_start - 1) * 4);
514 xdr_start[1] = cpu_to_be32(count);
515
516 dprintk("%s found %i ranges\n", __func__, count);
517 return ret;
518}
519
520void
521ext_tree_mark_committed(struct pnfs_block_layout *bl, int status)
522{
523 struct rb_root *root = &bl->bl_ext_rw;
524 struct pnfs_block_extent *be;
525
526 dprintk("%s status %d\n", __func__, status);
527
528 spin_lock(&bl->bl_ext_lock);
529 for (be = ext_tree_first(root); be; be = ext_tree_next(be)) {
530 if (be->be_state != PNFS_BLOCK_INVALID_DATA ||
531 be->be_tag != EXTENT_COMMITTING)
532 continue;
533
534 if (status) {
535 /*
536 * Mark as written and try again.
537 *
538 * XXX: some real error handling here wouldn't hurt..
539 */
540 be->be_tag = EXTENT_WRITTEN;
541 } else {
542 be->be_state = PNFS_BLOCK_READWRITE_DATA;
543 be->be_tag = 0;
544 }
545
546 be = ext_try_to_merge_left(root, be);
547 be = ext_try_to_merge_right(root, be);
548 }
549 spin_unlock(&bl->bl_ext_lock);
550}