18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2016 Christoph Hellwig. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/sunrpc/svc.h> 68c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 78c2ecf20Sopenharmony_ci#include <linux/nfs4.h> 88c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 98c2ecf20Sopenharmony_ci#include <linux/nfs_xdr.h> 108c2ecf20Sopenharmony_ci#include <linux/pr.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "blocklayout.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_PNFS_LD 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic void 178c2ecf20Sopenharmony_cibl_free_device(struct pnfs_block_dev *dev) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci if (dev->nr_children) { 208c2ecf20Sopenharmony_ci int i; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci for (i = 0; i < dev->nr_children; i++) 238c2ecf20Sopenharmony_ci bl_free_device(&dev->children[i]); 248c2ecf20Sopenharmony_ci kfree(dev->children); 258c2ecf20Sopenharmony_ci } else { 268c2ecf20Sopenharmony_ci if (dev->pr_registered) { 278c2ecf20Sopenharmony_ci const struct pr_ops *ops = 288c2ecf20Sopenharmony_ci dev->bdev->bd_disk->fops->pr_ops; 298c2ecf20Sopenharmony_ci int error; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci error = ops->pr_register(dev->bdev, dev->pr_key, 0, 328c2ecf20Sopenharmony_ci false); 338c2ecf20Sopenharmony_ci if (error) 348c2ecf20Sopenharmony_ci pr_err("failed to unregister PR key.\n"); 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (dev->bdev) 388c2ecf20Sopenharmony_ci blkdev_put(dev->bdev, FMODE_READ | FMODE_WRITE); 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid 438c2ecf20Sopenharmony_cibl_free_deviceid_node(struct nfs4_deviceid_node *d) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct pnfs_block_dev *dev = 468c2ecf20Sopenharmony_ci container_of(d, struct pnfs_block_dev, node); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci bl_free_device(dev); 498c2ecf20Sopenharmony_ci kfree_rcu(dev, node.rcu); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int 538c2ecf20Sopenharmony_cinfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci __be32 *p; 568c2ecf20Sopenharmony_ci int i; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 4); 598c2ecf20Sopenharmony_ci if (!p) 608c2ecf20Sopenharmony_ci return -EIO; 618c2ecf20Sopenharmony_ci b->type = be32_to_cpup(p++); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci switch (b->type) { 648c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_SIMPLE: 658c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 4); 668c2ecf20Sopenharmony_ci if (!p) 678c2ecf20Sopenharmony_ci return -EIO; 688c2ecf20Sopenharmony_ci b->simple.nr_sigs = be32_to_cpup(p++); 698c2ecf20Sopenharmony_ci if (!b->simple.nr_sigs || b->simple.nr_sigs > PNFS_BLOCK_MAX_UUIDS) { 708c2ecf20Sopenharmony_ci dprintk("Bad signature count: %d\n", b->simple.nr_sigs); 718c2ecf20Sopenharmony_ci return -EIO; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci b->simple.len = 4 + 4; 758c2ecf20Sopenharmony_ci for (i = 0; i < b->simple.nr_sigs; i++) { 768c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 8 + 4); 778c2ecf20Sopenharmony_ci if (!p) 788c2ecf20Sopenharmony_ci return -EIO; 798c2ecf20Sopenharmony_ci p = xdr_decode_hyper(p, &b->simple.sigs[i].offset); 808c2ecf20Sopenharmony_ci b->simple.sigs[i].sig_len = be32_to_cpup(p++); 818c2ecf20Sopenharmony_ci if (b->simple.sigs[i].sig_len > PNFS_BLOCK_UUID_LEN) { 828c2ecf20Sopenharmony_ci pr_info("signature too long: %d\n", 838c2ecf20Sopenharmony_ci b->simple.sigs[i].sig_len); 848c2ecf20Sopenharmony_ci return -EIO; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, b->simple.sigs[i].sig_len); 888c2ecf20Sopenharmony_ci if (!p) 898c2ecf20Sopenharmony_ci return -EIO; 908c2ecf20Sopenharmony_ci memcpy(&b->simple.sigs[i].sig, p, 918c2ecf20Sopenharmony_ci b->simple.sigs[i].sig_len); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci b->simple.len += 8 + 4 + \ 948c2ecf20Sopenharmony_ci (XDR_QUADLEN(b->simple.sigs[i].sig_len) << 2); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_SLICE: 988c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 8 + 8 + 4); 998c2ecf20Sopenharmony_ci if (!p) 1008c2ecf20Sopenharmony_ci return -EIO; 1018c2ecf20Sopenharmony_ci p = xdr_decode_hyper(p, &b->slice.start); 1028c2ecf20Sopenharmony_ci p = xdr_decode_hyper(p, &b->slice.len); 1038c2ecf20Sopenharmony_ci b->slice.volume = be32_to_cpup(p++); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_CONCAT: 1068c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 4); 1078c2ecf20Sopenharmony_ci if (!p) 1088c2ecf20Sopenharmony_ci return -EIO; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci b->concat.volumes_count = be32_to_cpup(p++); 1118c2ecf20Sopenharmony_ci if (b->concat.volumes_count > PNFS_BLOCK_MAX_DEVICES) { 1128c2ecf20Sopenharmony_ci dprintk("Too many volumes: %d\n", b->concat.volumes_count); 1138c2ecf20Sopenharmony_ci return -EIO; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, b->concat.volumes_count * 4); 1178c2ecf20Sopenharmony_ci if (!p) 1188c2ecf20Sopenharmony_ci return -EIO; 1198c2ecf20Sopenharmony_ci for (i = 0; i < b->concat.volumes_count; i++) 1208c2ecf20Sopenharmony_ci b->concat.volumes[i] = be32_to_cpup(p++); 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_STRIPE: 1238c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 8 + 4); 1248c2ecf20Sopenharmony_ci if (!p) 1258c2ecf20Sopenharmony_ci return -EIO; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci p = xdr_decode_hyper(p, &b->stripe.chunk_size); 1288c2ecf20Sopenharmony_ci b->stripe.volumes_count = be32_to_cpup(p++); 1298c2ecf20Sopenharmony_ci if (b->stripe.volumes_count > PNFS_BLOCK_MAX_DEVICES) { 1308c2ecf20Sopenharmony_ci dprintk("Too many volumes: %d\n", b->stripe.volumes_count); 1318c2ecf20Sopenharmony_ci return -EIO; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, b->stripe.volumes_count * 4); 1358c2ecf20Sopenharmony_ci if (!p) 1368c2ecf20Sopenharmony_ci return -EIO; 1378c2ecf20Sopenharmony_ci for (i = 0; i < b->stripe.volumes_count; i++) 1388c2ecf20Sopenharmony_ci b->stripe.volumes[i] = be32_to_cpup(p++); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_SCSI: 1418c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 4 + 4 + 4); 1428c2ecf20Sopenharmony_ci if (!p) 1438c2ecf20Sopenharmony_ci return -EIO; 1448c2ecf20Sopenharmony_ci b->scsi.code_set = be32_to_cpup(p++); 1458c2ecf20Sopenharmony_ci b->scsi.designator_type = be32_to_cpup(p++); 1468c2ecf20Sopenharmony_ci b->scsi.designator_len = be32_to_cpup(p++); 1478c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, b->scsi.designator_len); 1488c2ecf20Sopenharmony_ci if (!p) 1498c2ecf20Sopenharmony_ci return -EIO; 1508c2ecf20Sopenharmony_ci if (b->scsi.designator_len > 256) 1518c2ecf20Sopenharmony_ci return -EIO; 1528c2ecf20Sopenharmony_ci memcpy(&b->scsi.designator, p, b->scsi.designator_len); 1538c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 8); 1548c2ecf20Sopenharmony_ci if (!p) 1558c2ecf20Sopenharmony_ci return -EIO; 1568c2ecf20Sopenharmony_ci p = xdr_decode_hyper(p, &b->scsi.pr_key); 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci default: 1598c2ecf20Sopenharmony_ci dprintk("unknown volume type!\n"); 1608c2ecf20Sopenharmony_ci return -EIO; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic bool bl_map_simple(struct pnfs_block_dev *dev, u64 offset, 1678c2ecf20Sopenharmony_ci struct pnfs_block_dev_map *map) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci map->start = dev->start; 1708c2ecf20Sopenharmony_ci map->len = dev->len; 1718c2ecf20Sopenharmony_ci map->disk_offset = dev->disk_offset; 1728c2ecf20Sopenharmony_ci map->bdev = dev->bdev; 1738c2ecf20Sopenharmony_ci return true; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic bool bl_map_concat(struct pnfs_block_dev *dev, u64 offset, 1778c2ecf20Sopenharmony_ci struct pnfs_block_dev_map *map) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int i; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (i = 0; i < dev->nr_children; i++) { 1828c2ecf20Sopenharmony_ci struct pnfs_block_dev *child = &dev->children[i]; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (child->start > offset || 1858c2ecf20Sopenharmony_ci child->start + child->len <= offset) 1868c2ecf20Sopenharmony_ci continue; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci child->map(child, offset - child->start, map); 1898c2ecf20Sopenharmony_ci return true; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci dprintk("%s: ran off loop!\n", __func__); 1938c2ecf20Sopenharmony_ci return false; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic bool bl_map_stripe(struct pnfs_block_dev *dev, u64 offset, 1978c2ecf20Sopenharmony_ci struct pnfs_block_dev_map *map) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct pnfs_block_dev *child; 2008c2ecf20Sopenharmony_ci u64 chunk; 2018c2ecf20Sopenharmony_ci u32 chunk_idx; 2028c2ecf20Sopenharmony_ci u64 disk_offset; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci chunk = div_u64(offset, dev->chunk_size); 2058c2ecf20Sopenharmony_ci div_u64_rem(chunk, dev->nr_children, &chunk_idx); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (chunk_idx >= dev->nr_children) { 2088c2ecf20Sopenharmony_ci dprintk("%s: invalid chunk idx %d (%lld/%lld)\n", 2098c2ecf20Sopenharmony_ci __func__, chunk_idx, offset, dev->chunk_size); 2108c2ecf20Sopenharmony_ci /* error, should not happen */ 2118c2ecf20Sopenharmony_ci return false; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* truncate offset to the beginning of the stripe */ 2158c2ecf20Sopenharmony_ci offset = chunk * dev->chunk_size; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* disk offset of the stripe */ 2188c2ecf20Sopenharmony_ci disk_offset = div_u64(offset, dev->nr_children); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci child = &dev->children[chunk_idx]; 2218c2ecf20Sopenharmony_ci child->map(child, disk_offset, map); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci map->start += offset; 2248c2ecf20Sopenharmony_ci map->disk_offset += disk_offset; 2258c2ecf20Sopenharmony_ci map->len = dev->chunk_size; 2268c2ecf20Sopenharmony_ci return true; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int 2308c2ecf20Sopenharmony_cibl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d, 2318c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int 2358c2ecf20Sopenharmony_cibl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d, 2368c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 2398c2ecf20Sopenharmony_ci struct block_device *bdev; 2408c2ecf20Sopenharmony_ci dev_t dev; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci dev = bl_resolve_deviceid(server, v, gfp_mask); 2438c2ecf20Sopenharmony_ci if (!dev) 2448c2ecf20Sopenharmony_ci return -EIO; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci bdev = blkdev_get_by_dev(dev, FMODE_READ | FMODE_WRITE, NULL); 2478c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) { 2488c2ecf20Sopenharmony_ci printk(KERN_WARNING "pNFS: failed to open device %d:%d (%ld)\n", 2498c2ecf20Sopenharmony_ci MAJOR(dev), MINOR(dev), PTR_ERR(bdev)); 2508c2ecf20Sopenharmony_ci return PTR_ERR(bdev); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci d->bdev = bdev; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci d->len = i_size_read(d->bdev->bd_inode); 2568c2ecf20Sopenharmony_ci d->map = bl_map_simple; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci printk(KERN_INFO "pNFS: using block device %s\n", 2598c2ecf20Sopenharmony_ci d->bdev->bd_disk->disk_name); 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic bool 2648c2ecf20Sopenharmony_cibl_validate_designator(struct pnfs_block_volume *v) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci switch (v->scsi.designator_type) { 2678c2ecf20Sopenharmony_ci case PS_DESIGNATOR_EUI64: 2688c2ecf20Sopenharmony_ci if (v->scsi.code_set != PS_CODE_SET_BINARY) 2698c2ecf20Sopenharmony_ci return false; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (v->scsi.designator_len != 8 && 2728c2ecf20Sopenharmony_ci v->scsi.designator_len != 10 && 2738c2ecf20Sopenharmony_ci v->scsi.designator_len != 16) 2748c2ecf20Sopenharmony_ci return false; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return true; 2778c2ecf20Sopenharmony_ci case PS_DESIGNATOR_NAA: 2788c2ecf20Sopenharmony_ci if (v->scsi.code_set != PS_CODE_SET_BINARY) 2798c2ecf20Sopenharmony_ci return false; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (v->scsi.designator_len != 8 && 2828c2ecf20Sopenharmony_ci v->scsi.designator_len != 16) 2838c2ecf20Sopenharmony_ci return false; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return true; 2868c2ecf20Sopenharmony_ci case PS_DESIGNATOR_T10: 2878c2ecf20Sopenharmony_ci case PS_DESIGNATOR_NAME: 2888c2ecf20Sopenharmony_ci pr_err("pNFS: unsupported designator " 2898c2ecf20Sopenharmony_ci "(code set %d, type %d, len %d.\n", 2908c2ecf20Sopenharmony_ci v->scsi.code_set, 2918c2ecf20Sopenharmony_ci v->scsi.designator_type, 2928c2ecf20Sopenharmony_ci v->scsi.designator_len); 2938c2ecf20Sopenharmony_ci return false; 2948c2ecf20Sopenharmony_ci default: 2958c2ecf20Sopenharmony_ci pr_err("pNFS: invalid designator " 2968c2ecf20Sopenharmony_ci "(code set %d, type %d, len %d.\n", 2978c2ecf20Sopenharmony_ci v->scsi.code_set, 2988c2ecf20Sopenharmony_ci v->scsi.designator_type, 2998c2ecf20Sopenharmony_ci v->scsi.designator_len); 3008c2ecf20Sopenharmony_ci return false; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * Try to open the udev path for the WWN. At least on Debian the udev 3068c2ecf20Sopenharmony_ci * by-id path will always point to the dm-multipath device if one exists. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistatic struct block_device * 3098c2ecf20Sopenharmony_cibl_open_udev_path(struct pnfs_block_volume *v) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct block_device *bdev; 3128c2ecf20Sopenharmony_ci const char *devname; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%*phN", 3158c2ecf20Sopenharmony_ci v->scsi.designator_len, v->scsi.designator); 3168c2ecf20Sopenharmony_ci if (!devname) 3178c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci bdev = blkdev_get_by_path(devname, FMODE_READ | FMODE_WRITE, NULL); 3208c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) { 3218c2ecf20Sopenharmony_ci pr_warn("pNFS: failed to open device %s (%ld)\n", 3228c2ecf20Sopenharmony_ci devname, PTR_ERR(bdev)); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci kfree(devname); 3268c2ecf20Sopenharmony_ci return bdev; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* 3308c2ecf20Sopenharmony_ci * Try to open the RH/Fedora specific dm-mpath udev path for this WWN, as the 3318c2ecf20Sopenharmony_ci * wwn- links will only point to the first discovered SCSI device there. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_cistatic struct block_device * 3348c2ecf20Sopenharmony_cibl_open_dm_mpath_udev_path(struct pnfs_block_volume *v) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct block_device *bdev; 3378c2ecf20Sopenharmony_ci const char *devname; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci devname = kasprintf(GFP_KERNEL, 3408c2ecf20Sopenharmony_ci "/dev/disk/by-id/dm-uuid-mpath-%d%*phN", 3418c2ecf20Sopenharmony_ci v->scsi.designator_type, 3428c2ecf20Sopenharmony_ci v->scsi.designator_len, v->scsi.designator); 3438c2ecf20Sopenharmony_ci if (!devname) 3448c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci bdev = blkdev_get_by_path(devname, FMODE_READ | FMODE_WRITE, NULL); 3478c2ecf20Sopenharmony_ci kfree(devname); 3488c2ecf20Sopenharmony_ci return bdev; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int 3528c2ecf20Sopenharmony_cibl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d, 3538c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 3568c2ecf20Sopenharmony_ci struct block_device *bdev; 3578c2ecf20Sopenharmony_ci const struct pr_ops *ops; 3588c2ecf20Sopenharmony_ci int error; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!bl_validate_designator(v)) 3618c2ecf20Sopenharmony_ci return -EINVAL; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci bdev = bl_open_dm_mpath_udev_path(v); 3648c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) 3658c2ecf20Sopenharmony_ci bdev = bl_open_udev_path(v); 3668c2ecf20Sopenharmony_ci if (IS_ERR(bdev)) 3678c2ecf20Sopenharmony_ci return PTR_ERR(bdev); 3688c2ecf20Sopenharmony_ci d->bdev = bdev; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci d->len = i_size_read(d->bdev->bd_inode); 3718c2ecf20Sopenharmony_ci d->map = bl_map_simple; 3728c2ecf20Sopenharmony_ci d->pr_key = v->scsi.pr_key; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci pr_info("pNFS: using block device %s (reservation key 0x%llx)\n", 3758c2ecf20Sopenharmony_ci d->bdev->bd_disk->disk_name, d->pr_key); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci ops = d->bdev->bd_disk->fops->pr_ops; 3788c2ecf20Sopenharmony_ci if (!ops) { 3798c2ecf20Sopenharmony_ci pr_err("pNFS: block device %s does not support reservations.", 3808c2ecf20Sopenharmony_ci d->bdev->bd_disk->disk_name); 3818c2ecf20Sopenharmony_ci error = -EINVAL; 3828c2ecf20Sopenharmony_ci goto out_blkdev_put; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci error = ops->pr_register(d->bdev, 0, d->pr_key, true); 3868c2ecf20Sopenharmony_ci if (error) { 3878c2ecf20Sopenharmony_ci pr_err("pNFS: failed to register key for block device %s.", 3888c2ecf20Sopenharmony_ci d->bdev->bd_disk->disk_name); 3898c2ecf20Sopenharmony_ci goto out_blkdev_put; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci d->pr_registered = true; 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciout_blkdev_put: 3968c2ecf20Sopenharmony_ci blkdev_put(d->bdev, FMODE_READ | FMODE_WRITE); 3978c2ecf20Sopenharmony_ci return error; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int 4018c2ecf20Sopenharmony_cibl_parse_slice(struct nfs_server *server, struct pnfs_block_dev *d, 4028c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 4058c2ecf20Sopenharmony_ci int ret; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ret = bl_parse_deviceid(server, d, volumes, v->slice.volume, gfp_mask); 4088c2ecf20Sopenharmony_ci if (ret) 4098c2ecf20Sopenharmony_ci return ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci d->disk_offset = v->slice.start; 4128c2ecf20Sopenharmony_ci d->len = v->slice.len; 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int 4178c2ecf20Sopenharmony_cibl_parse_concat(struct nfs_server *server, struct pnfs_block_dev *d, 4188c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 4218c2ecf20Sopenharmony_ci u64 len = 0; 4228c2ecf20Sopenharmony_ci int ret, i; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci d->children = kcalloc(v->concat.volumes_count, 4258c2ecf20Sopenharmony_ci sizeof(struct pnfs_block_dev), gfp_mask); 4268c2ecf20Sopenharmony_ci if (!d->children) 4278c2ecf20Sopenharmony_ci return -ENOMEM; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci for (i = 0; i < v->concat.volumes_count; i++) { 4308c2ecf20Sopenharmony_ci ret = bl_parse_deviceid(server, &d->children[i], 4318c2ecf20Sopenharmony_ci volumes, v->concat.volumes[i], gfp_mask); 4328c2ecf20Sopenharmony_ci if (ret) 4338c2ecf20Sopenharmony_ci return ret; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci d->nr_children++; 4368c2ecf20Sopenharmony_ci d->children[i].start += len; 4378c2ecf20Sopenharmony_ci len += d->children[i].len; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci d->len = len; 4418c2ecf20Sopenharmony_ci d->map = bl_map_concat; 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int 4468c2ecf20Sopenharmony_cibl_parse_stripe(struct nfs_server *server, struct pnfs_block_dev *d, 4478c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct pnfs_block_volume *v = &volumes[idx]; 4508c2ecf20Sopenharmony_ci u64 len = 0; 4518c2ecf20Sopenharmony_ci int ret, i; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci d->children = kcalloc(v->stripe.volumes_count, 4548c2ecf20Sopenharmony_ci sizeof(struct pnfs_block_dev), gfp_mask); 4558c2ecf20Sopenharmony_ci if (!d->children) 4568c2ecf20Sopenharmony_ci return -ENOMEM; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for (i = 0; i < v->stripe.volumes_count; i++) { 4598c2ecf20Sopenharmony_ci ret = bl_parse_deviceid(server, &d->children[i], 4608c2ecf20Sopenharmony_ci volumes, v->stripe.volumes[i], gfp_mask); 4618c2ecf20Sopenharmony_ci if (ret) 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci d->nr_children++; 4658c2ecf20Sopenharmony_ci len += d->children[i].len; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci d->len = len; 4698c2ecf20Sopenharmony_ci d->chunk_size = v->stripe.chunk_size; 4708c2ecf20Sopenharmony_ci d->map = bl_map_stripe; 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int 4758c2ecf20Sopenharmony_cibl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d, 4768c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci switch (volumes[idx].type) { 4798c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_SIMPLE: 4808c2ecf20Sopenharmony_ci return bl_parse_simple(server, d, volumes, idx, gfp_mask); 4818c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_SLICE: 4828c2ecf20Sopenharmony_ci return bl_parse_slice(server, d, volumes, idx, gfp_mask); 4838c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_CONCAT: 4848c2ecf20Sopenharmony_ci return bl_parse_concat(server, d, volumes, idx, gfp_mask); 4858c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_STRIPE: 4868c2ecf20Sopenharmony_ci return bl_parse_stripe(server, d, volumes, idx, gfp_mask); 4878c2ecf20Sopenharmony_ci case PNFS_BLOCK_VOLUME_SCSI: 4888c2ecf20Sopenharmony_ci return bl_parse_scsi(server, d, volumes, idx, gfp_mask); 4898c2ecf20Sopenharmony_ci default: 4908c2ecf20Sopenharmony_ci dprintk("unsupported volume type: %d\n", volumes[idx].type); 4918c2ecf20Sopenharmony_ci return -EIO; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistruct nfs4_deviceid_node * 4968c2ecf20Sopenharmony_cibl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev, 4978c2ecf20Sopenharmony_ci gfp_t gfp_mask) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct nfs4_deviceid_node *node = NULL; 5008c2ecf20Sopenharmony_ci struct pnfs_block_volume *volumes; 5018c2ecf20Sopenharmony_ci struct pnfs_block_dev *top; 5028c2ecf20Sopenharmony_ci struct xdr_stream xdr; 5038c2ecf20Sopenharmony_ci struct xdr_buf buf; 5048c2ecf20Sopenharmony_ci struct page *scratch; 5058c2ecf20Sopenharmony_ci int nr_volumes, ret, i; 5068c2ecf20Sopenharmony_ci __be32 *p; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci scratch = alloc_page(gfp_mask); 5098c2ecf20Sopenharmony_ci if (!scratch) 5108c2ecf20Sopenharmony_ci goto out; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen); 5138c2ecf20Sopenharmony_ci xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci p = xdr_inline_decode(&xdr, sizeof(__be32)); 5168c2ecf20Sopenharmony_ci if (!p) 5178c2ecf20Sopenharmony_ci goto out_free_scratch; 5188c2ecf20Sopenharmony_ci nr_volumes = be32_to_cpup(p++); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci volumes = kcalloc(nr_volumes, sizeof(struct pnfs_block_volume), 5218c2ecf20Sopenharmony_ci gfp_mask); 5228c2ecf20Sopenharmony_ci if (!volumes) 5238c2ecf20Sopenharmony_ci goto out_free_scratch; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci for (i = 0; i < nr_volumes; i++) { 5268c2ecf20Sopenharmony_ci ret = nfs4_block_decode_volume(&xdr, &volumes[i]); 5278c2ecf20Sopenharmony_ci if (ret < 0) 5288c2ecf20Sopenharmony_ci goto out_free_volumes; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci top = kzalloc(sizeof(*top), gfp_mask); 5328c2ecf20Sopenharmony_ci if (!top) 5338c2ecf20Sopenharmony_ci goto out_free_volumes; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = bl_parse_deviceid(server, top, volumes, nr_volumes - 1, gfp_mask); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci node = &top->node; 5388c2ecf20Sopenharmony_ci nfs4_init_deviceid_node(node, server, &pdev->dev_id); 5398c2ecf20Sopenharmony_ci if (ret) 5408c2ecf20Sopenharmony_ci nfs4_mark_deviceid_unavailable(node); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciout_free_volumes: 5438c2ecf20Sopenharmony_ci kfree(volumes); 5448c2ecf20Sopenharmony_ciout_free_scratch: 5458c2ecf20Sopenharmony_ci __free_page(scratch); 5468c2ecf20Sopenharmony_ciout: 5478c2ecf20Sopenharmony_ci return node; 5488c2ecf20Sopenharmony_ci} 549