diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/block/rbd.c | 111 |
1 files changed, 97 insertions, 14 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index bec5a50c9890..73ed5f3a862d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c | |||
| @@ -4207,11 +4207,13 @@ static ssize_t rbd_parent_show(struct device *dev, | |||
| 4207 | 4207 | ||
| 4208 | count += sprintf(&buf[count], "%s" | 4208 | count += sprintf(&buf[count], "%s" |
| 4209 | "pool_id %llu\npool_name %s\n" | 4209 | "pool_id %llu\npool_name %s\n" |
| 4210 | "pool_ns %s\n" | ||
| 4210 | "image_id %s\nimage_name %s\n" | 4211 | "image_id %s\nimage_name %s\n" |
| 4211 | "snap_id %llu\nsnap_name %s\n" | 4212 | "snap_id %llu\nsnap_name %s\n" |
| 4212 | "overlap %llu\n", | 4213 | "overlap %llu\n", |
| 4213 | !count ? "" : "\n", /* first? */ | 4214 | !count ? "" : "\n", /* first? */ |
| 4214 | spec->pool_id, spec->pool_name, | 4215 | spec->pool_id, spec->pool_name, |
| 4216 | spec->pool_ns ?: "", | ||
| 4215 | spec->image_id, spec->image_name ?: "(unknown)", | 4217 | spec->image_id, spec->image_name ?: "(unknown)", |
| 4216 | spec->snap_id, spec->snap_name, | 4218 | spec->snap_id, spec->snap_name, |
| 4217 | rbd_dev->parent_overlap); | 4219 | rbd_dev->parent_overlap); |
| @@ -4586,15 +4588,92 @@ static int rbd_dev_v2_features(struct rbd_device *rbd_dev) | |||
| 4586 | 4588 | ||
| 4587 | struct parent_image_info { | 4589 | struct parent_image_info { |
| 4588 | u64 pool_id; | 4590 | u64 pool_id; |
| 4591 | const char *pool_ns; | ||
| 4589 | const char *image_id; | 4592 | const char *image_id; |
| 4590 | u64 snap_id; | 4593 | u64 snap_id; |
| 4591 | 4594 | ||
| 4595 | bool has_overlap; | ||
| 4592 | u64 overlap; | 4596 | u64 overlap; |
| 4593 | }; | 4597 | }; |
| 4594 | 4598 | ||
| 4595 | /* | 4599 | /* |
| 4596 | * The caller is responsible for @pii. | 4600 | * The caller is responsible for @pii. |
| 4597 | */ | 4601 | */ |
| 4602 | static int decode_parent_image_spec(void **p, void *end, | ||
| 4603 | struct parent_image_info *pii) | ||
| 4604 | { | ||
| 4605 | u8 struct_v; | ||
| 4606 | u32 struct_len; | ||
| 4607 | int ret; | ||
| 4608 | |||
| 4609 | ret = ceph_start_decoding(p, end, 1, "ParentImageSpec", | ||
| 4610 | &struct_v, &struct_len); | ||
| 4611 | if (ret) | ||
| 4612 | return ret; | ||
| 4613 | |||
| 4614 | ceph_decode_64_safe(p, end, pii->pool_id, e_inval); | ||
| 4615 | pii->pool_ns = ceph_extract_encoded_string(p, end, NULL, GFP_KERNEL); | ||
| 4616 | if (IS_ERR(pii->pool_ns)) { | ||
| 4617 | ret = PTR_ERR(pii->pool_ns); | ||
| 4618 | pii->pool_ns = NULL; | ||
| 4619 | return ret; | ||
| 4620 | } | ||
| 4621 | pii->image_id = ceph_extract_encoded_string(p, end, NULL, GFP_KERNEL); | ||
| 4622 | if (IS_ERR(pii->image_id)) { | ||
| 4623 | ret = PTR_ERR(pii->image_id); | ||
| 4624 | pii->image_id = NULL; | ||
| 4625 | return ret; | ||
| 4626 | } | ||
| 4627 | ceph_decode_64_safe(p, end, pii->snap_id, e_inval); | ||
| 4628 | return 0; | ||
| 4629 | |||
| 4630 | e_inval: | ||
| 4631 | return -EINVAL; | ||
| 4632 | } | ||
| 4633 | |||
| 4634 | static int __get_parent_info(struct rbd_device *rbd_dev, | ||
| 4635 | struct page *req_page, | ||
| 4636 | struct page *reply_page, | ||
| 4637 | struct parent_image_info *pii) | ||
| 4638 | { | ||
| 4639 | struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; | ||
| 4640 | size_t reply_len = PAGE_SIZE; | ||
| 4641 | void *p, *end; | ||
| 4642 | int ret; | ||
| 4643 | |||
| 4644 | ret = ceph_osdc_call(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc, | ||
| 4645 | "rbd", "parent_get", CEPH_OSD_FLAG_READ, | ||
| 4646 | req_page, sizeof(u64), reply_page, &reply_len); | ||
| 4647 | if (ret) | ||
| 4648 | return ret == -EOPNOTSUPP ? 1 : ret; | ||
| 4649 | |||
| 4650 | p = page_address(reply_page); | ||
| 4651 | end = p + reply_len; | ||
| 4652 | ret = decode_parent_image_spec(&p, end, pii); | ||
| 4653 | if (ret) | ||
| 4654 | return ret; | ||
| 4655 | |||
| 4656 | ret = ceph_osdc_call(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc, | ||
| 4657 | "rbd", "parent_overlap_get", CEPH_OSD_FLAG_READ, | ||
| 4658 | req_page, sizeof(u64), reply_page, &reply_len); | ||
| 4659 | if (ret) | ||
| 4660 | return ret; | ||
| 4661 | |||
| 4662 | p = page_address(reply_page); | ||
| 4663 | end = p + reply_len; | ||
| 4664 | ceph_decode_8_safe(&p, end, pii->has_overlap, e_inval); | ||
| 4665 | if (pii->has_overlap) | ||
| 4666 | ceph_decode_64_safe(&p, end, pii->overlap, e_inval); | ||
| 4667 | |||
| 4668 | return 0; | ||
| 4669 | |||
| 4670 | e_inval: | ||
| 4671 | return -EINVAL; | ||
| 4672 | } | ||
| 4673 | |||
| 4674 | /* | ||
| 4675 | * The caller is responsible for @pii. | ||
| 4676 | */ | ||
| 4598 | static int __get_parent_info_legacy(struct rbd_device *rbd_dev, | 4677 | static int __get_parent_info_legacy(struct rbd_device *rbd_dev, |
| 4599 | struct page *req_page, | 4678 | struct page *req_page, |
| 4600 | struct page *reply_page, | 4679 | struct page *reply_page, |
| @@ -4621,6 +4700,7 @@ static int __get_parent_info_legacy(struct rbd_device *rbd_dev, | |||
| 4621 | return ret; | 4700 | return ret; |
| 4622 | } | 4701 | } |
| 4623 | ceph_decode_64_safe(&p, end, pii->snap_id, e_inval); | 4702 | ceph_decode_64_safe(&p, end, pii->snap_id, e_inval); |
| 4703 | pii->has_overlap = true; | ||
| 4624 | ceph_decode_64_safe(&p, end, pii->overlap, e_inval); | 4704 | ceph_decode_64_safe(&p, end, pii->overlap, e_inval); |
| 4625 | 4705 | ||
| 4626 | return 0; | 4706 | return 0; |
| @@ -4648,7 +4728,10 @@ static int get_parent_info(struct rbd_device *rbd_dev, | |||
| 4648 | 4728 | ||
| 4649 | p = page_address(req_page); | 4729 | p = page_address(req_page); |
| 4650 | ceph_encode_64(&p, rbd_dev->spec->snap_id); | 4730 | ceph_encode_64(&p, rbd_dev->spec->snap_id); |
| 4651 | ret = __get_parent_info_legacy(rbd_dev, req_page, reply_page, pii); | 4731 | ret = __get_parent_info(rbd_dev, req_page, reply_page, pii); |
| 4732 | if (ret > 0) | ||
| 4733 | ret = __get_parent_info_legacy(rbd_dev, req_page, reply_page, | ||
| 4734 | pii); | ||
| 4652 | 4735 | ||
| 4653 | __free_page(req_page); | 4736 | __free_page(req_page); |
| 4654 | __free_page(reply_page); | 4737 | __free_page(reply_page); |
| @@ -4669,10 +4752,11 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4669 | if (ret) | 4752 | if (ret) |
| 4670 | goto out_err; | 4753 | goto out_err; |
| 4671 | 4754 | ||
| 4672 | dout("%s pool_id %llu image_id %s snap_id %llu overlap %llu\n", | 4755 | dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n", |
| 4673 | __func__, pii.pool_id, pii.image_id, pii.snap_id, pii.overlap); | 4756 | __func__, pii.pool_id, pii.pool_ns, pii.image_id, pii.snap_id, |
| 4757 | pii.has_overlap, pii.overlap); | ||
| 4674 | 4758 | ||
| 4675 | if (pii.pool_id == CEPH_NOPOOL) { | 4759 | if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) { |
| 4676 | /* | 4760 | /* |
| 4677 | * Either the parent never existed, or we have | 4761 | * Either the parent never existed, or we have |
| 4678 | * record of it but the image got flattened so it no | 4762 | * record of it but the image got flattened so it no |
| @@ -4681,6 +4765,10 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4681 | * overlap to 0. The effect of this is that all new | 4765 | * overlap to 0. The effect of this is that all new |
| 4682 | * requests will be treated as if the image had no | 4766 | * requests will be treated as if the image had no |
| 4683 | * parent. | 4767 | * parent. |
| 4768 | * | ||
| 4769 | * If !pii.has_overlap, the parent image spec is not | ||
| 4770 | * applicable. It's there to avoid duplication in each | ||
| 4771 | * snapshot record. | ||
| 4684 | */ | 4772 | */ |
| 4685 | if (rbd_dev->parent_overlap) { | 4773 | if (rbd_dev->parent_overlap) { |
| 4686 | rbd_dev->parent_overlap = 0; | 4774 | rbd_dev->parent_overlap = 0; |
| @@ -4708,20 +4796,14 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4708 | */ | 4796 | */ |
| 4709 | if (!rbd_dev->parent_spec) { | 4797 | if (!rbd_dev->parent_spec) { |
| 4710 | parent_spec->pool_id = pii.pool_id; | 4798 | parent_spec->pool_id = pii.pool_id; |
| 4799 | if (pii.pool_ns && *pii.pool_ns) { | ||
| 4800 | parent_spec->pool_ns = pii.pool_ns; | ||
| 4801 | pii.pool_ns = NULL; | ||
| 4802 | } | ||
| 4711 | parent_spec->image_id = pii.image_id; | 4803 | parent_spec->image_id = pii.image_id; |
| 4712 | pii.image_id = NULL; | 4804 | pii.image_id = NULL; |
| 4713 | parent_spec->snap_id = pii.snap_id; | 4805 | parent_spec->snap_id = pii.snap_id; |
| 4714 | 4806 | ||
| 4715 | /* TODO: support cloning across namespaces */ | ||
| 4716 | if (rbd_dev->spec->pool_ns) { | ||
| 4717 | parent_spec->pool_ns = kstrdup(rbd_dev->spec->pool_ns, | ||
| 4718 | GFP_KERNEL); | ||
| 4719 | if (!parent_spec->pool_ns) { | ||
| 4720 | ret = -ENOMEM; | ||
| 4721 | goto out_err; | ||
| 4722 | } | ||
| 4723 | } | ||
| 4724 | |||
| 4725 | rbd_dev->parent_spec = parent_spec; | 4807 | rbd_dev->parent_spec = parent_spec; |
| 4726 | parent_spec = NULL; /* rbd_dev now owns this */ | 4808 | parent_spec = NULL; /* rbd_dev now owns this */ |
| 4727 | } | 4809 | } |
| @@ -4746,6 +4828,7 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4746 | out: | 4828 | out: |
| 4747 | ret = 0; | 4829 | ret = 0; |
| 4748 | out_err: | 4830 | out_err: |
| 4831 | kfree(pii.pool_ns); | ||
| 4749 | kfree(pii.image_id); | 4832 | kfree(pii.image_id); |
| 4750 | rbd_spec_put(parent_spec); | 4833 | rbd_spec_put(parent_spec); |
| 4751 | return ret; | 4834 | return ret; |
