* Allocate a new ABD to point to offset off of sabd. It shares the underlying * buffer data with sabd. Use abd_put() to free. sabd must not be freed while * any derived ABDs exist. */
| 520 | * any derived ABDs exist. |
| 521 | */ |
| 522 | static abd_t * |
| 523 | abd_get_offset_impl(abd_t *sabd, size_t off, size_t size) |
| 524 | { |
| 525 | abd_t *abd = NULL; |
| 526 | |
| 527 | abd_verify(sabd); |
| 528 | ASSERT3U(off, <=, sabd->abd_size); |
| 529 | |
| 530 | if (abd_is_linear(sabd)) { |
| 531 | abd = abd_alloc_struct(0); |
| 532 | |
| 533 | /* |
| 534 | * Even if this buf is filesystem metadata, we only track that |
| 535 | * if we own the underlying data buffer, which is not true in |
| 536 | * this case. Therefore, we don't ever use ABD_FLAG_META here. |
| 537 | */ |
| 538 | abd->abd_flags = ABD_FLAG_LINEAR; |
| 539 | |
| 540 | ABD_LINEAR_BUF(abd) = (char *)ABD_LINEAR_BUF(sabd) + off; |
| 541 | } else if (abd_is_gang(sabd)) { |
| 542 | size_t left = size; |
| 543 | abd = abd_alloc_gang_abd(); |
| 544 | abd->abd_flags &= ~ABD_FLAG_OWNER; |
| 545 | for (abd_t *cabd = abd_gang_get_offset(sabd, &off); |
| 546 | cabd != NULL && left > 0; |
| 547 | cabd = list_next(&ABD_GANG(sabd).abd_gang_chain, cabd)) { |
| 548 | int csize = MIN(left, cabd->abd_size - off); |
| 549 | |
| 550 | abd_t *nabd = abd_get_offset_impl(cabd, off, csize); |
| 551 | abd_gang_add(abd, nabd, B_FALSE); |
| 552 | left -= csize; |
| 553 | off = 0; |
| 554 | } |
| 555 | ASSERT3U(left, ==, 0); |
| 556 | } else { |
| 557 | abd = abd_get_offset_scatter(sabd, off); |
| 558 | } |
| 559 | |
| 560 | abd->abd_size = size; |
| 561 | abd->abd_parent = sabd; |
| 562 | zfs_refcount_create(&abd->abd_children); |
| 563 | (void) zfs_refcount_add_many(&sabd->abd_children, abd->abd_size, abd); |
| 564 | return (abd); |
| 565 | } |
| 566 | |
| 567 | abd_t * |
| 568 | abd_get_offset(abd_t *sabd, size_t off) |
no test coverage detected