Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2014-2016 Christoph Hellwig.
0004  */
0005 #include <linux/exportfs.h>
0006 #include <linux/iomap.h>
0007 #include <linux/slab.h>
0008 #include <linux/pr.h>
0009 
0010 #include <linux/nfsd/debug.h>
0011 
0012 #include "blocklayoutxdr.h"
0013 #include "pnfs.h"
0014 #include "filecache.h"
0015 
0016 #define NFSDDBG_FACILITY    NFSDDBG_PNFS
0017 
0018 
0019 static __be32
0020 nfsd4_block_proc_layoutget(struct inode *inode, const struct svc_fh *fhp,
0021         struct nfsd4_layoutget *args)
0022 {
0023     struct nfsd4_layout_seg *seg = &args->lg_seg;
0024     struct super_block *sb = inode->i_sb;
0025     u32 block_size = i_blocksize(inode);
0026     struct pnfs_block_extent *bex;
0027     struct iomap iomap;
0028     u32 device_generation = 0;
0029     int error;
0030 
0031     if (seg->offset & (block_size - 1)) {
0032         dprintk("pnfsd: I/O misaligned\n");
0033         goto out_layoutunavailable;
0034     }
0035 
0036     /*
0037      * Some clients barf on non-zero block numbers for NONE or INVALID
0038      * layouts, so make sure to zero the whole structure.
0039      */
0040     error = -ENOMEM;
0041     bex = kzalloc(sizeof(*bex), GFP_KERNEL);
0042     if (!bex)
0043         goto out_error;
0044     args->lg_content = bex;
0045 
0046     error = sb->s_export_op->map_blocks(inode, seg->offset, seg->length,
0047                         &iomap, seg->iomode != IOMODE_READ,
0048                         &device_generation);
0049     if (error) {
0050         if (error == -ENXIO)
0051             goto out_layoutunavailable;
0052         goto out_error;
0053     }
0054 
0055     if (iomap.length < args->lg_minlength) {
0056         dprintk("pnfsd: extent smaller than minlength\n");
0057         goto out_layoutunavailable;
0058     }
0059 
0060     switch (iomap.type) {
0061     case IOMAP_MAPPED:
0062         if (seg->iomode == IOMODE_READ)
0063             bex->es = PNFS_BLOCK_READ_DATA;
0064         else
0065             bex->es = PNFS_BLOCK_READWRITE_DATA;
0066         bex->soff = iomap.addr;
0067         break;
0068     case IOMAP_UNWRITTEN:
0069         if (seg->iomode & IOMODE_RW) {
0070             /*
0071              * Crack monkey special case from section 2.3.1.
0072              */
0073             if (args->lg_minlength == 0) {
0074                 dprintk("pnfsd: no soup for you!\n");
0075                 goto out_layoutunavailable;
0076             }
0077 
0078             bex->es = PNFS_BLOCK_INVALID_DATA;
0079             bex->soff = iomap.addr;
0080             break;
0081         }
0082         fallthrough;
0083     case IOMAP_HOLE:
0084         if (seg->iomode == IOMODE_READ) {
0085             bex->es = PNFS_BLOCK_NONE_DATA;
0086             break;
0087         }
0088         fallthrough;
0089     case IOMAP_DELALLOC:
0090     default:
0091         WARN(1, "pnfsd: filesystem returned %d extent\n", iomap.type);
0092         goto out_layoutunavailable;
0093     }
0094 
0095     error = nfsd4_set_deviceid(&bex->vol_id, fhp, device_generation);
0096     if (error)
0097         goto out_error;
0098     bex->foff = iomap.offset;
0099     bex->len = iomap.length;
0100 
0101     seg->offset = iomap.offset;
0102     seg->length = iomap.length;
0103 
0104     dprintk("GET: 0x%llx:0x%llx %d\n", bex->foff, bex->len, bex->es);
0105     return 0;
0106 
0107 out_error:
0108     seg->length = 0;
0109     return nfserrno(error);
0110 out_layoutunavailable:
0111     seg->length = 0;
0112     return nfserr_layoutunavailable;
0113 }
0114 
0115 static __be32
0116 nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
0117         struct iomap *iomaps, int nr_iomaps)
0118 {
0119     loff_t new_size = lcp->lc_last_wr + 1;
0120     struct iattr iattr = { .ia_valid = 0 };
0121     int error;
0122 
0123     if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
0124         timespec64_compare(&lcp->lc_mtime, &inode->i_mtime) < 0)
0125         lcp->lc_mtime = current_time(inode);
0126     iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
0127     iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
0128 
0129     if (new_size > i_size_read(inode)) {
0130         iattr.ia_valid |= ATTR_SIZE;
0131         iattr.ia_size = new_size;
0132     }
0133 
0134     error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
0135             nr_iomaps, &iattr);
0136     kfree(iomaps);
0137     return nfserrno(error);
0138 }
0139 
0140 #ifdef CONFIG_NFSD_BLOCKLAYOUT
0141 static int
0142 nfsd4_block_get_device_info_simple(struct super_block *sb,
0143         struct nfsd4_getdeviceinfo *gdp)
0144 {
0145     struct pnfs_block_deviceaddr *dev;
0146     struct pnfs_block_volume *b;
0147 
0148     dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
0149               sizeof(struct pnfs_block_volume), GFP_KERNEL);
0150     if (!dev)
0151         return -ENOMEM;
0152     gdp->gd_device = dev;
0153 
0154     dev->nr_volumes = 1;
0155     b = &dev->volumes[0];
0156 
0157     b->type = PNFS_BLOCK_VOLUME_SIMPLE;
0158     b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
0159     return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
0160             &b->simple.offset);
0161 }
0162 
0163 static __be32
0164 nfsd4_block_proc_getdeviceinfo(struct super_block *sb,
0165         struct svc_rqst *rqstp,
0166         struct nfs4_client *clp,
0167         struct nfsd4_getdeviceinfo *gdp)
0168 {
0169     if (bdev_is_partition(sb->s_bdev))
0170         return nfserr_inval;
0171     return nfserrno(nfsd4_block_get_device_info_simple(sb, gdp));
0172 }
0173 
0174 static __be32
0175 nfsd4_block_proc_layoutcommit(struct inode *inode,
0176         struct nfsd4_layoutcommit *lcp)
0177 {
0178     struct iomap *iomaps;
0179     int nr_iomaps;
0180 
0181     nr_iomaps = nfsd4_block_decode_layoutupdate(lcp->lc_up_layout,
0182             lcp->lc_up_len, &iomaps, i_blocksize(inode));
0183     if (nr_iomaps < 0)
0184         return nfserrno(nr_iomaps);
0185 
0186     return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps);
0187 }
0188 
0189 const struct nfsd4_layout_ops bl_layout_ops = {
0190     /*
0191      * Pretend that we send notification to the client.  This is a blatant
0192      * lie to force recent Linux clients to cache our device IDs.
0193      * We rarely ever change the device ID, so the harm of leaking deviceids
0194      * for a while isn't too bad.  Unfortunately RFC5661 is a complete mess
0195      * in this regard, but I filed errata 4119 for this a while ago, and
0196      * hopefully the Linux client will eventually start caching deviceids
0197      * without this again.
0198      */
0199     .notify_types       =
0200             NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE,
0201     .proc_getdeviceinfo = nfsd4_block_proc_getdeviceinfo,
0202     .encode_getdeviceinfo   = nfsd4_block_encode_getdeviceinfo,
0203     .proc_layoutget     = nfsd4_block_proc_layoutget,
0204     .encode_layoutget   = nfsd4_block_encode_layoutget,
0205     .proc_layoutcommit  = nfsd4_block_proc_layoutcommit,
0206 };
0207 #endif /* CONFIG_NFSD_BLOCKLAYOUT */
0208 
0209 #ifdef CONFIG_NFSD_SCSILAYOUT
0210 #define NFSD_MDS_PR_KEY     0x0100000000000000ULL
0211 
0212 /*
0213  * We use the client ID as a unique key for the reservations.
0214  * This allows us to easily fence a client when recalls fail.
0215  */
0216 static u64 nfsd4_scsi_pr_key(struct nfs4_client *clp)
0217 {
0218     return ((u64)clp->cl_clientid.cl_boot << 32) | clp->cl_clientid.cl_id;
0219 }
0220 
0221 static const u8 designator_types[] = {
0222     PS_DESIGNATOR_EUI64,
0223     PS_DESIGNATOR_NAA,
0224 };
0225 
0226 static int
0227 nfsd4_block_get_unique_id(struct gendisk *disk, struct pnfs_block_volume *b)
0228 {
0229     int ret, i;
0230 
0231     for (i = 0; i < ARRAY_SIZE(designator_types); i++) {
0232         u8 type = designator_types[i];
0233 
0234         ret = disk->fops->get_unique_id(disk, b->scsi.designator, type);
0235         if (ret > 0) {
0236             b->scsi.code_set = PS_CODE_SET_BINARY;
0237             b->scsi.designator_type = type;
0238             b->scsi.designator_len = ret;
0239             return 0;
0240         }
0241     }
0242 
0243     return -EINVAL;
0244 }
0245 
0246 static int
0247 nfsd4_block_get_device_info_scsi(struct super_block *sb,
0248         struct nfs4_client *clp,
0249         struct nfsd4_getdeviceinfo *gdp)
0250 {
0251     struct pnfs_block_deviceaddr *dev;
0252     struct pnfs_block_volume *b;
0253     const struct pr_ops *ops;
0254     int ret;
0255 
0256     dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
0257               sizeof(struct pnfs_block_volume), GFP_KERNEL);
0258     if (!dev)
0259         return -ENOMEM;
0260     gdp->gd_device = dev;
0261 
0262     dev->nr_volumes = 1;
0263     b = &dev->volumes[0];
0264 
0265     b->type = PNFS_BLOCK_VOLUME_SCSI;
0266     b->scsi.pr_key = nfsd4_scsi_pr_key(clp);
0267 
0268     ret = nfsd4_block_get_unique_id(sb->s_bdev->bd_disk, b);
0269     if (ret < 0)
0270         goto out_free_dev;
0271 
0272     ret = -EINVAL;
0273     ops = sb->s_bdev->bd_disk->fops->pr_ops;
0274     if (!ops) {
0275         pr_err("pNFS: device %s does not support PRs.\n",
0276             sb->s_id);
0277         goto out_free_dev;
0278     }
0279 
0280     ret = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true);
0281     if (ret) {
0282         pr_err("pNFS: failed to register key for device %s.\n",
0283             sb->s_id);
0284         goto out_free_dev;
0285     }
0286 
0287     ret = ops->pr_reserve(sb->s_bdev, NFSD_MDS_PR_KEY,
0288             PR_EXCLUSIVE_ACCESS_REG_ONLY, 0);
0289     if (ret) {
0290         pr_err("pNFS: failed to reserve device %s.\n",
0291             sb->s_id);
0292         goto out_free_dev;
0293     }
0294 
0295     return 0;
0296 
0297 out_free_dev:
0298     kfree(dev);
0299     return ret;
0300 }
0301 
0302 static __be32
0303 nfsd4_scsi_proc_getdeviceinfo(struct super_block *sb,
0304         struct svc_rqst *rqstp,
0305         struct nfs4_client *clp,
0306         struct nfsd4_getdeviceinfo *gdp)
0307 {
0308     if (bdev_is_partition(sb->s_bdev))
0309         return nfserr_inval;
0310     return nfserrno(nfsd4_block_get_device_info_scsi(sb, clp, gdp));
0311 }
0312 static __be32
0313 nfsd4_scsi_proc_layoutcommit(struct inode *inode,
0314         struct nfsd4_layoutcommit *lcp)
0315 {
0316     struct iomap *iomaps;
0317     int nr_iomaps;
0318 
0319     nr_iomaps = nfsd4_scsi_decode_layoutupdate(lcp->lc_up_layout,
0320             lcp->lc_up_len, &iomaps, i_blocksize(inode));
0321     if (nr_iomaps < 0)
0322         return nfserrno(nr_iomaps);
0323 
0324     return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps);
0325 }
0326 
0327 static void
0328 nfsd4_scsi_fence_client(struct nfs4_layout_stateid *ls)
0329 {
0330     struct nfs4_client *clp = ls->ls_stid.sc_client;
0331     struct block_device *bdev = ls->ls_file->nf_file->f_path.mnt->mnt_sb->s_bdev;
0332 
0333     bdev->bd_disk->fops->pr_ops->pr_preempt(bdev, NFSD_MDS_PR_KEY,
0334             nfsd4_scsi_pr_key(clp), 0, true);
0335 }
0336 
0337 const struct nfsd4_layout_ops scsi_layout_ops = {
0338     /*
0339      * Pretend that we send notification to the client.  This is a blatant
0340      * lie to force recent Linux clients to cache our device IDs.
0341      * We rarely ever change the device ID, so the harm of leaking deviceids
0342      * for a while isn't too bad.  Unfortunately RFC5661 is a complete mess
0343      * in this regard, but I filed errata 4119 for this a while ago, and
0344      * hopefully the Linux client will eventually start caching deviceids
0345      * without this again.
0346      */
0347     .notify_types       =
0348             NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE,
0349     .proc_getdeviceinfo = nfsd4_scsi_proc_getdeviceinfo,
0350     .encode_getdeviceinfo   = nfsd4_block_encode_getdeviceinfo,
0351     .proc_layoutget     = nfsd4_block_proc_layoutget,
0352     .encode_layoutget   = nfsd4_block_encode_layoutget,
0353     .proc_layoutcommit  = nfsd4_scsi_proc_layoutcommit,
0354     .fence_client       = nfsd4_scsi_fence_client,
0355 };
0356 #endif /* CONFIG_NFSD_SCSILAYOUT */