18650b8a0SChristoph Hellwig /* 28650b8a0SChristoph Hellwig * Copyright (c) 2014 Christoph Hellwig. 38650b8a0SChristoph Hellwig */ 48650b8a0SChristoph Hellwig #include <linux/exportfs.h> 58650b8a0SChristoph Hellwig #include <linux/genhd.h> 68650b8a0SChristoph Hellwig #include <linux/slab.h> 78650b8a0SChristoph Hellwig 88650b8a0SChristoph Hellwig #include <linux/nfsd/debug.h> 98650b8a0SChristoph Hellwig 108650b8a0SChristoph Hellwig #include "blocklayoutxdr.h" 118650b8a0SChristoph Hellwig #include "pnfs.h" 128650b8a0SChristoph Hellwig 138650b8a0SChristoph Hellwig #define NFSDDBG_FACILITY NFSDDBG_PNFS 148650b8a0SChristoph Hellwig 158650b8a0SChristoph Hellwig 168650b8a0SChristoph Hellwig static int 178650b8a0SChristoph Hellwig nfsd4_block_get_device_info_simple(struct super_block *sb, 188650b8a0SChristoph Hellwig struct nfsd4_getdeviceinfo *gdp) 198650b8a0SChristoph Hellwig { 208650b8a0SChristoph Hellwig struct pnfs_block_deviceaddr *dev; 218650b8a0SChristoph Hellwig struct pnfs_block_volume *b; 228650b8a0SChristoph Hellwig 238650b8a0SChristoph Hellwig dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) + 248650b8a0SChristoph Hellwig sizeof(struct pnfs_block_volume), GFP_KERNEL); 258650b8a0SChristoph Hellwig if (!dev) 268650b8a0SChristoph Hellwig return -ENOMEM; 278650b8a0SChristoph Hellwig gdp->gd_device = dev; 288650b8a0SChristoph Hellwig 298650b8a0SChristoph Hellwig dev->nr_volumes = 1; 308650b8a0SChristoph Hellwig b = &dev->volumes[0]; 318650b8a0SChristoph Hellwig 328650b8a0SChristoph Hellwig b->type = PNFS_BLOCK_VOLUME_SIMPLE; 338650b8a0SChristoph Hellwig b->simple.sig_len = PNFS_BLOCK_UUID_LEN; 348650b8a0SChristoph Hellwig return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len, 358650b8a0SChristoph Hellwig &b->simple.offset); 368650b8a0SChristoph Hellwig } 378650b8a0SChristoph Hellwig 388650b8a0SChristoph Hellwig static __be32 398650b8a0SChristoph Hellwig nfsd4_block_proc_getdeviceinfo(struct super_block *sb, 408650b8a0SChristoph Hellwig struct nfsd4_getdeviceinfo *gdp) 418650b8a0SChristoph Hellwig { 428650b8a0SChristoph Hellwig if (sb->s_bdev != sb->s_bdev->bd_contains) 438650b8a0SChristoph Hellwig return nfserr_inval; 448650b8a0SChristoph Hellwig return nfserrno(nfsd4_block_get_device_info_simple(sb, gdp)); 458650b8a0SChristoph Hellwig } 468650b8a0SChristoph Hellwig 478650b8a0SChristoph Hellwig static __be32 488650b8a0SChristoph Hellwig nfsd4_block_proc_layoutget(struct inode *inode, const struct svc_fh *fhp, 498650b8a0SChristoph Hellwig struct nfsd4_layoutget *args) 508650b8a0SChristoph Hellwig { 518650b8a0SChristoph Hellwig struct nfsd4_layout_seg *seg = &args->lg_seg; 528650b8a0SChristoph Hellwig struct super_block *sb = inode->i_sb; 538650b8a0SChristoph Hellwig u32 block_size = (1 << inode->i_blkbits); 548650b8a0SChristoph Hellwig struct pnfs_block_extent *bex; 558650b8a0SChristoph Hellwig struct iomap iomap; 568650b8a0SChristoph Hellwig u32 device_generation = 0; 578650b8a0SChristoph Hellwig int error; 588650b8a0SChristoph Hellwig 598650b8a0SChristoph Hellwig /* 608650b8a0SChristoph Hellwig * We do not attempt to support I/O smaller than the fs block size, 618650b8a0SChristoph Hellwig * or not aligned to it. 628650b8a0SChristoph Hellwig */ 638650b8a0SChristoph Hellwig if (args->lg_minlength < block_size) { 648650b8a0SChristoph Hellwig dprintk("pnfsd: I/O too small\n"); 658650b8a0SChristoph Hellwig goto out_layoutunavailable; 668650b8a0SChristoph Hellwig } 678650b8a0SChristoph Hellwig if (seg->offset & (block_size - 1)) { 688650b8a0SChristoph Hellwig dprintk("pnfsd: I/O misaligned\n"); 698650b8a0SChristoph Hellwig goto out_layoutunavailable; 708650b8a0SChristoph Hellwig } 718650b8a0SChristoph Hellwig 728650b8a0SChristoph Hellwig /* 738650b8a0SChristoph Hellwig * Some clients barf on non-zero block numbers for NONE or INVALID 748650b8a0SChristoph Hellwig * layouts, so make sure to zero the whole structure. 758650b8a0SChristoph Hellwig */ 768650b8a0SChristoph Hellwig error = -ENOMEM; 778650b8a0SChristoph Hellwig bex = kzalloc(sizeof(*bex), GFP_KERNEL); 788650b8a0SChristoph Hellwig if (!bex) 798650b8a0SChristoph Hellwig goto out_error; 808650b8a0SChristoph Hellwig args->lg_content = bex; 818650b8a0SChristoph Hellwig 828650b8a0SChristoph Hellwig error = sb->s_export_op->map_blocks(inode, seg->offset, seg->length, 838650b8a0SChristoph Hellwig &iomap, seg->iomode != IOMODE_READ, 848650b8a0SChristoph Hellwig &device_generation); 858650b8a0SChristoph Hellwig if (error) { 868650b8a0SChristoph Hellwig if (error == -ENXIO) 878650b8a0SChristoph Hellwig goto out_layoutunavailable; 888650b8a0SChristoph Hellwig goto out_error; 898650b8a0SChristoph Hellwig } 908650b8a0SChristoph Hellwig 918650b8a0SChristoph Hellwig if (iomap.length < args->lg_minlength) { 928650b8a0SChristoph Hellwig dprintk("pnfsd: extent smaller than minlength\n"); 938650b8a0SChristoph Hellwig goto out_layoutunavailable; 948650b8a0SChristoph Hellwig } 958650b8a0SChristoph Hellwig 968650b8a0SChristoph Hellwig switch (iomap.type) { 978650b8a0SChristoph Hellwig case IOMAP_MAPPED: 988650b8a0SChristoph Hellwig if (seg->iomode == IOMODE_READ) 998650b8a0SChristoph Hellwig bex->es = PNFS_BLOCK_READ_DATA; 1008650b8a0SChristoph Hellwig else 1018650b8a0SChristoph Hellwig bex->es = PNFS_BLOCK_READWRITE_DATA; 1028650b8a0SChristoph Hellwig bex->soff = (iomap.blkno << 9); 1038650b8a0SChristoph Hellwig break; 1048650b8a0SChristoph Hellwig case IOMAP_UNWRITTEN: 1058650b8a0SChristoph Hellwig if (seg->iomode & IOMODE_RW) { 1068650b8a0SChristoph Hellwig /* 1078650b8a0SChristoph Hellwig * Crack monkey special case from section 2.3.1. 1088650b8a0SChristoph Hellwig */ 1098650b8a0SChristoph Hellwig if (args->lg_minlength == 0) { 1108650b8a0SChristoph Hellwig dprintk("pnfsd: no soup for you!\n"); 1118650b8a0SChristoph Hellwig goto out_layoutunavailable; 1128650b8a0SChristoph Hellwig } 1138650b8a0SChristoph Hellwig 1148650b8a0SChristoph Hellwig bex->es = PNFS_BLOCK_INVALID_DATA; 1158650b8a0SChristoph Hellwig bex->soff = (iomap.blkno << 9); 1168650b8a0SChristoph Hellwig break; 1178650b8a0SChristoph Hellwig } 1188650b8a0SChristoph Hellwig /*FALLTHRU*/ 1198650b8a0SChristoph Hellwig case IOMAP_HOLE: 1208650b8a0SChristoph Hellwig if (seg->iomode == IOMODE_READ) { 1218650b8a0SChristoph Hellwig bex->es = PNFS_BLOCK_NONE_DATA; 1228650b8a0SChristoph Hellwig break; 1238650b8a0SChristoph Hellwig } 1248650b8a0SChristoph Hellwig /*FALLTHRU*/ 1258650b8a0SChristoph Hellwig case IOMAP_DELALLOC: 1268650b8a0SChristoph Hellwig default: 1278650b8a0SChristoph Hellwig WARN(1, "pnfsd: filesystem returned %d extent\n", iomap.type); 1288650b8a0SChristoph Hellwig goto out_layoutunavailable; 1298650b8a0SChristoph Hellwig } 1308650b8a0SChristoph Hellwig 1318650b8a0SChristoph Hellwig error = nfsd4_set_deviceid(&bex->vol_id, fhp, device_generation); 1328650b8a0SChristoph Hellwig if (error) 1338650b8a0SChristoph Hellwig goto out_error; 1348650b8a0SChristoph Hellwig bex->foff = iomap.offset; 1358650b8a0SChristoph Hellwig bex->len = iomap.length; 1368650b8a0SChristoph Hellwig 1378650b8a0SChristoph Hellwig seg->offset = iomap.offset; 1388650b8a0SChristoph Hellwig seg->length = iomap.length; 1398650b8a0SChristoph Hellwig 1408650b8a0SChristoph Hellwig dprintk("GET: %lld:%lld %d\n", bex->foff, bex->len, bex->es); 1418650b8a0SChristoph Hellwig return 0; 1428650b8a0SChristoph Hellwig 1438650b8a0SChristoph Hellwig out_error: 1448650b8a0SChristoph Hellwig seg->length = 0; 1458650b8a0SChristoph Hellwig return nfserrno(error); 1468650b8a0SChristoph Hellwig out_layoutunavailable: 1478650b8a0SChristoph Hellwig seg->length = 0; 1488650b8a0SChristoph Hellwig return nfserr_layoutunavailable; 1498650b8a0SChristoph Hellwig } 1508650b8a0SChristoph Hellwig 1518650b8a0SChristoph Hellwig static __be32 1528650b8a0SChristoph Hellwig nfsd4_block_proc_layoutcommit(struct inode *inode, 1538650b8a0SChristoph Hellwig struct nfsd4_layoutcommit *lcp) 1548650b8a0SChristoph Hellwig { 1558650b8a0SChristoph Hellwig loff_t new_size = lcp->lc_last_wr + 1; 1568650b8a0SChristoph Hellwig struct iattr iattr = { .ia_valid = 0 }; 1578650b8a0SChristoph Hellwig struct iomap *iomaps; 1588650b8a0SChristoph Hellwig int nr_iomaps; 1598650b8a0SChristoph Hellwig int error; 1608650b8a0SChristoph Hellwig 1618650b8a0SChristoph Hellwig nr_iomaps = nfsd4_block_decode_layoutupdate(lcp->lc_up_layout, 1628650b8a0SChristoph Hellwig lcp->lc_up_len, &iomaps, 1 << inode->i_blkbits); 1638650b8a0SChristoph Hellwig if (nr_iomaps < 0) 1648650b8a0SChristoph Hellwig return nfserrno(nr_iomaps); 1658650b8a0SChristoph Hellwig 1668650b8a0SChristoph Hellwig if (lcp->lc_mtime.tv_nsec == UTIME_NOW || 1678650b8a0SChristoph Hellwig timespec_compare(&lcp->lc_mtime, &inode->i_mtime) < 0) 1688650b8a0SChristoph Hellwig lcp->lc_mtime = current_fs_time(inode->i_sb); 1698650b8a0SChristoph Hellwig iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME; 1708650b8a0SChristoph Hellwig iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime; 1718650b8a0SChristoph Hellwig 1728650b8a0SChristoph Hellwig if (new_size > i_size_read(inode)) { 1738650b8a0SChristoph Hellwig iattr.ia_valid |= ATTR_SIZE; 1748650b8a0SChristoph Hellwig iattr.ia_size = new_size; 1758650b8a0SChristoph Hellwig } 1768650b8a0SChristoph Hellwig 1778650b8a0SChristoph Hellwig error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps, 1788650b8a0SChristoph Hellwig nr_iomaps, &iattr); 1798650b8a0SChristoph Hellwig kfree(iomaps); 1808650b8a0SChristoph Hellwig return nfserrno(error); 1818650b8a0SChristoph Hellwig } 1828650b8a0SChristoph Hellwig 1838650b8a0SChristoph Hellwig const struct nfsd4_layout_ops bl_layout_ops = { 1848650b8a0SChristoph Hellwig .proc_getdeviceinfo = nfsd4_block_proc_getdeviceinfo, 1858650b8a0SChristoph Hellwig .encode_getdeviceinfo = nfsd4_block_encode_getdeviceinfo, 1868650b8a0SChristoph Hellwig .proc_layoutget = nfsd4_block_proc_layoutget, 1878650b8a0SChristoph Hellwig .encode_layoutget = nfsd4_block_encode_layoutget, 1888650b8a0SChristoph Hellwig .proc_layoutcommit = nfsd4_block_proc_layoutcommit, 1898650b8a0SChristoph Hellwig }; 190