162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014-2016 Christoph Hellwig. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/sunrpc/svc.h> 662306a36Sopenharmony_ci#include <linux/blkdev.h> 762306a36Sopenharmony_ci#include <linux/nfs4.h> 862306a36Sopenharmony_ci#include <linux/nfs_fs.h> 962306a36Sopenharmony_ci#include <linux/nfs_xdr.h> 1062306a36Sopenharmony_ci#include <linux/pr.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "blocklayout.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_PNFS_LD 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic void 1762306a36Sopenharmony_cibl_free_device(struct pnfs_block_dev *dev) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci if (dev->nr_children) { 2062306a36Sopenharmony_ci int i; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci for (i = 0; i < dev->nr_children; i++) 2362306a36Sopenharmony_ci bl_free_device(&dev->children[i]); 2462306a36Sopenharmony_ci kfree(dev->children); 2562306a36Sopenharmony_ci } else { 2662306a36Sopenharmony_ci if (dev->pr_registered) { 2762306a36Sopenharmony_ci const struct pr_ops *ops = 2862306a36Sopenharmony_ci dev->bdev->bd_disk->fops->pr_ops; 2962306a36Sopenharmony_ci int error; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci error = ops->pr_register(dev->bdev, dev->pr_key, 0, 3262306a36Sopenharmony_ci false); 3362306a36Sopenharmony_ci if (error) 3462306a36Sopenharmony_ci pr_err("failed to unregister PR key.\n"); 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (dev->bdev) 3862306a36Sopenharmony_ci blkdev_put(dev->bdev, NULL); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_civoid 4362306a36Sopenharmony_cibl_free_deviceid_node(struct nfs4_deviceid_node *d) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct pnfs_block_dev *dev = 4662306a36Sopenharmony_ci container_of(d, struct pnfs_block_dev, node); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci bl_free_device(dev); 4962306a36Sopenharmony_ci kfree_rcu(dev, node.rcu); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int 5362306a36Sopenharmony_cinfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci __be32 *p; 5662306a36Sopenharmony_ci int i; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 4); 5962306a36Sopenharmony_ci if (!p) 6062306a36Sopenharmony_ci return -EIO; 6162306a36Sopenharmony_ci b->type = be32_to_cpup(p++); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci switch (b->type) { 6462306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_SIMPLE: 6562306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 4); 6662306a36Sopenharmony_ci if (!p) 6762306a36Sopenharmony_ci return -EIO; 6862306a36Sopenharmony_ci b->simple.nr_sigs = be32_to_cpup(p++); 6962306a36Sopenharmony_ci if (!b->simple.nr_sigs || b->simple.nr_sigs > PNFS_BLOCK_MAX_UUIDS) { 7062306a36Sopenharmony_ci dprintk("Bad signature count: %d\n", b->simple.nr_sigs); 7162306a36Sopenharmony_ci return -EIO; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci b->simple.len = 4 + 4; 7562306a36Sopenharmony_ci for (i = 0; i < b->simple.nr_sigs; i++) { 7662306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 8 + 4); 7762306a36Sopenharmony_ci if (!p) 7862306a36Sopenharmony_ci return -EIO; 7962306a36Sopenharmony_ci p = xdr_decode_hyper(p, &b->simple.sigs[i].offset); 8062306a36Sopenharmony_ci b->simple.sigs[i].sig_len = be32_to_cpup(p++); 8162306a36Sopenharmony_ci if (b->simple.sigs[i].sig_len > PNFS_BLOCK_UUID_LEN) { 8262306a36Sopenharmony_ci pr_info("signature too long: %d\n", 8362306a36Sopenharmony_ci b->simple.sigs[i].sig_len); 8462306a36Sopenharmony_ci return -EIO; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci p = xdr_inline_decode(xdr, b->simple.sigs[i].sig_len); 8862306a36Sopenharmony_ci if (!p) 8962306a36Sopenharmony_ci return -EIO; 9062306a36Sopenharmony_ci memcpy(&b->simple.sigs[i].sig, p, 9162306a36Sopenharmony_ci b->simple.sigs[i].sig_len); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci b->simple.len += 8 + 4 + \ 9462306a36Sopenharmony_ci (XDR_QUADLEN(b->simple.sigs[i].sig_len) << 2); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_SLICE: 9862306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 8 + 8 + 4); 9962306a36Sopenharmony_ci if (!p) 10062306a36Sopenharmony_ci return -EIO; 10162306a36Sopenharmony_ci p = xdr_decode_hyper(p, &b->slice.start); 10262306a36Sopenharmony_ci p = xdr_decode_hyper(p, &b->slice.len); 10362306a36Sopenharmony_ci b->slice.volume = be32_to_cpup(p++); 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_CONCAT: 10662306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 4); 10762306a36Sopenharmony_ci if (!p) 10862306a36Sopenharmony_ci return -EIO; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci b->concat.volumes_count = be32_to_cpup(p++); 11162306a36Sopenharmony_ci if (b->concat.volumes_count > PNFS_BLOCK_MAX_DEVICES) { 11262306a36Sopenharmony_ci dprintk("Too many volumes: %d\n", b->concat.volumes_count); 11362306a36Sopenharmony_ci return -EIO; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci p = xdr_inline_decode(xdr, b->concat.volumes_count * 4); 11762306a36Sopenharmony_ci if (!p) 11862306a36Sopenharmony_ci return -EIO; 11962306a36Sopenharmony_ci for (i = 0; i < b->concat.volumes_count; i++) 12062306a36Sopenharmony_ci b->concat.volumes[i] = be32_to_cpup(p++); 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_STRIPE: 12362306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 8 + 4); 12462306a36Sopenharmony_ci if (!p) 12562306a36Sopenharmony_ci return -EIO; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci p = xdr_decode_hyper(p, &b->stripe.chunk_size); 12862306a36Sopenharmony_ci b->stripe.volumes_count = be32_to_cpup(p++); 12962306a36Sopenharmony_ci if (b->stripe.volumes_count > PNFS_BLOCK_MAX_DEVICES) { 13062306a36Sopenharmony_ci dprintk("Too many volumes: %d\n", b->stripe.volumes_count); 13162306a36Sopenharmony_ci return -EIO; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci p = xdr_inline_decode(xdr, b->stripe.volumes_count * 4); 13562306a36Sopenharmony_ci if (!p) 13662306a36Sopenharmony_ci return -EIO; 13762306a36Sopenharmony_ci for (i = 0; i < b->stripe.volumes_count; i++) 13862306a36Sopenharmony_ci b->stripe.volumes[i] = be32_to_cpup(p++); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_SCSI: 14162306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 4 + 4 + 4); 14262306a36Sopenharmony_ci if (!p) 14362306a36Sopenharmony_ci return -EIO; 14462306a36Sopenharmony_ci b->scsi.code_set = be32_to_cpup(p++); 14562306a36Sopenharmony_ci b->scsi.designator_type = be32_to_cpup(p++); 14662306a36Sopenharmony_ci b->scsi.designator_len = be32_to_cpup(p++); 14762306a36Sopenharmony_ci p = xdr_inline_decode(xdr, b->scsi.designator_len); 14862306a36Sopenharmony_ci if (!p) 14962306a36Sopenharmony_ci return -EIO; 15062306a36Sopenharmony_ci if (b->scsi.designator_len > 256) 15162306a36Sopenharmony_ci return -EIO; 15262306a36Sopenharmony_ci memcpy(&b->scsi.designator, p, b->scsi.designator_len); 15362306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 8); 15462306a36Sopenharmony_ci if (!p) 15562306a36Sopenharmony_ci return -EIO; 15662306a36Sopenharmony_ci p = xdr_decode_hyper(p, &b->scsi.pr_key); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci dprintk("unknown volume type!\n"); 16062306a36Sopenharmony_ci return -EIO; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic bool bl_map_simple(struct pnfs_block_dev *dev, u64 offset, 16762306a36Sopenharmony_ci struct pnfs_block_dev_map *map) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci map->start = dev->start; 17062306a36Sopenharmony_ci map->len = dev->len; 17162306a36Sopenharmony_ci map->disk_offset = dev->disk_offset; 17262306a36Sopenharmony_ci map->bdev = dev->bdev; 17362306a36Sopenharmony_ci return true; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic bool bl_map_concat(struct pnfs_block_dev *dev, u64 offset, 17762306a36Sopenharmony_ci struct pnfs_block_dev_map *map) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (i = 0; i < dev->nr_children; i++) { 18262306a36Sopenharmony_ci struct pnfs_block_dev *child = &dev->children[i]; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (child->start > offset || 18562306a36Sopenharmony_ci child->start + child->len <= offset) 18662306a36Sopenharmony_ci continue; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci child->map(child, offset - child->start, map); 18962306a36Sopenharmony_ci return true; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci dprintk("%s: ran off loop!\n", __func__); 19362306a36Sopenharmony_ci return false; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic bool bl_map_stripe(struct pnfs_block_dev *dev, u64 offset, 19762306a36Sopenharmony_ci struct pnfs_block_dev_map *map) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct pnfs_block_dev *child; 20062306a36Sopenharmony_ci u64 chunk; 20162306a36Sopenharmony_ci u32 chunk_idx; 20262306a36Sopenharmony_ci u64 disk_offset; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci chunk = div_u64(offset, dev->chunk_size); 20562306a36Sopenharmony_ci div_u64_rem(chunk, dev->nr_children, &chunk_idx); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (chunk_idx >= dev->nr_children) { 20862306a36Sopenharmony_ci dprintk("%s: invalid chunk idx %d (%lld/%lld)\n", 20962306a36Sopenharmony_ci __func__, chunk_idx, offset, dev->chunk_size); 21062306a36Sopenharmony_ci /* error, should not happen */ 21162306a36Sopenharmony_ci return false; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* truncate offset to the beginning of the stripe */ 21562306a36Sopenharmony_ci offset = chunk * dev->chunk_size; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* disk offset of the stripe */ 21862306a36Sopenharmony_ci disk_offset = div_u64(offset, dev->nr_children); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci child = &dev->children[chunk_idx]; 22162306a36Sopenharmony_ci child->map(child, disk_offset, map); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci map->start += offset; 22462306a36Sopenharmony_ci map->disk_offset += disk_offset; 22562306a36Sopenharmony_ci map->len = dev->chunk_size; 22662306a36Sopenharmony_ci return true; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int 23062306a36Sopenharmony_cibl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d, 23162306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int 23562306a36Sopenharmony_cibl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d, 23662306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 23962306a36Sopenharmony_ci struct block_device *bdev; 24062306a36Sopenharmony_ci dev_t dev; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci dev = bl_resolve_deviceid(server, v, gfp_mask); 24362306a36Sopenharmony_ci if (!dev) 24462306a36Sopenharmony_ci return -EIO; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci bdev = blkdev_get_by_dev(dev, BLK_OPEN_READ | BLK_OPEN_WRITE, NULL, 24762306a36Sopenharmony_ci NULL); 24862306a36Sopenharmony_ci if (IS_ERR(bdev)) { 24962306a36Sopenharmony_ci printk(KERN_WARNING "pNFS: failed to open device %d:%d (%ld)\n", 25062306a36Sopenharmony_ci MAJOR(dev), MINOR(dev), PTR_ERR(bdev)); 25162306a36Sopenharmony_ci return PTR_ERR(bdev); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci d->bdev = bdev; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci d->len = bdev_nr_bytes(d->bdev); 25762306a36Sopenharmony_ci d->map = bl_map_simple; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci printk(KERN_INFO "pNFS: using block device %s\n", 26062306a36Sopenharmony_ci d->bdev->bd_disk->disk_name); 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic bool 26562306a36Sopenharmony_cibl_validate_designator(struct pnfs_block_volume *v) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci switch (v->scsi.designator_type) { 26862306a36Sopenharmony_ci case PS_DESIGNATOR_EUI64: 26962306a36Sopenharmony_ci if (v->scsi.code_set != PS_CODE_SET_BINARY) 27062306a36Sopenharmony_ci return false; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (v->scsi.designator_len != 8 && 27362306a36Sopenharmony_ci v->scsi.designator_len != 10 && 27462306a36Sopenharmony_ci v->scsi.designator_len != 16) 27562306a36Sopenharmony_ci return false; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return true; 27862306a36Sopenharmony_ci case PS_DESIGNATOR_NAA: 27962306a36Sopenharmony_ci if (v->scsi.code_set != PS_CODE_SET_BINARY) 28062306a36Sopenharmony_ci return false; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (v->scsi.designator_len != 8 && 28362306a36Sopenharmony_ci v->scsi.designator_len != 16) 28462306a36Sopenharmony_ci return false; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return true; 28762306a36Sopenharmony_ci case PS_DESIGNATOR_T10: 28862306a36Sopenharmony_ci case PS_DESIGNATOR_NAME: 28962306a36Sopenharmony_ci pr_err("pNFS: unsupported designator " 29062306a36Sopenharmony_ci "(code set %d, type %d, len %d.\n", 29162306a36Sopenharmony_ci v->scsi.code_set, 29262306a36Sopenharmony_ci v->scsi.designator_type, 29362306a36Sopenharmony_ci v->scsi.designator_len); 29462306a36Sopenharmony_ci return false; 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci pr_err("pNFS: invalid designator " 29762306a36Sopenharmony_ci "(code set %d, type %d, len %d.\n", 29862306a36Sopenharmony_ci v->scsi.code_set, 29962306a36Sopenharmony_ci v->scsi.designator_type, 30062306a36Sopenharmony_ci v->scsi.designator_len); 30162306a36Sopenharmony_ci return false; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic struct block_device * 30662306a36Sopenharmony_cibl_open_path(struct pnfs_block_volume *v, const char *prefix) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct block_device *bdev; 30962306a36Sopenharmony_ci const char *devname; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/%s%*phN", 31262306a36Sopenharmony_ci prefix, v->scsi.designator_len, v->scsi.designator); 31362306a36Sopenharmony_ci if (!devname) 31462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci bdev = blkdev_get_by_path(devname, BLK_OPEN_READ | BLK_OPEN_WRITE, NULL, 31762306a36Sopenharmony_ci NULL); 31862306a36Sopenharmony_ci if (IS_ERR(bdev)) { 31962306a36Sopenharmony_ci pr_warn("pNFS: failed to open device %s (%ld)\n", 32062306a36Sopenharmony_ci devname, PTR_ERR(bdev)); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci kfree(devname); 32462306a36Sopenharmony_ci return bdev; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int 32862306a36Sopenharmony_cibl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d, 32962306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 33262306a36Sopenharmony_ci struct block_device *bdev; 33362306a36Sopenharmony_ci const struct pr_ops *ops; 33462306a36Sopenharmony_ci int error; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!bl_validate_designator(v)) 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * Try to open the RH/Fedora specific dm-mpath udev path first, as the 34162306a36Sopenharmony_ci * wwn- links will only point to the first discovered SCSI device there. 34262306a36Sopenharmony_ci * On other distributions like Debian, the default SCSI by-id path will 34362306a36Sopenharmony_ci * point to the dm-multipath device if one exists. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci bdev = bl_open_path(v, "dm-uuid-mpath-0x"); 34662306a36Sopenharmony_ci if (IS_ERR(bdev)) 34762306a36Sopenharmony_ci bdev = bl_open_path(v, "wwn-0x"); 34862306a36Sopenharmony_ci if (IS_ERR(bdev)) 34962306a36Sopenharmony_ci return PTR_ERR(bdev); 35062306a36Sopenharmony_ci d->bdev = bdev; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci d->len = bdev_nr_bytes(d->bdev); 35362306a36Sopenharmony_ci d->map = bl_map_simple; 35462306a36Sopenharmony_ci d->pr_key = v->scsi.pr_key; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pr_info("pNFS: using block device %s (reservation key 0x%llx)\n", 35762306a36Sopenharmony_ci d->bdev->bd_disk->disk_name, d->pr_key); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ops = d->bdev->bd_disk->fops->pr_ops; 36062306a36Sopenharmony_ci if (!ops) { 36162306a36Sopenharmony_ci pr_err("pNFS: block device %s does not support reservations.", 36262306a36Sopenharmony_ci d->bdev->bd_disk->disk_name); 36362306a36Sopenharmony_ci error = -EINVAL; 36462306a36Sopenharmony_ci goto out_blkdev_put; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci error = ops->pr_register(d->bdev, 0, d->pr_key, true); 36862306a36Sopenharmony_ci if (error) { 36962306a36Sopenharmony_ci pr_err("pNFS: failed to register key for block device %s.", 37062306a36Sopenharmony_ci d->bdev->bd_disk->disk_name); 37162306a36Sopenharmony_ci goto out_blkdev_put; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci d->pr_registered = true; 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ciout_blkdev_put: 37862306a36Sopenharmony_ci blkdev_put(d->bdev, NULL); 37962306a36Sopenharmony_ci return error; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int 38362306a36Sopenharmony_cibl_parse_slice(struct nfs_server *server, struct pnfs_block_dev *d, 38462306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 38762306a36Sopenharmony_ci int ret; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ret = bl_parse_deviceid(server, d, volumes, v->slice.volume, gfp_mask); 39062306a36Sopenharmony_ci if (ret) 39162306a36Sopenharmony_ci return ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci d->disk_offset = v->slice.start; 39462306a36Sopenharmony_ci d->len = v->slice.len; 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int 39962306a36Sopenharmony_cibl_parse_concat(struct nfs_server *server, struct pnfs_block_dev *d, 40062306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 40362306a36Sopenharmony_ci u64 len = 0; 40462306a36Sopenharmony_ci int ret, i; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci d->children = kcalloc(v->concat.volumes_count, 40762306a36Sopenharmony_ci sizeof(struct pnfs_block_dev), gfp_mask); 40862306a36Sopenharmony_ci if (!d->children) 40962306a36Sopenharmony_ci return -ENOMEM; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci for (i = 0; i < v->concat.volumes_count; i++) { 41262306a36Sopenharmony_ci ret = bl_parse_deviceid(server, &d->children[i], 41362306a36Sopenharmony_ci volumes, v->concat.volumes[i], gfp_mask); 41462306a36Sopenharmony_ci if (ret) 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci d->nr_children++; 41862306a36Sopenharmony_ci d->children[i].start += len; 41962306a36Sopenharmony_ci len += d->children[i].len; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci d->len = len; 42362306a36Sopenharmony_ci d->map = bl_map_concat; 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int 42862306a36Sopenharmony_cibl_parse_stripe(struct nfs_server *server, struct pnfs_block_dev *d, 42962306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 43262306a36Sopenharmony_ci u64 len = 0; 43362306a36Sopenharmony_ci int ret, i; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci d->children = kcalloc(v->stripe.volumes_count, 43662306a36Sopenharmony_ci sizeof(struct pnfs_block_dev), gfp_mask); 43762306a36Sopenharmony_ci if (!d->children) 43862306a36Sopenharmony_ci return -ENOMEM; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (i = 0; i < v->stripe.volumes_count; i++) { 44162306a36Sopenharmony_ci ret = bl_parse_deviceid(server, &d->children[i], 44262306a36Sopenharmony_ci volumes, v->stripe.volumes[i], gfp_mask); 44362306a36Sopenharmony_ci if (ret) 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci d->nr_children++; 44762306a36Sopenharmony_ci len += d->children[i].len; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci d->len = len; 45162306a36Sopenharmony_ci d->chunk_size = v->stripe.chunk_size; 45262306a36Sopenharmony_ci d->map = bl_map_stripe; 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int 45762306a36Sopenharmony_cibl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d, 45862306a36Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci switch (volumes[idx].type) { 46162306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_SIMPLE: 46262306a36Sopenharmony_ci return bl_parse_simple(server, d, volumes, idx, gfp_mask); 46362306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_SLICE: 46462306a36Sopenharmony_ci return bl_parse_slice(server, d, volumes, idx, gfp_mask); 46562306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_CONCAT: 46662306a36Sopenharmony_ci return bl_parse_concat(server, d, volumes, idx, gfp_mask); 46762306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_STRIPE: 46862306a36Sopenharmony_ci return bl_parse_stripe(server, d, volumes, idx, gfp_mask); 46962306a36Sopenharmony_ci case PNFS_BLOCK_VOLUME_SCSI: 47062306a36Sopenharmony_ci return bl_parse_scsi(server, d, volumes, idx, gfp_mask); 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci dprintk("unsupported volume type: %d\n", volumes[idx].type); 47362306a36Sopenharmony_ci return -EIO; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistruct nfs4_deviceid_node * 47862306a36Sopenharmony_cibl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev, 47962306a36Sopenharmony_ci gfp_t gfp_mask) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct nfs4_deviceid_node *node = NULL; 48262306a36Sopenharmony_ci struct pnfs_block_volume *volumes; 48362306a36Sopenharmony_ci struct pnfs_block_dev *top; 48462306a36Sopenharmony_ci struct xdr_stream xdr; 48562306a36Sopenharmony_ci struct xdr_buf buf; 48662306a36Sopenharmony_ci struct page *scratch; 48762306a36Sopenharmony_ci int nr_volumes, ret, i; 48862306a36Sopenharmony_ci __be32 *p; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci scratch = alloc_page(gfp_mask); 49162306a36Sopenharmony_ci if (!scratch) 49262306a36Sopenharmony_ci goto out; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen); 49562306a36Sopenharmony_ci xdr_set_scratch_page(&xdr, scratch); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci p = xdr_inline_decode(&xdr, sizeof(__be32)); 49862306a36Sopenharmony_ci if (!p) 49962306a36Sopenharmony_ci goto out_free_scratch; 50062306a36Sopenharmony_ci nr_volumes = be32_to_cpup(p++); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci volumes = kcalloc(nr_volumes, sizeof(struct pnfs_block_volume), 50362306a36Sopenharmony_ci gfp_mask); 50462306a36Sopenharmony_ci if (!volumes) 50562306a36Sopenharmony_ci goto out_free_scratch; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci for (i = 0; i < nr_volumes; i++) { 50862306a36Sopenharmony_ci ret = nfs4_block_decode_volume(&xdr, &volumes[i]); 50962306a36Sopenharmony_ci if (ret < 0) 51062306a36Sopenharmony_ci goto out_free_volumes; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci top = kzalloc(sizeof(*top), gfp_mask); 51462306a36Sopenharmony_ci if (!top) 51562306a36Sopenharmony_ci goto out_free_volumes; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = bl_parse_deviceid(server, top, volumes, nr_volumes - 1, gfp_mask); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci node = &top->node; 52062306a36Sopenharmony_ci nfs4_init_deviceid_node(node, server, &pdev->dev_id); 52162306a36Sopenharmony_ci if (ret) 52262306a36Sopenharmony_ci nfs4_mark_deviceid_unavailable(node); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ciout_free_volumes: 52562306a36Sopenharmony_ci kfree(volumes); 52662306a36Sopenharmony_ciout_free_scratch: 52762306a36Sopenharmony_ci __free_page(scratch); 52862306a36Sopenharmony_ciout: 52962306a36Sopenharmony_ci return node; 53062306a36Sopenharmony_ci} 531