diff options
| -rw-r--r-- | drivers/block/rbd.c | 235 | ||||
| -rw-r--r-- | fs/ceph/super.c | 16 |
2 files changed, 189 insertions, 62 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7915f3b03736..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); |
| @@ -4584,47 +4586,177 @@ static int rbd_dev_v2_features(struct rbd_device *rbd_dev) | |||
| 4584 | &rbd_dev->header.features); | 4586 | &rbd_dev->header.features); |
| 4585 | } | 4587 | } |
| 4586 | 4588 | ||
| 4589 | struct parent_image_info { | ||
| 4590 | u64 pool_id; | ||
| 4591 | const char *pool_ns; | ||
| 4592 | const char *image_id; | ||
| 4593 | u64 snap_id; | ||
| 4594 | |||
| 4595 | bool has_overlap; | ||
| 4596 | u64 overlap; | ||
| 4597 | }; | ||
| 4598 | |||
| 4599 | /* | ||
| 4600 | * The caller is responsible for @pii. | ||
| 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 | */ | ||
| 4677 | static int __get_parent_info_legacy(struct rbd_device *rbd_dev, | ||
| 4678 | struct page *req_page, | ||
| 4679 | struct page *reply_page, | ||
| 4680 | struct parent_image_info *pii) | ||
| 4681 | { | ||
| 4682 | struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; | ||
| 4683 | size_t reply_len = PAGE_SIZE; | ||
| 4684 | void *p, *end; | ||
| 4685 | int ret; | ||
| 4686 | |||
| 4687 | ret = ceph_osdc_call(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc, | ||
| 4688 | "rbd", "get_parent", CEPH_OSD_FLAG_READ, | ||
| 4689 | req_page, sizeof(u64), reply_page, &reply_len); | ||
| 4690 | if (ret) | ||
| 4691 | return ret; | ||
| 4692 | |||
| 4693 | p = page_address(reply_page); | ||
| 4694 | end = p + reply_len; | ||
| 4695 | ceph_decode_64_safe(&p, end, pii->pool_id, e_inval); | ||
| 4696 | pii->image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); | ||
| 4697 | if (IS_ERR(pii->image_id)) { | ||
| 4698 | ret = PTR_ERR(pii->image_id); | ||
| 4699 | pii->image_id = NULL; | ||
| 4700 | return ret; | ||
| 4701 | } | ||
| 4702 | ceph_decode_64_safe(&p, end, pii->snap_id, e_inval); | ||
| 4703 | pii->has_overlap = true; | ||
| 4704 | ceph_decode_64_safe(&p, end, pii->overlap, e_inval); | ||
| 4705 | |||
| 4706 | return 0; | ||
| 4707 | |||
| 4708 | e_inval: | ||
| 4709 | return -EINVAL; | ||
| 4710 | } | ||
| 4711 | |||
| 4712 | static int get_parent_info(struct rbd_device *rbd_dev, | ||
| 4713 | struct parent_image_info *pii) | ||
| 4714 | { | ||
| 4715 | struct page *req_page, *reply_page; | ||
| 4716 | void *p; | ||
| 4717 | int ret; | ||
| 4718 | |||
| 4719 | req_page = alloc_page(GFP_KERNEL); | ||
| 4720 | if (!req_page) | ||
| 4721 | return -ENOMEM; | ||
| 4722 | |||
| 4723 | reply_page = alloc_page(GFP_KERNEL); | ||
| 4724 | if (!reply_page) { | ||
| 4725 | __free_page(req_page); | ||
| 4726 | return -ENOMEM; | ||
| 4727 | } | ||
| 4728 | |||
| 4729 | p = page_address(req_page); | ||
| 4730 | ceph_encode_64(&p, rbd_dev->spec->snap_id); | ||
| 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); | ||
| 4735 | |||
| 4736 | __free_page(req_page); | ||
| 4737 | __free_page(reply_page); | ||
| 4738 | return ret; | ||
| 4739 | } | ||
| 4740 | |||
| 4587 | static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | 4741 | static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) |
| 4588 | { | 4742 | { |
| 4589 | struct rbd_spec *parent_spec; | 4743 | struct rbd_spec *parent_spec; |
| 4590 | size_t size; | 4744 | struct parent_image_info pii = { 0 }; |
| 4591 | void *reply_buf = NULL; | ||
| 4592 | __le64 snapid; | ||
| 4593 | void *p; | ||
| 4594 | void *end; | ||
| 4595 | u64 pool_id; | ||
| 4596 | char *image_id; | ||
| 4597 | u64 snap_id; | ||
| 4598 | u64 overlap; | ||
| 4599 | int ret; | 4745 | int ret; |
| 4600 | 4746 | ||
| 4601 | parent_spec = rbd_spec_alloc(); | 4747 | parent_spec = rbd_spec_alloc(); |
| 4602 | if (!parent_spec) | 4748 | if (!parent_spec) |
| 4603 | return -ENOMEM; | 4749 | return -ENOMEM; |
| 4604 | 4750 | ||
| 4605 | size = sizeof (__le64) + /* pool_id */ | 4751 | ret = get_parent_info(rbd_dev, &pii); |
| 4606 | sizeof (__le32) + RBD_IMAGE_ID_LEN_MAX + /* image_id */ | 4752 | if (ret) |
| 4607 | sizeof (__le64) + /* snap_id */ | ||
| 4608 | sizeof (__le64); /* overlap */ | ||
| 4609 | reply_buf = kmalloc(size, GFP_KERNEL); | ||
| 4610 | if (!reply_buf) { | ||
| 4611 | ret = -ENOMEM; | ||
| 4612 | goto out_err; | 4753 | goto out_err; |
| 4613 | } | ||
| 4614 | 4754 | ||
| 4615 | snapid = cpu_to_le64(rbd_dev->spec->snap_id); | 4755 | dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n", |
| 4616 | ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, | 4756 | __func__, pii.pool_id, pii.pool_ns, pii.image_id, pii.snap_id, |
| 4617 | &rbd_dev->header_oloc, "get_parent", | 4757 | pii.has_overlap, pii.overlap); |
| 4618 | &snapid, sizeof(snapid), reply_buf, size); | ||
| 4619 | dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); | ||
| 4620 | if (ret < 0) | ||
| 4621 | goto out_err; | ||
| 4622 | 4758 | ||
| 4623 | p = reply_buf; | 4759 | if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) { |
| 4624 | end = reply_buf + ret; | ||
| 4625 | ret = -ERANGE; | ||
| 4626 | ceph_decode_64_safe(&p, end, pool_id, out_err); | ||
| 4627 | if (pool_id == CEPH_NOPOOL) { | ||
| 4628 | /* | 4760 | /* |
| 4629 | * Either the parent never existed, or we have | 4761 | * Either the parent never existed, or we have |
| 4630 | * record of it but the image got flattened so it no | 4762 | * record of it but the image got flattened so it no |
| @@ -4633,6 +4765,10 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4633 | * overlap to 0. The effect of this is that all new | 4765 | * overlap to 0. The effect of this is that all new |
| 4634 | * requests will be treated as if the image had no | 4766 | * requests will be treated as if the image had no |
| 4635 | * 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. | ||
| 4636 | */ | 4772 | */ |
| 4637 | if (rbd_dev->parent_overlap) { | 4773 | if (rbd_dev->parent_overlap) { |
| 4638 | rbd_dev->parent_overlap = 0; | 4774 | rbd_dev->parent_overlap = 0; |
| @@ -4647,51 +4783,36 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4647 | /* The ceph file layout needs to fit pool id in 32 bits */ | 4783 | /* The ceph file layout needs to fit pool id in 32 bits */ |
| 4648 | 4784 | ||
| 4649 | ret = -EIO; | 4785 | ret = -EIO; |
| 4650 | if (pool_id > (u64)U32_MAX) { | 4786 | if (pii.pool_id > (u64)U32_MAX) { |
| 4651 | rbd_warn(NULL, "parent pool id too large (%llu > %u)", | 4787 | rbd_warn(NULL, "parent pool id too large (%llu > %u)", |
| 4652 | (unsigned long long)pool_id, U32_MAX); | 4788 | (unsigned long long)pii.pool_id, U32_MAX); |
| 4653 | goto out_err; | 4789 | goto out_err; |
| 4654 | } | 4790 | } |
| 4655 | 4791 | ||
| 4656 | image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); | ||
| 4657 | if (IS_ERR(image_id)) { | ||
| 4658 | ret = PTR_ERR(image_id); | ||
| 4659 | goto out_err; | ||
| 4660 | } | ||
| 4661 | ceph_decode_64_safe(&p, end, snap_id, out_err); | ||
| 4662 | ceph_decode_64_safe(&p, end, overlap, out_err); | ||
| 4663 | |||
| 4664 | /* | 4792 | /* |
| 4665 | * The parent won't change (except when the clone is | 4793 | * The parent won't change (except when the clone is |
| 4666 | * flattened, already handled that). So we only need to | 4794 | * flattened, already handled that). So we only need to |
| 4667 | * record the parent spec we have not already done so. | 4795 | * record the parent spec we have not already done so. |
| 4668 | */ | 4796 | */ |
| 4669 | if (!rbd_dev->parent_spec) { | 4797 | if (!rbd_dev->parent_spec) { |
| 4670 | parent_spec->pool_id = pool_id; | 4798 | parent_spec->pool_id = pii.pool_id; |
| 4671 | parent_spec->image_id = image_id; | 4799 | if (pii.pool_ns && *pii.pool_ns) { |
| 4672 | parent_spec->snap_id = snap_id; | 4800 | parent_spec->pool_ns = pii.pool_ns; |
| 4673 | 4801 | pii.pool_ns = NULL; | |
| 4674 | /* TODO: support cloning across namespaces */ | ||
| 4675 | if (rbd_dev->spec->pool_ns) { | ||
| 4676 | parent_spec->pool_ns = kstrdup(rbd_dev->spec->pool_ns, | ||
| 4677 | GFP_KERNEL); | ||
| 4678 | if (!parent_spec->pool_ns) { | ||
| 4679 | ret = -ENOMEM; | ||
| 4680 | goto out_err; | ||
| 4681 | } | ||
| 4682 | } | 4802 | } |
| 4803 | parent_spec->image_id = pii.image_id; | ||
| 4804 | pii.image_id = NULL; | ||
| 4805 | parent_spec->snap_id = pii.snap_id; | ||
| 4683 | 4806 | ||
| 4684 | rbd_dev->parent_spec = parent_spec; | 4807 | rbd_dev->parent_spec = parent_spec; |
| 4685 | parent_spec = NULL; /* rbd_dev now owns this */ | 4808 | parent_spec = NULL; /* rbd_dev now owns this */ |
| 4686 | } else { | ||
| 4687 | kfree(image_id); | ||
| 4688 | } | 4809 | } |
| 4689 | 4810 | ||
| 4690 | /* | 4811 | /* |
| 4691 | * We always update the parent overlap. If it's zero we issue | 4812 | * We always update the parent overlap. If it's zero we issue |
| 4692 | * a warning, as we will proceed as if there was no parent. | 4813 | * a warning, as we will proceed as if there was no parent. |
| 4693 | */ | 4814 | */ |
| 4694 | if (!overlap) { | 4815 | if (!pii.overlap) { |
| 4695 | if (parent_spec) { | 4816 | if (parent_spec) { |
| 4696 | /* refresh, careful to warn just once */ | 4817 | /* refresh, careful to warn just once */ |
| 4697 | if (rbd_dev->parent_overlap) | 4818 | if (rbd_dev->parent_overlap) |
| @@ -4702,14 +4823,14 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
| 4702 | rbd_warn(rbd_dev, "clone is standalone (overlap 0)"); | 4823 | rbd_warn(rbd_dev, "clone is standalone (overlap 0)"); |
| 4703 | } | 4824 | } |
| 4704 | } | 4825 | } |
| 4705 | rbd_dev->parent_overlap = overlap; | 4826 | rbd_dev->parent_overlap = pii.overlap; |
| 4706 | 4827 | ||
| 4707 | out: | 4828 | out: |
| 4708 | ret = 0; | 4829 | ret = 0; |
| 4709 | out_err: | 4830 | out_err: |
| 4710 | kfree(reply_buf); | 4831 | kfree(pii.pool_ns); |
| 4832 | kfree(pii.image_id); | ||
| 4711 | rbd_spec_put(parent_spec); | 4833 | rbd_spec_put(parent_spec); |
| 4712 | |||
| 4713 | return ret; | 4834 | return ret; |
| 4714 | } | 4835 | } |
| 4715 | 4836 | ||
diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 43ca3b763875..eab1359d0553 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c | |||
| @@ -602,6 +602,8 @@ static int extra_mon_dispatch(struct ceph_client *client, struct ceph_msg *msg) | |||
| 602 | 602 | ||
| 603 | /* | 603 | /* |
| 604 | * create a new fs client | 604 | * create a new fs client |
| 605 | * | ||
| 606 | * Success or not, this function consumes @fsopt and @opt. | ||
| 605 | */ | 607 | */ |
| 606 | static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt, | 608 | static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt, |
| 607 | struct ceph_options *opt) | 609 | struct ceph_options *opt) |
| @@ -609,17 +611,20 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt, | |||
| 609 | struct ceph_fs_client *fsc; | 611 | struct ceph_fs_client *fsc; |
| 610 | int page_count; | 612 | int page_count; |
| 611 | size_t size; | 613 | size_t size; |
| 612 | int err = -ENOMEM; | 614 | int err; |
| 613 | 615 | ||
| 614 | fsc = kzalloc(sizeof(*fsc), GFP_KERNEL); | 616 | fsc = kzalloc(sizeof(*fsc), GFP_KERNEL); |
| 615 | if (!fsc) | 617 | if (!fsc) { |
| 616 | return ERR_PTR(-ENOMEM); | 618 | err = -ENOMEM; |
| 619 | goto fail; | ||
| 620 | } | ||
| 617 | 621 | ||
| 618 | fsc->client = ceph_create_client(opt, fsc); | 622 | fsc->client = ceph_create_client(opt, fsc); |
| 619 | if (IS_ERR(fsc->client)) { | 623 | if (IS_ERR(fsc->client)) { |
| 620 | err = PTR_ERR(fsc->client); | 624 | err = PTR_ERR(fsc->client); |
| 621 | goto fail; | 625 | goto fail; |
| 622 | } | 626 | } |
| 627 | opt = NULL; /* fsc->client now owns this */ | ||
| 623 | 628 | ||
| 624 | fsc->client->extra_mon_dispatch = extra_mon_dispatch; | 629 | fsc->client->extra_mon_dispatch = extra_mon_dispatch; |
| 625 | fsc->client->osdc.abort_on_full = true; | 630 | fsc->client->osdc.abort_on_full = true; |
| @@ -677,6 +682,9 @@ fail_client: | |||
| 677 | ceph_destroy_client(fsc->client); | 682 | ceph_destroy_client(fsc->client); |
| 678 | fail: | 683 | fail: |
| 679 | kfree(fsc); | 684 | kfree(fsc); |
| 685 | if (opt) | ||
| 686 | ceph_destroy_options(opt); | ||
| 687 | destroy_mount_options(fsopt); | ||
| 680 | return ERR_PTR(err); | 688 | return ERR_PTR(err); |
| 681 | } | 689 | } |
| 682 | 690 | ||
| @@ -1042,8 +1050,6 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type, | |||
| 1042 | fsc = create_fs_client(fsopt, opt); | 1050 | fsc = create_fs_client(fsopt, opt); |
| 1043 | if (IS_ERR(fsc)) { | 1051 | if (IS_ERR(fsc)) { |
| 1044 | res = ERR_CAST(fsc); | 1052 | res = ERR_CAST(fsc); |
| 1045 | destroy_mount_options(fsopt); | ||
| 1046 | ceph_destroy_options(opt); | ||
| 1047 | goto out_final; | 1053 | goto out_final; |
| 1048 | } | 1054 | } |
| 1049 | 1055 | ||
