forked from luck/tmp_suning_uos_patched
0e6acf0204
Changes in this update: o generic iomap based IO path infrastructure o generic iomap based fiemap implementation o xfs iomap based Io path implementation o buffer error handling fixes o tracking of in flight buffer IO for unmount serialisation o direct IO and DAX io path separation and simplification o shortform directory format definition changes for wider platform compatibility o various buffer cache fixes o cleanups in preparation for rmap merge o error injection cleanups and fixes o log item format buffer memory allocation restructuring to prevent rare OOM reclaim deadlocks o sparse inode chunks are now fully supported. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXmA5XAAoJEK3oKUf0dfodCc0QAKY5Jlfw5HwLria+Ad87HCcM Zi/LGMMC3CPh+vkbqsmDnLKHYjXRwi3HamBoXdufiE8E3UtOjp/sV98/fCw+zwhe tHDLmdAx23RLTn7gUhcsIXydKeXh0+HlRxPa4eBAlmnsJ3nGgrKrKQLgDT7Gjlum nPfRSTYjzm5gs2dpUTYhMV7MplenDW9GFz2uBMct6N9kYQ9m225I99fd/4nb/L7R o/8UocsK7iREUXP6decDoN9uIAzE2mYR720EL+Txy09CTYy+luNyGoNXOsQtxT5O plyoPZbzIIDvC44bvp6bZX96Udm7tAeTloieInCZG13I2zJy9gmTmLqkZ3M2at12 kOyeAMSBOWQYSa3uh++FsEP+JGtBTlZXf+4DAYf+U08s8tMVE/61/RZrtJZF4OjW hyumRBD6zqZ9Y6Qtji2HaA3l9IGxOC2k4URw9JZdDDyMoRTQvawN1QWNAeZINXiv 9ywqTruVsfQnoGDC1Gk1OEfQpubNztTAkEPqVM7ez5dkwOdwuOZXcZPL1Ltvb4Bt PLaWKLIYFYZKrM5kqgQlTERspSQA99++z8H9a21wFezfetaBby28fIqwMMfQAiSw nCq95WshJPwenogMtWjNfOgs/fqOBKdPdLFw0H6Jpmjwna2KpuFIZiTnwu25vvjz dHh4DVSuMTq1pBkXEU7B =vcSd -----END PGP SIGNATURE----- Merge tag 'xfs-for-linus-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs Pull xfs updates from Dave Chinner: "The major addition is the new iomap based block mapping infrastructure. We've been kicking this about locally for years, but there are other filesystems want to use it too (e.g. gfs2). Now it is fully working, reviewed and ready for merge and be used by other filesystems. There are a lot of other fixes and cleanups in the tree, but those are XFS internal things and none are of the scale or visibility of the iomap changes. See below for details. I am likely to send another pull request next week - we're just about ready to merge some new functionality (on disk block->owner reverse mapping infrastructure), but that's a huge chunk of code (74 files changed, 7283 insertions(+), 1114 deletions(-)) so I'm keeping that separate to all the "normal" pull request changes so they don't get lost in the noise. Summary of changes in this update: - generic iomap based IO path infrastructure - generic iomap based fiemap implementation - xfs iomap based Io path implementation - buffer error handling fixes - tracking of in flight buffer IO for unmount serialisation - direct IO and DAX io path separation and simplification - shortform directory format definition changes for wider platform compatibility - various buffer cache fixes - cleanups in preparation for rmap merge - error injection cleanups and fixes - log item format buffer memory allocation restructuring to prevent rare OOM reclaim deadlocks - sparse inode chunks are now fully supported" * tag 'xfs-for-linus-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs: (53 commits) xfs: remove EXPERIMENTAL tag from sparse inode feature xfs: bufferhead chains are invalid after end_page_writeback xfs: allocate log vector buffers outside CIL context lock libxfs: directory node splitting does not have an extra block xfs: remove dax code from object file when disabled xfs: skip dirty pages in ->releasepage() xfs: remove __arch_pack xfs: kill xfs_dir2_inou_t xfs: kill xfs_dir2_sf_off_t xfs: split direct I/O and DAX path xfs: direct calls in the direct I/O path xfs: stop using generic_file_read_iter for direct I/O xfs: split xfs_file_read_iter into buffered and direct I/O helpers xfs: remove s_maxbytes enforcement in xfs_file_read_iter xfs: kill ioflags xfs: don't pass ioflags around in the ioctl path xfs: track and serialize in-flight async buffers against unmount xfs: exclude never-released buffers from buftarg I/O accounting xfs: don't reset b_retries to 0 on every failure xfs: remove extraneous buffer flag changes ...
410 lines
10 KiB
C
410 lines
10 KiB
C
/*
|
|
* Copyright (c) 2014-2016 Christoph Hellwig.
|
|
*/
|
|
#include <linux/exportfs.h>
|
|
#include <linux/iomap.h>
|
|
#include <linux/genhd.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/pr.h>
|
|
|
|
#include <linux/nfsd/debug.h>
|
|
#include <scsi/scsi_proto.h>
|
|
#include <scsi/scsi_common.h>
|
|
|
|
#include "blocklayoutxdr.h"
|
|
#include "pnfs.h"
|
|
|
|
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
|
|
|
|
|
static __be32
|
|
nfsd4_block_proc_layoutget(struct inode *inode, const struct svc_fh *fhp,
|
|
struct nfsd4_layoutget *args)
|
|
{
|
|
struct nfsd4_layout_seg *seg = &args->lg_seg;
|
|
struct super_block *sb = inode->i_sb;
|
|
u32 block_size = (1 << inode->i_blkbits);
|
|
struct pnfs_block_extent *bex;
|
|
struct iomap iomap;
|
|
u32 device_generation = 0;
|
|
int error;
|
|
|
|
if (seg->offset & (block_size - 1)) {
|
|
dprintk("pnfsd: I/O misaligned\n");
|
|
goto out_layoutunavailable;
|
|
}
|
|
|
|
/*
|
|
* Some clients barf on non-zero block numbers for NONE or INVALID
|
|
* layouts, so make sure to zero the whole structure.
|
|
*/
|
|
error = -ENOMEM;
|
|
bex = kzalloc(sizeof(*bex), GFP_KERNEL);
|
|
if (!bex)
|
|
goto out_error;
|
|
args->lg_content = bex;
|
|
|
|
error = sb->s_export_op->map_blocks(inode, seg->offset, seg->length,
|
|
&iomap, seg->iomode != IOMODE_READ,
|
|
&device_generation);
|
|
if (error) {
|
|
if (error == -ENXIO)
|
|
goto out_layoutunavailable;
|
|
goto out_error;
|
|
}
|
|
|
|
if (iomap.length < args->lg_minlength) {
|
|
dprintk("pnfsd: extent smaller than minlength\n");
|
|
goto out_layoutunavailable;
|
|
}
|
|
|
|
switch (iomap.type) {
|
|
case IOMAP_MAPPED:
|
|
if (seg->iomode == IOMODE_READ)
|
|
bex->es = PNFS_BLOCK_READ_DATA;
|
|
else
|
|
bex->es = PNFS_BLOCK_READWRITE_DATA;
|
|
bex->soff = (iomap.blkno << 9);
|
|
break;
|
|
case IOMAP_UNWRITTEN:
|
|
if (seg->iomode & IOMODE_RW) {
|
|
/*
|
|
* Crack monkey special case from section 2.3.1.
|
|
*/
|
|
if (args->lg_minlength == 0) {
|
|
dprintk("pnfsd: no soup for you!\n");
|
|
goto out_layoutunavailable;
|
|
}
|
|
|
|
bex->es = PNFS_BLOCK_INVALID_DATA;
|
|
bex->soff = (iomap.blkno << 9);
|
|
break;
|
|
}
|
|
/*FALLTHRU*/
|
|
case IOMAP_HOLE:
|
|
if (seg->iomode == IOMODE_READ) {
|
|
bex->es = PNFS_BLOCK_NONE_DATA;
|
|
break;
|
|
}
|
|
/*FALLTHRU*/
|
|
case IOMAP_DELALLOC:
|
|
default:
|
|
WARN(1, "pnfsd: filesystem returned %d extent\n", iomap.type);
|
|
goto out_layoutunavailable;
|
|
}
|
|
|
|
error = nfsd4_set_deviceid(&bex->vol_id, fhp, device_generation);
|
|
if (error)
|
|
goto out_error;
|
|
bex->foff = iomap.offset;
|
|
bex->len = iomap.length;
|
|
|
|
seg->offset = iomap.offset;
|
|
seg->length = iomap.length;
|
|
|
|
dprintk("GET: 0x%llx:0x%llx %d\n", bex->foff, bex->len, bex->es);
|
|
return 0;
|
|
|
|
out_error:
|
|
seg->length = 0;
|
|
return nfserrno(error);
|
|
out_layoutunavailable:
|
|
seg->length = 0;
|
|
return nfserr_layoutunavailable;
|
|
}
|
|
|
|
static __be32
|
|
nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
|
|
struct iomap *iomaps, int nr_iomaps)
|
|
{
|
|
loff_t new_size = lcp->lc_last_wr + 1;
|
|
struct iattr iattr = { .ia_valid = 0 };
|
|
int error;
|
|
|
|
if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
|
|
timespec_compare(&lcp->lc_mtime, &inode->i_mtime) < 0)
|
|
lcp->lc_mtime = current_fs_time(inode->i_sb);
|
|
iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
|
|
iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
|
|
|
|
if (new_size > i_size_read(inode)) {
|
|
iattr.ia_valid |= ATTR_SIZE;
|
|
iattr.ia_size = new_size;
|
|
}
|
|
|
|
error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
|
|
nr_iomaps, &iattr);
|
|
kfree(iomaps);
|
|
return nfserrno(error);
|
|
}
|
|
|
|
#ifdef CONFIG_NFSD_BLOCKLAYOUT
|
|
static int
|
|
nfsd4_block_get_device_info_simple(struct super_block *sb,
|
|
struct nfsd4_getdeviceinfo *gdp)
|
|
{
|
|
struct pnfs_block_deviceaddr *dev;
|
|
struct pnfs_block_volume *b;
|
|
|
|
dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
|
|
sizeof(struct pnfs_block_volume), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
gdp->gd_device = dev;
|
|
|
|
dev->nr_volumes = 1;
|
|
b = &dev->volumes[0];
|
|
|
|
b->type = PNFS_BLOCK_VOLUME_SIMPLE;
|
|
b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
|
|
return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
|
|
&b->simple.offset);
|
|
}
|
|
|
|
static __be32
|
|
nfsd4_block_proc_getdeviceinfo(struct super_block *sb,
|
|
struct nfs4_client *clp,
|
|
struct nfsd4_getdeviceinfo *gdp)
|
|
{
|
|
if (sb->s_bdev != sb->s_bdev->bd_contains)
|
|
return nfserr_inval;
|
|
return nfserrno(nfsd4_block_get_device_info_simple(sb, gdp));
|
|
}
|
|
|
|
static __be32
|
|
nfsd4_block_proc_layoutcommit(struct inode *inode,
|
|
struct nfsd4_layoutcommit *lcp)
|
|
{
|
|
struct iomap *iomaps;
|
|
int nr_iomaps;
|
|
|
|
nr_iomaps = nfsd4_block_decode_layoutupdate(lcp->lc_up_layout,
|
|
lcp->lc_up_len, &iomaps, 1 << inode->i_blkbits);
|
|
if (nr_iomaps < 0)
|
|
return nfserrno(nr_iomaps);
|
|
|
|
return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps);
|
|
}
|
|
|
|
const struct nfsd4_layout_ops bl_layout_ops = {
|
|
/*
|
|
* Pretend that we send notification to the client. This is a blatant
|
|
* lie to force recent Linux clients to cache our device IDs.
|
|
* We rarely ever change the device ID, so the harm of leaking deviceids
|
|
* for a while isn't too bad. Unfortunately RFC5661 is a complete mess
|
|
* in this regard, but I filed errata 4119 for this a while ago, and
|
|
* hopefully the Linux client will eventually start caching deviceids
|
|
* without this again.
|
|
*/
|
|
.notify_types =
|
|
NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE,
|
|
.proc_getdeviceinfo = nfsd4_block_proc_getdeviceinfo,
|
|
.encode_getdeviceinfo = nfsd4_block_encode_getdeviceinfo,
|
|
.proc_layoutget = nfsd4_block_proc_layoutget,
|
|
.encode_layoutget = nfsd4_block_encode_layoutget,
|
|
.proc_layoutcommit = nfsd4_block_proc_layoutcommit,
|
|
};
|
|
#endif /* CONFIG_NFSD_BLOCKLAYOUT */
|
|
|
|
#ifdef CONFIG_NFSD_SCSILAYOUT
|
|
static int nfsd4_scsi_identify_device(struct block_device *bdev,
|
|
struct pnfs_block_volume *b)
|
|
{
|
|
struct request_queue *q = bdev->bd_disk->queue;
|
|
struct request *rq;
|
|
size_t bufflen = 252, len, id_len;
|
|
u8 *buf, *d, type, assoc;
|
|
int error;
|
|
|
|
buf = kzalloc(bufflen, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
rq = blk_get_request(q, READ, GFP_KERNEL);
|
|
if (IS_ERR(rq)) {
|
|
error = -ENOMEM;
|
|
goto out_free_buf;
|
|
}
|
|
blk_rq_set_block_pc(rq);
|
|
|
|
error = blk_rq_map_kern(q, rq, buf, bufflen, GFP_KERNEL);
|
|
if (error)
|
|
goto out_put_request;
|
|
|
|
rq->cmd[0] = INQUIRY;
|
|
rq->cmd[1] = 1;
|
|
rq->cmd[2] = 0x83;
|
|
rq->cmd[3] = bufflen >> 8;
|
|
rq->cmd[4] = bufflen & 0xff;
|
|
rq->cmd_len = COMMAND_SIZE(INQUIRY);
|
|
|
|
error = blk_execute_rq(rq->q, NULL, rq, 1);
|
|
if (error) {
|
|
pr_err("pNFS: INQUIRY 0x83 failed with: %x\n",
|
|
rq->errors);
|
|
goto out_put_request;
|
|
}
|
|
|
|
len = (buf[2] << 8) + buf[3] + 4;
|
|
if (len > bufflen) {
|
|
pr_err("pNFS: INQUIRY 0x83 response invalid (len = %zd)\n",
|
|
len);
|
|
goto out_put_request;
|
|
}
|
|
|
|
d = buf + 4;
|
|
for (d = buf + 4; d < buf + len; d += id_len + 4) {
|
|
id_len = d[3];
|
|
type = d[1] & 0xf;
|
|
assoc = (d[1] >> 4) & 0x3;
|
|
|
|
/*
|
|
* We only care about a EUI-64 and NAA designator types
|
|
* with LU association.
|
|
*/
|
|
if (assoc != 0x00)
|
|
continue;
|
|
if (type != 0x02 && type != 0x03)
|
|
continue;
|
|
if (id_len != 8 && id_len != 12 && id_len != 16)
|
|
continue;
|
|
|
|
b->scsi.code_set = PS_CODE_SET_BINARY;
|
|
b->scsi.designator_type = type == 0x02 ?
|
|
PS_DESIGNATOR_EUI64 : PS_DESIGNATOR_NAA;
|
|
b->scsi.designator_len = id_len;
|
|
memcpy(b->scsi.designator, d + 4, id_len);
|
|
|
|
/*
|
|
* If we found a 8 or 12 byte descriptor continue on to
|
|
* see if a 16 byte one is available. If we find a
|
|
* 16 byte descriptor we're done.
|
|
*/
|
|
if (id_len == 16)
|
|
break;
|
|
}
|
|
|
|
out_put_request:
|
|
blk_put_request(rq);
|
|
out_free_buf:
|
|
kfree(buf);
|
|
return error;
|
|
}
|
|
|
|
#define NFSD_MDS_PR_KEY 0x0100000000000000ULL
|
|
|
|
/*
|
|
* We use the client ID as a unique key for the reservations.
|
|
* This allows us to easily fence a client when recalls fail.
|
|
*/
|
|
static u64 nfsd4_scsi_pr_key(struct nfs4_client *clp)
|
|
{
|
|
return ((u64)clp->cl_clientid.cl_boot << 32) | clp->cl_clientid.cl_id;
|
|
}
|
|
|
|
static int
|
|
nfsd4_block_get_device_info_scsi(struct super_block *sb,
|
|
struct nfs4_client *clp,
|
|
struct nfsd4_getdeviceinfo *gdp)
|
|
{
|
|
struct pnfs_block_deviceaddr *dev;
|
|
struct pnfs_block_volume *b;
|
|
const struct pr_ops *ops;
|
|
int error;
|
|
|
|
dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
|
|
sizeof(struct pnfs_block_volume), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
gdp->gd_device = dev;
|
|
|
|
dev->nr_volumes = 1;
|
|
b = &dev->volumes[0];
|
|
|
|
b->type = PNFS_BLOCK_VOLUME_SCSI;
|
|
b->scsi.pr_key = nfsd4_scsi_pr_key(clp);
|
|
|
|
error = nfsd4_scsi_identify_device(sb->s_bdev, b);
|
|
if (error)
|
|
return error;
|
|
|
|
ops = sb->s_bdev->bd_disk->fops->pr_ops;
|
|
if (!ops) {
|
|
pr_err("pNFS: device %s does not support PRs.\n",
|
|
sb->s_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
error = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true);
|
|
if (error) {
|
|
pr_err("pNFS: failed to register key for device %s.\n",
|
|
sb->s_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
error = ops->pr_reserve(sb->s_bdev, NFSD_MDS_PR_KEY,
|
|
PR_EXCLUSIVE_ACCESS_REG_ONLY, 0);
|
|
if (error) {
|
|
pr_err("pNFS: failed to reserve device %s.\n",
|
|
sb->s_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __be32
|
|
nfsd4_scsi_proc_getdeviceinfo(struct super_block *sb,
|
|
struct nfs4_client *clp,
|
|
struct nfsd4_getdeviceinfo *gdp)
|
|
{
|
|
if (sb->s_bdev != sb->s_bdev->bd_contains)
|
|
return nfserr_inval;
|
|
return nfserrno(nfsd4_block_get_device_info_scsi(sb, clp, gdp));
|
|
}
|
|
static __be32
|
|
nfsd4_scsi_proc_layoutcommit(struct inode *inode,
|
|
struct nfsd4_layoutcommit *lcp)
|
|
{
|
|
struct iomap *iomaps;
|
|
int nr_iomaps;
|
|
|
|
nr_iomaps = nfsd4_scsi_decode_layoutupdate(lcp->lc_up_layout,
|
|
lcp->lc_up_len, &iomaps, 1 << inode->i_blkbits);
|
|
if (nr_iomaps < 0)
|
|
return nfserrno(nr_iomaps);
|
|
|
|
return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps);
|
|
}
|
|
|
|
static void
|
|
nfsd4_scsi_fence_client(struct nfs4_layout_stateid *ls)
|
|
{
|
|
struct nfs4_client *clp = ls->ls_stid.sc_client;
|
|
struct block_device *bdev = ls->ls_file->f_path.mnt->mnt_sb->s_bdev;
|
|
|
|
bdev->bd_disk->fops->pr_ops->pr_preempt(bdev, NFSD_MDS_PR_KEY,
|
|
nfsd4_scsi_pr_key(clp), 0, true);
|
|
}
|
|
|
|
const struct nfsd4_layout_ops scsi_layout_ops = {
|
|
/*
|
|
* Pretend that we send notification to the client. This is a blatant
|
|
* lie to force recent Linux clients to cache our device IDs.
|
|
* We rarely ever change the device ID, so the harm of leaking deviceids
|
|
* for a while isn't too bad. Unfortunately RFC5661 is a complete mess
|
|
* in this regard, but I filed errata 4119 for this a while ago, and
|
|
* hopefully the Linux client will eventually start caching deviceids
|
|
* without this again.
|
|
*/
|
|
.notify_types =
|
|
NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE,
|
|
.proc_getdeviceinfo = nfsd4_scsi_proc_getdeviceinfo,
|
|
.encode_getdeviceinfo = nfsd4_block_encode_getdeviceinfo,
|
|
.proc_layoutget = nfsd4_block_proc_layoutget,
|
|
.encode_layoutget = nfsd4_block_encode_layoutget,
|
|
.proc_layoutcommit = nfsd4_scsi_proc_layoutcommit,
|
|
.fence_client = nfsd4_scsi_fence_client,
|
|
};
|
|
#endif /* CONFIG_NFSD_SCSILAYOUT */
|