Commit 25641c0c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.18-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Highlights include:

  Stable fixes:
   - fix an NFSv4.1 state renewal regression
   - fix open/lock state recovery error handling
   - fix lock recovery when CREATE_SESSION/SETCLIENTID_CONFIRM fails
   - fix statd when reconnection fails
   - don't wake tasks during connection abort
   - don't start reboot recovery if lease check fails
   - fix duplicate proc entries

  Features:
  - pNFS block driver fixes and clean ups from Christoph
  - More code cleanups from Anna
  - Improve mmap() writeback performance
  - Replace use of PF_TRANS with a more generic mechanism for avoiding
    deadlocks in nfs_release_page"

* tag 'nfs-for-3.18-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (66 commits)
  NFSv4.1: Fix an NFSv4.1 state renewal regression
  NFSv4: fix open/lock state recovery error handling
  NFSv4: Fix lock recovery when CREATE_SESSION/SETCLIENTID_CONFIRM fails
  NFS: Fabricate fscache server index key correctly
  SUNRPC: Add missing support for RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT
  NFSv3: Fix missing includes of nfs3_fs.h
  NFS/SUNRPC: Remove other deadlock-avoidance mechanisms in nfs_release_page()
  NFS: avoid waiting at all in nfs_release_page when congested.
  NFS: avoid deadlocks with loop-back mounted NFS filesystems.
  MM: export page_wakeup functions
  SCHED: add some "wait..on_bit...timeout()" interfaces.
  NFS: don't use STABLE writes during writeback.
  NFSv4: use exponential retry on NFS4ERR_DELAY for async requests.
  rpc: Add -EPERM processing for xs_udp_send_request()
  rpc: return sent and err from xs_sendpages()
  lockd: Try to reconnect if statd has moved
  SUNRPC: Don't wake tasks during connection abort
  Fixing lease renewal
  nfs: fix duplicate proc entries
  pnfs/blocklayout: Fix a 64-bit division/remainder issue in bl_map_stripe
  ...
parents ef0625b7 72c23f08
...@@ -159,6 +159,12 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res, ...@@ -159,6 +159,12 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
msg.rpc_proc = &clnt->cl_procinfo[proc]; msg.rpc_proc = &clnt->cl_procinfo[proc];
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN); status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
if (status == -ECONNREFUSED) {
dprintk("lockd: NSM upcall RPC failed, status=%d, forcing rebind\n",
status);
rpc_force_rebind(clnt);
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
}
if (status < 0) if (status < 0)
dprintk("lockd: NSM upcall RPC failed, status=%d\n", dprintk("lockd: NSM upcall RPC failed, status=%d\n",
status); status);
......
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
# Makefile for the pNFS block layout driver kernel module # Makefile for the pNFS block layout driver kernel module
# #
obj-$(CONFIG_PNFS_BLOCK) += blocklayoutdriver.o obj-$(CONFIG_PNFS_BLOCK) += blocklayoutdriver.o
blocklayoutdriver-objs := blocklayout.o extents.o blocklayoutdev.o blocklayoutdm.o
blocklayoutdriver-y += blocklayout.o dev.o extent_tree.o rpc_pipefs.o
This diff is collapsed.
...@@ -44,105 +44,112 @@ ...@@ -44,105 +44,112 @@
#define PAGE_CACHE_SECTOR_SHIFT (PAGE_CACHE_SHIFT - SECTOR_SHIFT) #define PAGE_CACHE_SECTOR_SHIFT (PAGE_CACHE_SHIFT - SECTOR_SHIFT)
#define SECTOR_SIZE (1 << SECTOR_SHIFT) #define SECTOR_SIZE (1 << SECTOR_SHIFT)
struct block_mount_id { struct pnfs_block_dev;
spinlock_t bm_lock; /* protects list */
struct list_head bm_devlist; /* holds pnfs_block_dev */
};
struct pnfs_block_dev { enum pnfs_block_volume_type {
struct list_head bm_node; PNFS_BLOCK_VOLUME_SIMPLE = 0,
struct nfs4_deviceid bm_mdevid; /* associated devid */ PNFS_BLOCK_VOLUME_SLICE = 1,
struct block_device *bm_mdev; /* meta device itself */ PNFS_BLOCK_VOLUME_CONCAT = 2,
struct net *net; PNFS_BLOCK_VOLUME_STRIPE = 3,
}; };
enum exstate4 { #define PNFS_BLOCK_MAX_UUIDS 4
PNFS_BLOCK_READWRITE_DATA = 0, #define PNFS_BLOCK_MAX_DEVICES 64
PNFS_BLOCK_READ_DATA = 1,
PNFS_BLOCK_INVALID_DATA = 2, /* mapped, but data is invalid */ /*
PNFS_BLOCK_NONE_DATA = 3 /* unmapped, it's a hole */ * Random upper cap for the uuid length to avoid unbounded allocation.
* Not actually limited by the protocol.
*/
#define PNFS_BLOCK_UUID_LEN 128
struct pnfs_block_volume {
enum pnfs_block_volume_type type;
union {
struct {
int len;
int nr_sigs;
struct {
u64 offset;
u32 sig_len;
u8 sig[PNFS_BLOCK_UUID_LEN];
} sigs[PNFS_BLOCK_MAX_UUIDS];
} simple;
struct {
u64 start;
u64 len;
u32 volume;
} slice;
struct {
u32 volumes_count;
u32 volumes[PNFS_BLOCK_MAX_DEVICES];
} concat;
struct {
u64 chunk_size;
u32 volumes_count;
u32 volumes[PNFS_BLOCK_MAX_DEVICES];
} stripe;
};
}; };
#define MY_MAX_TAGS (15) /* tag bitnums used must be less than this */ struct pnfs_block_dev_map {
sector_t start;
sector_t len;
struct my_tree { sector_t disk_offset;
sector_t mtt_step_size; /* Internal sector alignment */ struct block_device *bdev;
struct list_head mtt_stub; /* Should be a radix tree */
}; };
struct pnfs_inval_markings { struct pnfs_block_dev {
spinlock_t im_lock; struct nfs4_deviceid_node node;
struct my_tree im_tree; /* Sectors that need LAYOUTCOMMIT */
sector_t im_block_size; /* Server blocksize in sectors */ u64 start;
struct list_head im_extents; /* Short extents for INVAL->RW conversion */ u64 len;
u32 nr_children;
struct pnfs_block_dev *children;
u64 chunk_size;
struct block_device *bdev;
u64 disk_offset;
bool (*map)(struct pnfs_block_dev *dev, u64 offset,
struct pnfs_block_dev_map *map);
}; };
struct pnfs_inval_tracking { enum exstate4 {
struct list_head it_link; PNFS_BLOCK_READWRITE_DATA = 0,
int it_sector; PNFS_BLOCK_READ_DATA = 1,
int it_tags; PNFS_BLOCK_INVALID_DATA = 2, /* mapped, but data is invalid */
PNFS_BLOCK_NONE_DATA = 3 /* unmapped, it's a hole */
}; };
/* sector_t fields are all in 512-byte sectors */ /* sector_t fields are all in 512-byte sectors */
struct pnfs_block_extent { struct pnfs_block_extent {
struct kref be_refcnt; union {
struct list_head be_node; /* link into lseg list */ struct rb_node be_node;
struct nfs4_deviceid be_devid; /* FIXME: could use device cache instead */ struct list_head be_list;
struct block_device *be_mdev; };
struct nfs4_deviceid_node *be_device;
sector_t be_f_offset; /* the starting offset in the file */ sector_t be_f_offset; /* the starting offset in the file */
sector_t be_length; /* the size of the extent */ sector_t be_length; /* the size of the extent */
sector_t be_v_offset; /* the starting offset in the volume */ sector_t be_v_offset; /* the starting offset in the volume */
enum exstate4 be_state; /* the state of this extent */ enum exstate4 be_state; /* the state of this extent */
struct pnfs_inval_markings *be_inval; /* tracks INVAL->RW transition */ #define EXTENT_WRITTEN 1
#define EXTENT_COMMITTING 2
unsigned int be_tag;
}; };
/* Shortened extent used by LAYOUTCOMMIT */ /* on the wire size of the extent */
struct pnfs_block_short_extent { #define BL_EXTENT_SIZE (7 * sizeof(__be32) + NFS4_DEVICEID4_SIZE)
struct list_head bse_node;
struct nfs4_deviceid bse_devid;
struct block_device *bse_mdev;
sector_t bse_f_offset; /* the starting offset in the file */
sector_t bse_length; /* the size of the extent */
};
static inline void
BL_INIT_INVAL_MARKS(struct pnfs_inval_markings *marks, sector_t blocksize)
{
spin_lock_init(&marks->im_lock);
INIT_LIST_HEAD(&marks->im_tree.mtt_stub);
INIT_LIST_HEAD(&marks->im_extents);
marks->im_block_size = blocksize;
marks->im_tree.mtt_step_size = min((sector_t)PAGE_CACHE_SECTORS,
blocksize);
}
enum extentclass4 {
RW_EXTENT = 0, /* READWRTE and INVAL */
RO_EXTENT = 1, /* READ and NONE */
EXTENT_LISTS = 2,
};
static inline int bl_choose_list(enum exstate4 state)
{
if (state == PNFS_BLOCK_READ_DATA || state == PNFS_BLOCK_NONE_DATA)
return RO_EXTENT;
else
return RW_EXTENT;
}
struct pnfs_block_layout { struct pnfs_block_layout {
struct pnfs_layout_hdr bl_layout; struct pnfs_layout_hdr bl_layout;
struct pnfs_inval_markings bl_inval; /* tracks INVAL->RW transition */ struct rb_root bl_ext_rw;
struct rb_root bl_ext_ro;
spinlock_t bl_ext_lock; /* Protects list manipulation */ spinlock_t bl_ext_lock; /* Protects list manipulation */
struct list_head bl_extents[EXTENT_LISTS]; /* R and RW extents */
struct list_head bl_commit; /* Needs layout commit */
struct list_head bl_committing; /* Layout committing */
unsigned int bl_count; /* entries in bl_commit */
sector_t bl_blocksize; /* Server blocksize in sectors */
}; };
#define BLK_ID(lo) ((struct block_mount_id *)(NFS_SERVER(lo->plh_inode)->pnfs_ld_data))
static inline struct pnfs_block_layout * static inline struct pnfs_block_layout *
BLK_LO2EXT(struct pnfs_layout_hdr *lo) BLK_LO2EXT(struct pnfs_layout_hdr *lo)
{ {
...@@ -171,41 +178,27 @@ struct bl_msg_hdr { ...@@ -171,41 +178,27 @@ struct bl_msg_hdr {
#define BL_DEVICE_REQUEST_PROC 0x1 /* User level process succeeds */ #define BL_DEVICE_REQUEST_PROC 0x1 /* User level process succeeds */
#define BL_DEVICE_REQUEST_ERR 0x2 /* User level process fails */ #define BL_DEVICE_REQUEST_ERR 0x2 /* User level process fails */
/* blocklayoutdev.c */ /* dev.c */
ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t); struct nfs4_deviceid_node *bl_alloc_deviceid_node(struct nfs_server *server,
void bl_pipe_destroy_msg(struct rpc_pipe_msg *); struct pnfs_device *pdev, gfp_t gfp_mask);
void nfs4_blkdev_put(struct block_device *bdev); void bl_free_deviceid_node(struct nfs4_deviceid_node *d);
struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server,
struct pnfs_device *dev); /* extent_tree.c */
int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, int ext_tree_insert(struct pnfs_block_layout *bl,
struct nfs4_layoutget_res *lgr, gfp_t gfp_flags); struct pnfs_block_extent *new);
int ext_tree_remove(struct pnfs_block_layout *bl, bool rw, sector_t start,
/* blocklayoutdm.c */ sector_t end);
void bl_free_block_dev(struct pnfs_block_dev *bdev); int ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
sector_t len);
/* extents.c */ bool ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect,
struct pnfs_block_extent * struct pnfs_block_extent *ret, bool rw);
bl_find_get_extent(struct pnfs_block_layout *bl, sector_t isect, int ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg);
struct pnfs_block_extent **cow_read); void ext_tree_mark_committed(struct nfs4_layoutcommit_args *arg, int status);
int bl_mark_sectors_init(struct pnfs_inval_markings *marks,
sector_t offset, sector_t length); /* rpc_pipefs.c */
void bl_put_extent(struct pnfs_block_extent *be); dev_t bl_resolve_deviceid(struct nfs_server *server,
struct pnfs_block_extent *bl_alloc_extent(void); struct pnfs_block_volume *b, gfp_t gfp_mask);
int bl_is_sector_init(struct pnfs_inval_markings *marks, sector_t isect); int __init bl_init_pipefs(void);
int encode_pnfs_block_layoutupdate(struct pnfs_block_layout *bl, void __exit bl_cleanup_pipefs(void);
struct xdr_stream *xdr,
const struct nfs4_layoutcommit_args *arg);
void clean_pnfs_block_layoutupdate(struct pnfs_block_layout *bl,
const struct nfs4_layoutcommit_args *arg,
int status);
int bl_add_merge_extent(struct pnfs_block_layout *bl,
struct pnfs_block_extent *new);
int bl_mark_for_commit(struct pnfs_block_extent *be,
sector_t offset, sector_t length,
struct pnfs_block_short_extent *new);
int bl_push_one_short_extent(struct pnfs_inval_markings *marks);
struct pnfs_block_short_extent *
bl_pop_one_short_extent(struct pnfs_inval_markings *marks);
void bl_free_short_extents(struct pnfs_inval_markings *marks, int num_to_free);
#endif /* FS_NFS_NFS4BLOCKLAYOUT_H */ #endif /* FS_NFS_NFS4BLOCKLAYOUT_H */
This diff is collapsed.
/*
* linux/fs/nfs/blocklayout/blocklayoutdm.c
*
* Module for the NFSv4.1 pNFS block layout driver.
*
* Copyright (c) 2007 The Regents of the University of Michigan.
* All rights reserved.
*
* Fred Isaman <iisaman@umich.edu>
* Andy Adamson <andros@citi.umich.edu>
*
* permission is granted to use, copy, create derivative works and
* redistribute this software and such derivative works for any purpose,
* so long as the name of the university of michigan is not used in
* any advertising or publicity pertaining to the use or distribution
* of this software without specific, written prior authorization. if
* the above copyright notice or any other identification of the
* university of michigan is included in any copy of any portion of
* this software, then the disclaimer below must also be included.
*
* this software is provided as is, without representation from the
* university of michigan as to its fitness for any purpose, and without
* warranty by the university of michigan of any kind, either express
* or implied, including without limitation the implied warranties of
* merchantability and fitness for a particular purpose. the regents
* of the university of michigan shall not be liable for any damages,
* including special, indirect, incidental, or consequential damages,
* with respect to any claim arising out or in connection with the use
* of the software, even if it has been or is hereafter advised of the
* possibility of such damages.
*/
#include <linux/genhd.h> /* gendisk - used in a dprintk*/
#include <linux/sched.h>
#include <linux/hash.h>
#include "blocklayout.h"
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
static void dev_remove(struct net *net, dev_t dev)
{
struct bl_pipe_msg bl_pipe_msg;
struct rpc_pipe_msg *msg = &bl_pipe_msg.msg;
struct bl_dev_msg bl_umount_request;
struct bl_msg_hdr bl_msg = {
.type = BL_DEVICE_UMOUNT,
.totallen = sizeof(bl_umount_request),
};
uint8_t *dataptr;
DECLARE_WAITQUEUE(wq, current);
struct nfs_net *nn = net_generic(net, nfs_net_id);
dprintk("Entering %s\n", __func__);
bl_pipe_msg.bl_wq = &nn->bl_wq;
memset(msg, 0, sizeof(*msg));
msg->len = sizeof(bl_msg) + bl_msg.totallen;
msg->data = kzalloc(msg->len, GFP_NOFS);
if (!msg->data)
goto out;
memset(&bl_umount_request, 0, sizeof(bl_umount_request));
bl_umount_request.major = MAJOR(dev);
bl_umount_request.minor = MINOR(dev);
memcpy(msg->data, &bl_msg, sizeof(bl_msg));
dataptr = (uint8_t *) msg->data;
memcpy(&dataptr[sizeof(bl_msg)], &bl_umount_request, sizeof(bl_umount_request));
add_wait_queue(&nn->bl_wq, &wq);
if (rpc_queue_upcall(nn->bl_device_pipe, msg) < 0) {
remove_wait_queue(&nn->bl_wq, &wq);
goto out;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
__set_current_state(TASK_RUNNING);
remove_wait_queue(&nn->bl_wq, &wq);
out:
kfree(msg->data);
}
/*
* Release meta device
*/
static void nfs4_blk_metadev_release(struct pnfs_block_dev *bdev)
{
dprintk("%s Releasing\n", __func__);
nfs4_blkdev_put(bdev->bm_mdev);
dev_remove(bdev->net, bdev->bm_mdev->bd_dev);
}
void bl_free_block_dev(struct pnfs_block_dev *bdev)
{
if (bdev) {
if (bdev->bm_mdev) {
dprintk("%s Removing DM device: %d:%d\n",
__func__,
MAJOR(bdev->bm_mdev->bd_dev),
MINOR(bdev->bm_mdev->bd_dev));
nfs4_blk_metadev_release(bdev);
}
kfree(bdev);
}
}
/*
* Copyright (c) 2014 Christoph Hellwig.
*/
#include <linux/sunrpc/svc.h>
#include <linux/blkdev.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_xdr.h>
#include "blocklayout.h"
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
static void
bl_free_device(struct pnfs_block_dev *dev)
{
if (dev->nr_children) {
int i;
for (i = 0; i < dev->nr_children; i++)
bl_free_device(&dev->children[i]);
kfree(dev->children);
} else {
if (dev->bdev)
blkdev_put(dev->bdev, FMODE_READ);
}
}
void
bl_free_deviceid_node(struct nfs4_deviceid_node *d)
{
struct pnfs_block_dev *dev =
container_of(d, struct pnfs_block_dev, node);
bl_free_device(dev);
kfree(dev);
}
static int
nfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
{
__be32 *p;
int i;
p = xdr_inline_decode(xdr, 4);
if (!p)
return -EIO;
b->type = be32_to_cpup(p++);
switch (b->type) {
case PNFS_BLOCK_VOLUME_SIMPLE:
p = xdr_inline_decode(xdr, 4);
if (!p)
return -EIO;
b->simple.nr_sigs = be32_to_cpup(p++);
if (!b->simple.nr_sigs) {
dprintk("no signature\n");
return -EIO;
}
b->simple.len = 4 + 4;
for (i = 0; i < b->simple.nr_sigs; i++) {
p = xdr_inline_decode(xdr, 8 + 4);
if (!p)
return -EIO;
p = xdr_decode_hyper(p, &b->simple.sigs[i].offset);
b->simple.sigs[i].sig_len = be32_to_cpup(p++);
p = xdr_inline_decode(xdr, b->simple.sigs[i].sig_len);
if (!p)
return -EIO;
memcpy(&b->simple.sigs[i].sig, p,
b->simple.sigs[i].sig_len);
b->simple.len += 8 + 4 + b->simple.sigs[i].sig_len;
}
break;
case PNFS_BLOCK_VOLUME_SLICE:
p = xdr_inline_decode(xdr, 8 + 8 + 4);
if (!p)
return -EIO;
p = xdr_decode_hyper(p, &b->slice.start);
p = xdr_decode_hyper(p, &b->slice.len);
b->slice.volume = be32_to_cpup(p++);
break;
case PNFS_BLOCK_VOLUME_CONCAT:
p = xdr_inline_decode(xdr, 4);
if (!p)
return -EIO;
b->concat.volumes_count = be32_to_cpup(p++);
p = xdr_inline_decode(xdr, b->concat.volumes_count * 4);
if (!p)
return -EIO;
for (i = 0; i < b->concat.volumes_count; i++)
b->concat.volumes[i] = be32_to_cpup(p++);
break;
case PNFS_BLOCK_VOLUME_STRIPE:
p = xdr_inline_decode(xdr, 8 + 4);
if (!p)
return -EIO;
p = xdr_decode_hyper(p, &b->stripe.chunk_size);
b->stripe.volumes_count = be32_to_cpup(p++);
p = xdr_inline_decode(xdr, b->stripe.volumes_count * 4);
if (!p)
return -EIO;
for (i = 0; i < b->stripe.volumes_count; i++)
b->stripe.volumes[i] = be32_to_cpup(p++);
break;
default:
dprintk("unknown volume type!\n");
return -EIO;
}
return 0;
}
static bool bl_map_simple(struct pnfs_block_dev *dev, u64 offset,
struct pnfs_block_dev_map *map)
{
map->start = dev->start;
map->len = dev->len;
map->disk_offset = dev->disk_offset;
map->bdev = dev->bdev;
return true;
}
static bool bl_map_concat(struct pnfs_block_dev *dev, u64 offset,
struct pnfs_block_dev_map *map)
{
int i;
for (i = 0; i < dev->nr_children; i++) {
struct pnfs_block_dev *child = &dev->children[i];
if (child->start > offset ||
child->start + child->len <= offset)
continue;
child->map(child, offset - child->start, map);
return true;
}
dprintk("%s: ran off loop!\n", __func__);
return false;
}
static bool bl_map_stripe(struct pnfs_block_dev *dev, u64 offset,
struct pnfs_block_dev_map *map)
{
struct pnfs_block_dev *child;
u64 chunk;
u32 chunk_idx;
u64 disk_offset;
chunk = div_u64(offset, dev->chunk_size);
div_u64_rem(chunk, dev->nr_children, &chunk_idx);
if (chunk_idx > dev->nr_children) {
dprintk("%s: invalid chunk idx %d (%lld/%lld)\n",
__func__, chunk_idx, offset, dev->chunk_size);
/* error, should not happen */
return false;
}
/* truncate offset to the beginning of the stripe */
offset = chunk * dev->chunk_size;
/* disk offset of the stripe */
disk_offset = div_u64(offset, dev->nr_children);
child = &dev->children[chunk_idx];
child->map(child, disk_offset, map);
map->start += offset;
map->disk_offset += disk_offset;
map->len = dev->chunk_size;
return true;
}
static int
bl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask);
static int
bl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
struct pnfs_block_volume *v = &volumes[idx];
dev_t dev;
dev = bl_resolve_deviceid(server, v, gfp_mask);
if (!dev)
return -EIO;
d->bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
if (IS_ERR(d->bdev)) {
printk(KERN_WARNING "pNFS: failed to open device %d:%d (%ld)\n",
MAJOR(dev), MINOR(dev), PTR_ERR(d->bdev));
return PTR_ERR(d->bdev);
}
d->len = i_size_read(d->bdev->bd_inode);
d->map = bl_map_simple;
printk(KERN_INFO "pNFS: using block device %s\n",
d->bdev->bd_disk->disk_name);
return 0;
}
static int
bl_parse_slice(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
struct pnfs_block_volume *v = &volumes[idx];
int ret;
ret = bl_parse_deviceid(server, d, volumes, v->slice.volume, gfp_mask);
if (ret)
return ret;
d->disk_offset = v->slice.start;
d->len = v->slice.len;
return 0;
}
static int
bl_parse_concat(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
struct pnfs_block_volume *v = &volumes[idx];
u64 len = 0;
int ret, i;
d->children = kcalloc(v->concat.volumes_count,
sizeof(struct pnfs_block_dev), GFP_KERNEL);
if (!d->children)
return -ENOMEM;
for (i = 0; i < v->concat.volumes_count; i++) {
ret = bl_parse_deviceid(server, &d->children[i],
volumes, v->concat.volumes[i], gfp_mask);
if (ret)
return ret;
d->nr_children++;
d->children[i].start += len;
len += d->children[i].len;
}
d->len = len;
d->map = bl_map_concat;
return 0;
}
static int
bl_parse_stripe(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
struct pnfs_block_volume *v = &volumes[idx];
u64 len = 0;
int ret, i;
d->children = kcalloc(v->stripe.volumes_count,
sizeof(struct pnfs_block_dev), GFP_KERNEL);
if (!d->children)
return -ENOMEM;
for (i = 0; i < v->stripe.volumes_count; i++) {
ret = bl_parse_deviceid(server, &d->children[i],
volumes, v->stripe.volumes[i], gfp_mask);
if (ret)
return ret;
d->nr_children++;
len += d->children[i].len;
}
d->len = len;
d->chunk_size = v->stripe.chunk_size;
d->map = bl_map_stripe;
return 0;
}
static int
bl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
switch (volumes[idx].type) {
case PNFS_BLOCK_VOLUME_SIMPLE:
return bl_parse_simple(server, d, volumes, idx, gfp_mask);
case PNFS_BLOCK_VOLUME_SLICE:
return bl_parse_slice(server, d, volumes, idx, gfp_mask);
case PNFS_BLOCK_VOLUME_CONCAT:
return bl_parse_concat(server, d, volumes, idx, gfp_mask);
case PNFS_BLOCK_VOLUME_STRIPE:
return bl_parse_stripe(server, d, volumes, idx, gfp_mask);
default:
dprintk("unsupported volume type: %d\n", volumes[idx].type);
return -EIO;
}
}
struct nfs4_deviceid_node *
bl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
gfp_t gfp_mask)
{
struct nfs4_deviceid_node *node = NULL;
struct pnfs_block_volume *volumes;
struct pnfs_block_dev *top;
struct xdr_stream xdr;
struct xdr_buf buf;
struct page *scratch;
int nr_volumes, ret, i;
__be32 *p;
scratch = alloc_page(gfp_mask);
if (!scratch)
goto out;
xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen);
xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE);
p = xdr_inline_decode(&xdr, sizeof(__be32));
if (!p)
goto out_free_scratch;
nr_volumes = be32_to_cpup(p++);
volumes = kcalloc(nr_volumes, sizeof(struct pnfs_block_volume),
gfp_mask);
if (!volumes)
goto out_free_scratch;
for (i = 0; i < nr_volumes; i++) {
ret = nfs4_block_decode_volume(&xdr, &volumes[i]);
if (ret < 0)
goto out_free_volumes;
}
top = kzalloc(sizeof(*top), gfp_mask);
if (!top)
goto out_free_volumes;
ret = bl_parse_deviceid(server, top, volumes, nr_volumes - 1, gfp_mask);
if (ret) {
bl_free_device(top);
kfree(top);
goto out_free_volumes;
}
node = &top->node;
nfs4_init_deviceid_node(node, server, &pdev->dev_id);
out_free_volumes:
kfree(volumes);
out_free_scratch:
__free_page(scratch);
out:
return node;
}
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2006,2007 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@citi.umich.edu>
* Fred Isaman <iisaman@umich.edu>
*
* permission is granted to use, copy, create derivative works and
* redistribute this software and such derivative works for any purpose,
* so long as the name of the university of michigan is not used in
* any advertising or publicity pertaining to the use or distribution
* of this software without specific, written prior authorization. if
* the above copyright notice or any other identification of the
* university of michigan is included in any copy of any portion of
* this software, then the disclaimer below must also be included.
*
* this software is provided as is, without representation from the
* university of michigan as to its fitness for any purpose, and without
* warranty by the university of michigan of any kind, either express
* or implied, including without limitation the implied warranties of
* merchantability and fitness for a particular purpose. the regents
* of the university of michigan shall not be liable for any damages,
* including special, indirect, incidental, or consequential damages,
* with respect to any claim arising out or in connection with the use
* of the software, even if it has been or is hereafter advised of the
* possibility of such damages.
*/
#include <linux/module.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include "blocklayout.h"
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
static void
nfs4_encode_simple(__be32 *p, struct pnfs_block_volume *b)
{
int i;
*p++ = cpu_to_be32(1);
*p++ = cpu_to_be32(b->type);
*p++ = cpu_to_be32(b->simple.nr_sigs);
for (i = 0; i < b->simple.nr_sigs; i++) {
p = xdr_encode_hyper(p, b->simple.sigs[i].offset);
p = xdr_encode_opaque(p, b->simple.sigs[i].sig,
b->simple.sigs[i].sig_len);
}
}
dev_t
bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
gfp_t gfp_mask)
{
struct net *net = server->nfs_client->cl_net;
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct bl_dev_msg *reply = &nn->bl_mount_reply;
struct bl_pipe_msg bl_pipe_msg;
struct rpc_pipe_msg *msg = &bl_pipe_msg.msg;
struct bl_msg_hdr *bl_msg;
DECLARE_WAITQUEUE(wq, current);
dev_t dev = 0;
int rc;
dprintk("%s CREATING PIPEFS MESSAGE\n", __func__);
bl_pipe_msg.bl_wq = &nn->bl_wq;
b->simple.len += 4; /* single volume */
if (b->simple.len > PAGE_SIZE)
return -EIO;
memset(msg, 0, sizeof(*msg));
msg->len = sizeof(*bl_msg) + b->simple.len;
msg->data = kzalloc(msg->len, gfp_mask);
if (!msg->data)
goto out;
bl_msg = msg->data;
bl_msg->type = BL_DEVICE_MOUNT,
bl_msg->totallen = b->simple.len;
nfs4_encode_simple(msg->data + sizeof(*bl_msg), b);
dprintk("%s CALLING USERSPACE DAEMON\n", __func__);
add_wait_queue(&nn->bl_wq, &wq);
rc = rpc_queue_upcall(nn->bl_device_pipe, msg);
if (rc < 0) {
remove_wait_queue(&nn->bl_wq, &wq);
goto out;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
__set_current_state(TASK_RUNNING);
remove_wait_queue(&nn->bl_wq, &wq);
if (reply->status != BL_DEVICE_REQUEST_PROC) {
printk(KERN_WARNING "%s failed to decode device: %d\n",
__func__, reply->status);
goto out;
}
dev = MKDEV(reply->major, reply->minor);
out:
kfree(msg->data);
return dev;
}
static ssize_t bl_pipe_downcall(struct file *filp, const char __user *src,
size_t mlen)
{
struct nfs_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
nfs_net_id);
if (mlen != sizeof (struct bl_dev_msg))
return -EINVAL;
if (copy_from_user(&nn->bl_mount_reply, src, mlen) != 0)
return -EFAULT;
wake_up(&nn->bl_wq);
return mlen;
}
static void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
struct bl_pipe_msg *bl_pipe_msg =
container_of(msg, struct bl_pipe_msg, msg);
if (msg->errno >= 0)
return;
wake_up(bl_pipe_msg->bl_wq);
}
static const struct rpc_pipe_ops bl_upcall_ops = {
.upcall = rpc_pipe_generic_upcall,
.downcall = bl_pipe_downcall,
.destroy_msg = bl_pipe_destroy_msg,
};
static struct dentry *nfs4blocklayout_register_sb(struct super_block *sb,
struct rpc_pipe *pipe)
{
struct dentry *dir, *dentry;
dir = rpc_d_lookup_sb(sb, NFS_PIPE_DIRNAME);
if (dir == NULL)
return ERR_PTR(-ENOENT);
dentry = rpc_mkpipe_dentry(dir, "blocklayout", NULL, pipe);
dput(dir);
return dentry;
}
static void nfs4blocklayout_unregister_sb(struct super_block *sb,
struct rpc_pipe *pipe)
{
if (pipe->dentry)
rpc_unlink(pipe->dentry);
}
static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct super_block *sb = ptr;
struct net *net = sb->s_fs_info;
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct dentry *dentry;
int ret = 0;
if (!try_module_get(THIS_MODULE))
return 0;
if (nn->bl_device_pipe == NULL) {
module_put(THIS_MODULE);
return 0;
}
switch (event) {
case RPC_PIPEFS_MOUNT:
dentry = nfs4blocklayout_register_sb(sb, nn->bl_device_pipe);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
break;
}
nn->bl_device_pipe->dentry = dentry;
break;
case RPC_PIPEFS_UMOUNT:
if (nn->bl_device_pipe->dentry)
nfs4blocklayout_unregister_sb(sb, nn->bl_device_pipe);
break;
default:
ret = -ENOTSUPP;
break;
}
module_put(THIS_MODULE);
return ret;
}
static struct notifier_block nfs4blocklayout_block = {
.notifier_call = rpc_pipefs_event,
};
static struct dentry *nfs4blocklayout_register_net(struct net *net,
struct rpc_pipe *pipe)
{
struct super_block *pipefs_sb;
struct dentry *dentry;
pipefs_sb = rpc_get_sb_net(net);
if (!pipefs_sb)
return NULL;
dentry = nfs4blocklayout_register_sb(pipefs_sb, pipe);
rpc_put_sb_net(net);
return dentry;
}
static void nfs4blocklayout_unregister_net(struct net *net,
struct rpc_pipe *pipe)
{
struct super_block *pipefs_sb;
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
nfs4blocklayout_unregister_sb(pipefs_sb, pipe);
rpc_put_sb_net(net);
}
}
static int nfs4blocklayout_net_init(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct dentry *dentry;
init_waitqueue_head(&nn->bl_wq);
nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0);
if (IS_ERR(nn->bl_device_pipe))
return PTR_ERR(nn->bl_device_pipe);
dentry = nfs4blocklayout_register_net(net, nn->bl_device_pipe);
if (IS_ERR(dentry)) {
rpc_destroy_pipe_data(nn->bl_device_pipe);
return PTR_ERR(dentry);
}
nn->bl_device_pipe->dentry = dentry;
return 0;
}
static void nfs4blocklayout_net_exit(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
nfs4blocklayout_unregister_net(net, nn->bl_device_pipe);
rpc_destroy_pipe_data(nn->bl_device_pipe);
nn->bl_device_pipe = NULL;
}
static struct pernet_operations nfs4blocklayout_net_ops = {
.init = nfs4blocklayout_net_init,
.exit = nfs4blocklayout_net_exit,
};
int __init bl_init_pipefs(void)
{
int ret;
ret = rpc_pipefs_notifier_register(&nfs4blocklayout_block);
if (ret)
goto out;
ret = register_pernet_subsys(&nfs4blocklayout_net_ops);
if (ret)
goto out_unregister_notifier;
return 0;
out_unregister_notifier:
rpc_pipefs_notifier_unregister(&nfs4blocklayout_block);
out:
return ret;
}
void __exit bl_cleanup_pipefs(void)
{
rpc_pipefs_notifier_unregister(&nfs4blocklayout_block);
unregister_pernet_subsys(&nfs4blocklayout_net_ops);
}
...@@ -171,14 +171,26 @@ static u32 initiate_file_draining(struct nfs_client *clp, ...@@ -171,14 +171,26 @@ static u32 initiate_file_draining(struct nfs_client *clp,
goto out; goto out;
ino = lo->plh_inode; ino = lo->plh_inode;
spin_lock(&ino->i_lock);
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
spin_unlock(&ino->i_lock);
pnfs_layoutcommit_inode(ino, false);
spin_lock(&ino->i_lock); spin_lock(&ino->i_lock);
if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) || if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
pnfs_mark_matching_lsegs_invalid(lo, &free_me_list, pnfs_mark_matching_lsegs_invalid(lo, &free_me_list,
&args->cbl_range)) &args->cbl_range)) {
rv = NFS4ERR_DELAY; rv = NFS4ERR_DELAY;
else goto unlock;
rv = NFS4ERR_NOMATCHING_LAYOUT; }
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
&args->cbl_range);
}
unlock:
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&free_me_list); pnfs_free_lseg_list(&free_me_list);
pnfs_put_layout_hdr(lo); pnfs_put_layout_hdr(lo);
...@@ -277,9 +289,6 @@ __be32 nfs4_callback_devicenotify(struct cb_devicenotifyargs *args, ...@@ -277,9 +289,6 @@ __be32 nfs4_callback_devicenotify(struct cb_devicenotifyargs *args,
} }
found: found:
if (dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE)
dprintk("%s: NOTIFY_DEVICEID4_CHANGE not supported, "
"deleting instead\n", __func__);
nfs4_delete_deviceid(server->pnfs_curr_ld, clp, &dev->cbd_dev_id); nfs4_delete_deviceid(server->pnfs_curr_ld, clp, &dev->cbd_dev_id);
} }
......
...@@ -1252,6 +1252,7 @@ static int nfs_server_list_open(struct inode *inode, struct file *file) ...@@ -1252,6 +1252,7 @@ static int nfs_server_list_open(struct inode *inode, struct file *file)
* set up the iterator to start reading from the server list and return the first item * set up the iterator to start reading from the server list and return the first item
*/ */
static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
__acquires(&nn->nfs_client_lock)
{ {
struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
...@@ -1274,6 +1275,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) ...@@ -1274,6 +1275,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
* clean up after reading from the transports list * clean up after reading from the transports list
*/ */
static void nfs_server_list_stop(struct seq_file *p, void *v) static void nfs_server_list_stop(struct seq_file *p, void *v)
__releases(&nn->nfs_client_lock)
{ {
struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
...@@ -1318,7 +1320,7 @@ static int nfs_server_list_show(struct seq_file *m, void *v) ...@@ -1318,7 +1320,7 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
*/ */
static int nfs_volume_list_open(struct inode *inode, struct file *file) static int nfs_volume_list_open(struct inode *inode, struct file *file)
{ {
return seq_open_net(inode, file, &nfs_server_list_ops, return seq_open_net(inode, file, &nfs_volume_list_ops,
sizeof(struct seq_net_private)); sizeof(struct seq_net_private));
} }
...@@ -1326,6 +1328,7 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file) ...@@ -1326,6 +1328,7 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file)
* set up the iterator to start reading from the volume list and return the first item * set up the iterator to start reading from the volume list and return the first item
*/ */
static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
__acquires(&nn->nfs_client_lock)
{ {
struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
...@@ -1348,6 +1351,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) ...@@ -1348,6 +1351,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
* clean up after reading from the transports list * clean up after reading from the transports list
*/ */
static void nfs_volume_list_stop(struct seq_file *p, void *v) static void nfs_volume_list_stop(struct seq_file *p, void *v)
__releases(&nn->nfs_client_lock)
{ {
struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
......
...@@ -178,7 +178,6 @@ static int nfs_direct_set_or_cmp_hdr_verf(struct nfs_direct_req *dreq, ...@@ -178,7 +178,6 @@ static int nfs_direct_set_or_cmp_hdr_verf(struct nfs_direct_req *dreq,
return memcmp(verfp, &hdr->verf, sizeof(struct nfs_writeverf)); return memcmp(verfp, &hdr->verf, sizeof(struct nfs_writeverf));
} }
#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
/* /*
* nfs_direct_cmp_commit_data_verf - compare verifier for commit data * nfs_direct_cmp_commit_data_verf - compare verifier for commit data
* @dreq - direct request possibly spanning multiple servers * @dreq - direct request possibly spanning multiple servers
...@@ -197,7 +196,6 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq, ...@@ -197,7 +196,6 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq,
WARN_ON_ONCE(verfp->committed < 0); WARN_ON_ONCE(verfp->committed < 0);
return memcmp(verfp, &data->verf, sizeof(struct nfs_writeverf)); return memcmp(verfp, &data->verf, sizeof(struct nfs_writeverf));
} }
#endif
/** /**
* nfs_direct_IO - NFS address space operation for direct I/O * nfs_direct_IO - NFS address space operation for direct I/O
...@@ -576,7 +574,6 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, ...@@ -576,7 +574,6 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter,
return result; return result;
} }
#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
{ {
struct nfs_pageio_descriptor desc; struct nfs_pageio_descriptor desc;
...@@ -700,17 +697,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode ...@@ -700,17 +697,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
schedule_work(&dreq->work); /* Calls nfs_direct_write_schedule_work */ schedule_work(&dreq->work); /* Calls nfs_direct_write_schedule_work */
} }
#else
static void nfs_direct_write_schedule_work(struct work_struct *work)
{
}
static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
{
nfs_direct_complete(dreq, true);
}
#endif
static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
{ {
struct nfs_direct_req *dreq = hdr->dreq; struct nfs_direct_req *dreq = hdr->dreq;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "internal.h" #include "internal.h"
#include "iostat.h" #include "iostat.h"
#include "fscache.h" #include "fscache.h"
#include "pnfs.h"
#include "nfstrace.h" #include "nfstrace.h"
...@@ -327,6 +328,12 @@ static int nfs_want_read_modify_write(struct file *file, struct page *page, ...@@ -327,6 +328,12 @@ static int nfs_want_read_modify_write(struct file *file, struct page *page,
unsigned int offset = pos & (PAGE_CACHE_SIZE - 1); unsigned int offset = pos & (PAGE_CACHE_SIZE - 1);
unsigned int end = offset + len; unsigned int end = offset + len;
if (pnfs_ld_read_whole_page(file->f_mapping->host)) {
if (!PageUptodate(page))
return 1;
return 0;
}
if ((file->f_mode & FMODE_READ) && /* open for read? */ if ((file->f_mode & FMODE_READ) && /* open for read? */
!PageUptodate(page) && /* Uptodate? */ !PageUptodate(page) && /* Uptodate? */
!PagePrivate(page) && /* i/o request already? */ !PagePrivate(page) && /* i/o request already? */
...@@ -468,17 +475,26 @@ static int nfs_release_page(struct page *page, gfp_t gfp) ...@@ -468,17 +475,26 @@ static int nfs_release_page(struct page *page, gfp_t gfp)
dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page); dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
/* Only do I/O if gfp is a superset of GFP_KERNEL, and we're not /* Always try to initiate a 'commit' if relevant, but only
* doing this memory reclaim for a fs-related allocation. * wait for it if __GFP_WAIT is set. Even then, only wait 1
* second and only if the 'bdi' is not congested.
* Waiting indefinitely can cause deadlocks when the NFS
* server is on this machine, when a new TCP connection is
* needed and in other rare cases. There is no particular
* need to wait extensively here. A short wait has the
* benefit that someone else can worry about the freezer.
*/ */
if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL && if (mapping) {
!(current->flags & PF_FSTRANS)) { struct nfs_server *nfss = NFS_SERVER(mapping->host);
int how = FLUSH_SYNC; nfs_commit_inode(mapping->host, 0);
if ((gfp & __GFP_WAIT) &&
/* Don't let kswapd deadlock waiting for OOM RPC calls */ !bdi_write_congested(&nfss->backing_dev_info)) {
if (current_is_kswapd()) wait_on_page_bit_killable_timeout(page, PG_private,
how = 0; HZ);
nfs_commit_inode(mapping->host, how); if (PagePrivate(page))
set_bdi_congested(&nfss->backing_dev_info,
BLK_RW_ASYNC);
}
} }
/* If PagePrivate() is set, then the page is not freeable */ /* If PagePrivate() is set, then the page is not freeable */
if (PagePrivate(page)) if (PagePrivate(page))
...@@ -539,13 +555,25 @@ static int nfs_launder_page(struct page *page) ...@@ -539,13 +555,25 @@ static int nfs_launder_page(struct page *page)
static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file, static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
sector_t *span) sector_t *span)
{ {
int ret;
struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host);
*span = sis->pages; *span = sis->pages;
return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 1);
rcu_read_lock();
ret = xs_swapper(rcu_dereference(clnt->cl_xprt), 1);
rcu_read_unlock();
return ret;
} }
static void nfs_swap_deactivate(struct file *file) static void nfs_swap_deactivate(struct file *file)
{ {
xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 0); struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host);
rcu_read_lock();
xs_swapper(rcu_dereference(clnt->cl_xprt), 0);
rcu_read_unlock();
} }
#endif #endif
......
...@@ -265,7 +265,7 @@ filelayout_set_layoutcommit(struct nfs_pgio_header *hdr) ...@@ -265,7 +265,7 @@ filelayout_set_layoutcommit(struct nfs_pgio_header *hdr)
{ {
if (FILELAYOUT_LSEG(hdr->lseg)->commit_through_mds || if (FILELAYOUT_LSEG(hdr->lseg)->commit_through_mds ||
hdr->res.verf->committed == NFS_FILE_SYNC) hdr->res.verf->committed != NFS_DATA_SYNC)
return; return;
pnfs_set_layoutcommit(hdr); pnfs_set_layoutcommit(hdr);
...@@ -403,6 +403,9 @@ static int filelayout_commit_done_cb(struct rpc_task *task, ...@@ -403,6 +403,9 @@ static int filelayout_commit_done_cb(struct rpc_task *task,
return -EAGAIN; return -EAGAIN;
} }
if (data->verf.committed == NFS_UNSTABLE)
pnfs_commit_set_layoutcommit(data);
return 0; return 0;
} }
...@@ -646,18 +649,15 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo, ...@@ -646,18 +649,15 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo,
} }
/* find and reference the deviceid */ /* find and reference the deviceid */
d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode)->pnfs_curr_ld, d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), id,
NFS_SERVER(lo->plh_inode)->nfs_client, id); lo->plh_lc_cred, gfp_flags);
if (d == NULL) { if (d == NULL)
dsaddr = filelayout_get_device_info(lo->plh_inode, id, goto out;
lo->plh_lc_cred, gfp_flags);
if (dsaddr == NULL) dsaddr = container_of(d, struct nfs4_file_layout_dsaddr, id_node);
goto out;
} else
dsaddr = container_of(d, struct nfs4_file_layout_dsaddr, id_node);
/* Found deviceid is unavailable */ /* Found deviceid is unavailable */
if (filelayout_test_devid_unavailable(&dsaddr->id_node)) if (filelayout_test_devid_unavailable(&dsaddr->id_node))
goto out_put; goto out_put;
fl->dsaddr = dsaddr; fl->dsaddr = dsaddr;
...@@ -1368,6 +1368,17 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, ...@@ -1368,6 +1368,17 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
cinfo->ds->ncommitting = 0; cinfo->ds->ncommitting = 0;
return PNFS_ATTEMPTED; return PNFS_ATTEMPTED;
} }
static struct nfs4_deviceid_node *
filelayout_alloc_deviceid_node(struct nfs_server *server,
struct pnfs_device *pdev, gfp_t gfp_flags)
{
struct nfs4_file_layout_dsaddr *dsaddr;
dsaddr = nfs4_fl_alloc_deviceid_node(server, pdev, gfp_flags);
if (!dsaddr)
return NULL;
return &dsaddr->id_node;
}
static void static void
filelayout_free_deveiceid_node(struct nfs4_deviceid_node *d) filelayout_free_deveiceid_node(struct nfs4_deviceid_node *d)
...@@ -1420,6 +1431,7 @@ static struct pnfs_layoutdriver_type filelayout_type = { ...@@ -1420,6 +1431,7 @@ static struct pnfs_layoutdriver_type filelayout_type = {
.commit_pagelist = filelayout_commit_pagelist, .commit_pagelist = filelayout_commit_pagelist,
.read_pagelist = filelayout_read_pagelist, .read_pagelist = filelayout_read_pagelist,
.write_pagelist = filelayout_write_pagelist, .write_pagelist = filelayout_write_pagelist,
.alloc_deviceid_node = filelayout_alloc_deviceid_node,
.free_deviceid_node = filelayout_free_deveiceid_node, .free_deviceid_node = filelayout_free_deveiceid_node,
}; };
......
...@@ -147,10 +147,11 @@ u32 nfs4_fl_calc_j_index(struct pnfs_layout_segment *lseg, loff_t offset); ...@@ -147,10 +147,11 @@ u32 nfs4_fl_calc_j_index(struct pnfs_layout_segment *lseg, loff_t offset);
u32 nfs4_fl_calc_ds_index(struct pnfs_layout_segment *lseg, u32 j); u32 nfs4_fl_calc_ds_index(struct pnfs_layout_segment *lseg, u32 j);
struct nfs4_pnfs_ds *nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, struct nfs4_pnfs_ds *nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg,
u32 ds_idx); u32 ds_idx);
extern struct nfs4_file_layout_dsaddr *
nfs4_fl_alloc_deviceid_node(struct nfs_server *server,
struct pnfs_device *pdev, gfp_t gfp_flags);
extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr); extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);
extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr); extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);
struct nfs4_file_layout_dsaddr *
filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id,
struct rpc_cred *cred, gfp_t gfp_flags);
#endif /* FS_NFS_NFS4FILELAYOUT_H */ #endif /* FS_NFS_NFS4FILELAYOUT_H */
This diff is collapsed.
...@@ -74,11 +74,10 @@ static uint16_t nfs_server_get_key(const void *cookie_netfs_data, ...@@ -74,11 +74,10 @@ static uint16_t nfs_server_get_key(const void *cookie_netfs_data,
struct nfs_server_key *key = buffer; struct nfs_server_key *key = buffer;
uint16_t len = sizeof(struct nfs_server_key); uint16_t len = sizeof(struct nfs_server_key);
memset(key, 0, len);
key->nfsversion = clp->rpc_ops->version; key->nfsversion = clp->rpc_ops->version;
key->family = clp->cl_addr.ss_family; key->family = clp->cl_addr.ss_family;
memset(key, 0, len);
switch (clp->cl_addr.ss_family) { switch (clp->cl_addr.ss_family) {
case AF_INET: case AF_INET:
key->port = sin->sin_port; key->port = sin->sin_port;
......
...@@ -505,7 +505,9 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -505,7 +505,9 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
attr->ia_valid &= ~ATTR_MODE; attr->ia_valid &= ~ATTR_MODE;
if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode)) BUG_ON(!S_ISREG(inode->i_mode));
if (attr->ia_size == i_size_read(inode))
attr->ia_valid &= ~ATTR_SIZE; attr->ia_valid &= ~ATTR_SIZE;
} }
......
...@@ -218,13 +218,6 @@ static inline void nfs_fs_proc_exit(void) ...@@ -218,13 +218,6 @@ static inline void nfs_fs_proc_exit(void)
int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *); int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *);
#endif #endif
/* nfs3client.c */
#if IS_ENABLED(CONFIG_NFS_V3)
struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct nfs_subversion *);
struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);
#endif
/* callback_xdr.c */ /* callback_xdr.c */
extern struct svc_version nfs4_callback_version1; extern struct svc_version nfs4_callback_version1;
extern struct svc_version nfs4_callback_version4; extern struct svc_version nfs4_callback_version4;
......
/*
* Copyright (C) 2014 Anna Schumaker.
*
* NFSv3-specific filesystem definitions and declarations
*/
#ifndef __LINUX_FS_NFS_NFS3_FS_H
#define __LINUX_FS_NFS_NFS3_FS_H
/*
* nfs3acl.c
*/
#ifdef CONFIG_NFS_V3_ACL
extern struct posix_acl *nfs3_get_acl(struct inode *inode, int type);
extern int nfs3_set_acl(struct inode *inode, struct posix_acl *acl, int type);
extern int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
struct posix_acl *dfacl);
extern ssize_t nfs3_listxattr(struct dentry *, char *, size_t);
extern const struct xattr_handler *nfs3_xattr_handlers[];
#else
static inline int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
struct posix_acl *dfacl)
{
return 0;
}
#define nfs3_listxattr NULL
#endif /* CONFIG_NFS_V3_ACL */
/* nfs3client.c */
struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct nfs_subversion *);
struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);
#endif /* __LINUX_FS_NFS_NFS3_FS_H */
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/nfsacl.h> #include <linux/nfsacl.h>
#include "internal.h" #include "internal.h"
#include "nfs3_fs.h"
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
......
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h> #include <linux/nfs_mount.h>
#include "internal.h" #include "internal.h"
#include "nfs3_fs.h"
#ifdef CONFIG_NFS_V3_ACL #ifdef CONFIG_NFS_V3_ACL
static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program };
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "iostat.h" #include "iostat.h"
#include "internal.h" #include "internal.h"
#include "nfs3_fs.h"
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment