Commit df632d3c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

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

   - Remove CONFIG_EXPERIMENTAL dependency from NFSv4.1
     Aside from the issues discussed at the LKS, distros are shipping
     NFSv4.1 with all the trimmings.
   - Fix fdatasync()/fsync() for the corner case of a server reboot.
   - NFSv4 OPEN access fix: finally distinguish correctly between
     open-for-read and open-for-execute permissions in all situations.
   - Ensure that the TCP socket is closed when we're in CLOSE_WAIT
   - More idmapper bugfixes
   - Lots of pNFS bugfixes and cleanups to remove unnecessary state and
     make the code easier to read.
   - In cases where a pNFS read or write fails, allow the client to
     resume trying layoutgets after two minutes of read/write-
     through-mds.
   - More net namespace fixes to the NFSv4 callback code.
   - More net namespace fixes to the NFSv3 locking code.
   - More NFSv4 migration preparatory patches.
     Including patches to detect network trunking in both NFSv4 and
     NFSv4.1
   - pNFS block updates to optimise LAYOUTGET calls."

* tag 'nfs-for-3.7-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (113 commits)
  pnfsblock: cleanup nfs4_blkdev_get
  NFS41: send real read size in layoutget
  NFS41: send real write size in layoutget
  NFS: track direct IO left bytes
  NFSv4.1: Cleanup ugliness in pnfs_layoutgets_blocked()
  NFSv4.1: Ensure that the layout sequence id stays 'close' to the current
  NFSv4.1: Deal with seqid wraparound in the pNFS return-on-close code
  NFSv4 set open access operation call flag in nfs4_init_opendata_res
  NFSv4.1: Remove the dependency on CONFIG_EXPERIMENTAL
  NFSv4 reduce attribute requests for open reclaim
  NFSv4: nfs4_open_done first must check that GETATTR decoded a file type
  NFSv4.1: Deal with wraparound when updating the layout "barrier" seqid
  NFSv4.1: Deal with wraparound issues when updating the layout stateid
  NFSv4.1: Always set the layout stateid if this is the first layoutget
  NFSv4.1: Fix another refcount issue in pnfs_find_alloc_layout
  NFSv4: don't put ACCESS in OPEN compound if O_EXCL
  NFSv4: don't check MAY_WRITE access bit in OPEN
  NFS: Set key construction data for the legacy upcall
  NFSv4.1: don't do two EXCHANGE_IDs on mount
  NFS: nfs41_walk_client_list(): re-lock before iterating
  ...
parents 2474542f af283885
...@@ -12,9 +12,47 @@ and work is in progress on adding support for minor version 1 of the NFSv4 ...@@ -12,9 +12,47 @@ and work is in progress on adding support for minor version 1 of the NFSv4
protocol. protocol.
The purpose of this document is to provide information on some of the The purpose of this document is to provide information on some of the
upcall interfaces that are used in order to provide the NFS client with special features of the NFS client that can be configured by system
some of the information that it requires in order to fully comply with administrators.
the NFS spec.
The nfs4_unique_id parameter
============================
NFSv4 requires clients to identify themselves to servers with a unique
string. File open and lock state shared between one client and one server
is associated with this identity. To support robust NFSv4 state recovery
and transparent state migration, this identity string must not change
across client reboots.
Without any other intervention, the Linux client uses a string that contains
the local system's node name. System administrators, however, often do not
take care to ensure that node names are fully qualified and do not change
over the lifetime of a client system. Node names can have other
administrative requirements that require particular behavior that does not
work well as part of an nfs_client_id4 string.
The nfs.nfs4_unique_id boot parameter specifies a unique string that can be
used instead of a system's node name when an NFS client identifies itself to
a server. Thus, if the system's node name is not unique, or it changes, its
nfs.nfs4_unique_id stays the same, preventing collision with other clients
or loss of state during NFS reboot recovery or transparent state migration.
The nfs.nfs4_unique_id string is typically a UUID, though it can contain
anything that is believed to be unique across all NFS clients. An
nfs4_unique_id string should be chosen when a client system is installed,
just as a system's root file system gets a fresh UUID in its label at
install time.
The string should remain fixed for the lifetime of the client. It can be
changed safely if care is taken that the client shuts down cleanly and all
outstanding NFSv4 state has expired, to prevent loss of NFSv4 state.
This string can be stored in an NFS client's grub.conf, or it can be provided
via a net boot facility such as PXE. It may also be specified as an nfs.ko
module parameter. Specifying a uniquifier string is not support for NFS
clients running in containers.
The DNS resolver The DNS resolver
================ ================
......
...@@ -1730,6 +1730,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -1730,6 +1730,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
will be autodetected by the client, and it will fall will be autodetected by the client, and it will fall
back to using the idmapper. back to using the idmapper.
To turn off this behaviour, set the value to '0'. To turn off this behaviour, set the value to '0'.
nfs.nfs4_unique_id=
[NFS4] Specify an additional fixed unique ident-
ification string that NFSv4 clients can insert into
their nfs_client_id4 string. This is typically a
UUID that is generated at system install time.
nfs.send_implementation_id = nfs.send_implementation_id =
[NFSv4.1] Send client implementation identification [NFSv4.1] Send client implementation identification
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/utsname.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -19,6 +18,8 @@ ...@@ -19,6 +18,8 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "netns.h"
#define NLMDBG_FACILITY NLMDBG_MONITOR #define NLMDBG_FACILITY NLMDBG_MONITOR
#define NSM_PROGRAM 100024 #define NSM_PROGRAM 100024
#define NSM_VERSION 1 #define NSM_VERSION 1
...@@ -40,6 +41,7 @@ struct nsm_args { ...@@ -40,6 +41,7 @@ struct nsm_args {
u32 proc; u32 proc;
char *mon_name; char *mon_name;
char *nodename;
}; };
struct nsm_res { struct nsm_res {
...@@ -70,7 +72,7 @@ static struct rpc_clnt *nsm_create(struct net *net) ...@@ -70,7 +72,7 @@ static struct rpc_clnt *nsm_create(struct net *net)
}; };
struct rpc_create_args args = { struct rpc_create_args args = {
.net = net, .net = net,
.protocol = XPRT_TRANSPORT_UDP, .protocol = XPRT_TRANSPORT_TCP,
.address = (struct sockaddr *)&sin, .address = (struct sockaddr *)&sin,
.addrsize = sizeof(sin), .addrsize = sizeof(sin),
.servername = "rpc.statd", .servername = "rpc.statd",
...@@ -83,10 +85,54 @@ static struct rpc_clnt *nsm_create(struct net *net) ...@@ -83,10 +85,54 @@ static struct rpc_clnt *nsm_create(struct net *net)
return rpc_create(&args); return rpc_create(&args);
} }
static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res, static struct rpc_clnt *nsm_client_get(struct net *net)
struct net *net)
{ {
static DEFINE_MUTEX(nsm_create_mutex);
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct lockd_net *ln = net_generic(net, lockd_net_id);
spin_lock(&ln->nsm_clnt_lock);
if (ln->nsm_users) {
ln->nsm_users++;
clnt = ln->nsm_clnt;
spin_unlock(&ln->nsm_clnt_lock);
goto out;
}
spin_unlock(&ln->nsm_clnt_lock);
mutex_lock(&nsm_create_mutex);
clnt = nsm_create(net);
if (!IS_ERR(clnt)) {
ln->nsm_clnt = clnt;
smp_wmb();
ln->nsm_users = 1;
}
mutex_unlock(&nsm_create_mutex);
out:
return clnt;
}
static void nsm_client_put(struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
struct rpc_clnt *clnt = ln->nsm_clnt;
int shutdown = 0;
spin_lock(&ln->nsm_clnt_lock);
if (ln->nsm_users) {
if (--ln->nsm_users)
ln->nsm_clnt = NULL;
shutdown = !ln->nsm_users;
}
spin_unlock(&ln->nsm_clnt_lock);
if (shutdown)
rpc_shutdown_client(clnt);
}
static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
struct rpc_clnt *clnt)
{
int status; int status;
struct nsm_args args = { struct nsm_args args = {
.priv = &nsm->sm_priv, .priv = &nsm->sm_priv,
...@@ -94,31 +140,24 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res, ...@@ -94,31 +140,24 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
.vers = 3, .vers = 3,
.proc = NLMPROC_NSM_NOTIFY, .proc = NLMPROC_NSM_NOTIFY,
.mon_name = nsm->sm_mon_name, .mon_name = nsm->sm_mon_name,
.nodename = clnt->cl_nodename,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = res, .rpc_resp = res,
}; };
clnt = nsm_create(net); BUG_ON(clnt == NULL);
if (IS_ERR(clnt)) {
status = PTR_ERR(clnt);
dprintk("lockd: failed to create NSM upcall transport, "
"status=%d\n", status);
goto out;
}
memset(res, 0, sizeof(*res)); memset(res, 0, sizeof(*res));
msg.rpc_proc = &clnt->cl_procinfo[proc]; msg.rpc_proc = &clnt->cl_procinfo[proc];
status = rpc_call_sync(clnt, &msg, 0); 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);
else else
status = 0; status = 0;
rpc_shutdown_client(clnt);
out:
return status; return status;
} }
...@@ -138,6 +177,7 @@ int nsm_monitor(const struct nlm_host *host) ...@@ -138,6 +177,7 @@ int nsm_monitor(const struct nlm_host *host)
struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res; struct nsm_res res;
int status; int status;
struct rpc_clnt *clnt;
dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name); dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
...@@ -150,7 +190,15 @@ int nsm_monitor(const struct nlm_host *host) ...@@ -150,7 +190,15 @@ int nsm_monitor(const struct nlm_host *host)
*/ */
nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, host->net); clnt = nsm_client_get(host->net);
if (IS_ERR(clnt)) {
status = PTR_ERR(clnt);
dprintk("lockd: failed to create NSM upcall transport, "
"status=%d, net=%p\n", status, host->net);
return status;
}
status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, clnt);
if (unlikely(res.status != 0)) if (unlikely(res.status != 0))
status = -EIO; status = -EIO;
if (unlikely(status < 0)) { if (unlikely(status < 0)) {
...@@ -182,9 +230,11 @@ void nsm_unmonitor(const struct nlm_host *host) ...@@ -182,9 +230,11 @@ void nsm_unmonitor(const struct nlm_host *host)
if (atomic_read(&nsm->sm_count) == 1 if (atomic_read(&nsm->sm_count) == 1
&& nsm->sm_monitored && !nsm->sm_sticky) { && nsm->sm_monitored && !nsm->sm_sticky) {
struct lockd_net *ln = net_generic(host->net, lockd_net_id);
dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res, host->net); status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res, ln->nsm_clnt);
if (res.status != 0) if (res.status != 0)
status = -EIO; status = -EIO;
if (status < 0) if (status < 0)
...@@ -192,6 +242,8 @@ void nsm_unmonitor(const struct nlm_host *host) ...@@ -192,6 +242,8 @@ void nsm_unmonitor(const struct nlm_host *host)
nsm->sm_name); nsm->sm_name);
else else
nsm->sm_monitored = 0; nsm->sm_monitored = 0;
nsm_client_put(host->net);
} }
} }
...@@ -430,7 +482,7 @@ static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp) ...@@ -430,7 +482,7 @@ static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{ {
__be32 *p; __be32 *p;
encode_nsm_string(xdr, utsname()->nodename); encode_nsm_string(xdr, argp->nodename);
p = xdr_reserve_space(xdr, 4 + 4 + 4); p = xdr_reserve_space(xdr, 4 + 4 + 4);
*p++ = cpu_to_be32(argp->prog); *p++ = cpu_to_be32(argp->prog);
*p++ = cpu_to_be32(argp->vers); *p++ = cpu_to_be32(argp->vers);
......
...@@ -12,6 +12,10 @@ struct lockd_net { ...@@ -12,6 +12,10 @@ struct lockd_net {
struct delayed_work grace_period_end; struct delayed_work grace_period_end;
struct lock_manager lockd_manager; struct lock_manager lockd_manager;
struct list_head grace_list; struct list_head grace_list;
spinlock_t nsm_clnt_lock;
unsigned int nsm_users;
struct rpc_clnt *nsm_clnt;
}; };
extern int lockd_net_id; extern int lockd_net_id;
......
...@@ -596,6 +596,7 @@ static int lockd_init_net(struct net *net) ...@@ -596,6 +596,7 @@ static int lockd_init_net(struct net *net)
INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender); INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
INIT_LIST_HEAD(&ln->grace_list); INIT_LIST_HEAD(&ln->grace_list);
spin_lock_init(&ln->nsm_clnt_lock);
return 0; return 0;
} }
......
...@@ -95,8 +95,8 @@ config NFS_SWAP ...@@ -95,8 +95,8 @@ config NFS_SWAP
This option enables swapon to work on files located on NFS mounts. This option enables swapon to work on files located on NFS mounts.
config NFS_V4_1 config NFS_V4_1
bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" bool "NFS client support for NFSv4.1"
depends on NFS_V4 && EXPERIMENTAL depends on NFS_V4
select SUNRPC_BACKCHANNEL select SUNRPC_BACKCHANNEL
help help
This option enables support for minor version 1 of the NFSv4 protocol This option enables support for minor version 1 of the NFSv4 protocol
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/bio.h> /* struct bio */ #include <linux/bio.h> /* struct bio */
#include <linux/buffer_head.h> /* various write calls */ #include <linux/buffer_head.h> /* various write calls */
#include <linux/prefetch.h> #include <linux/prefetch.h>
#include <linux/pagevec.h>
#include "../pnfs.h" #include "../pnfs.h"
#include "../internal.h" #include "../internal.h"
...@@ -162,25 +163,39 @@ static struct bio *bl_alloc_init_bio(int npg, sector_t isect, ...@@ -162,25 +163,39 @@ static struct bio *bl_alloc_init_bio(int npg, sector_t isect,
return bio; return bio;
} }
static struct bio *bl_add_page_to_bio(struct bio *bio, int npg, int rw, static struct bio *do_add_page_to_bio(struct bio *bio, int npg, int rw,
sector_t isect, struct page *page, sector_t isect, struct page *page,
struct pnfs_block_extent *be, struct pnfs_block_extent *be,
void (*end_io)(struct bio *, int err), void (*end_io)(struct bio *, int err),
struct parallel_io *par) struct parallel_io *par,
unsigned int offset, int len)
{ {
isect = isect + (offset >> SECTOR_SHIFT);
dprintk("%s: npg %d rw %d isect %llu offset %u len %d\n", __func__,
npg, rw, (unsigned long long)isect, offset, len);
retry: retry:
if (!bio) { if (!bio) {
bio = bl_alloc_init_bio(npg, isect, be, end_io, par); bio = bl_alloc_init_bio(npg, isect, be, end_io, par);
if (!bio) if (!bio)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { if (bio_add_page(bio, page, len, offset) < len) {
bio = bl_submit_bio(rw, bio); bio = bl_submit_bio(rw, bio);
goto retry; goto retry;
} }
return bio; return bio;
} }
static struct bio *bl_add_page_to_bio(struct bio *bio, int npg, int rw,
sector_t isect, struct page *page,
struct pnfs_block_extent *be,
void (*end_io)(struct bio *, int err),
struct parallel_io *par)
{
return do_add_page_to_bio(bio, npg, rw, isect, page, be,
end_io, par, 0, PAGE_CACHE_SIZE);
}
/* This is basically copied from mpage_end_io_read */ /* This is basically copied from mpage_end_io_read */
static void bl_end_io_read(struct bio *bio, int err) static void bl_end_io_read(struct bio *bio, int err)
{ {
...@@ -228,14 +243,6 @@ bl_end_par_io_read(void *data, int unused) ...@@ -228,14 +243,6 @@ bl_end_par_io_read(void *data, int unused)
schedule_work(&rdata->task.u.tk_work); schedule_work(&rdata->task.u.tk_work);
} }
static bool
bl_check_alignment(u64 offset, u32 len, unsigned long blkmask)
{
if ((offset & blkmask) || (len & blkmask))
return false;
return true;
}
static enum pnfs_try_status static enum pnfs_try_status
bl_read_pagelist(struct nfs_read_data *rdata) bl_read_pagelist(struct nfs_read_data *rdata)
{ {
...@@ -246,15 +253,15 @@ bl_read_pagelist(struct nfs_read_data *rdata) ...@@ -246,15 +253,15 @@ bl_read_pagelist(struct nfs_read_data *rdata)
sector_t isect, extent_length = 0; sector_t isect, extent_length = 0;
struct parallel_io *par; struct parallel_io *par;
loff_t f_offset = rdata->args.offset; loff_t f_offset = rdata->args.offset;
size_t bytes_left = rdata->args.count;
unsigned int pg_offset, pg_len;
struct page **pages = rdata->args.pages; struct page **pages = rdata->args.pages;
int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT; int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT;
const bool is_dio = (header->dreq != NULL);
dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__,
rdata->pages.npages, f_offset, (unsigned int)rdata->args.count); rdata->pages.npages, f_offset, (unsigned int)rdata->args.count);
if (!bl_check_alignment(f_offset, rdata->args.count, PAGE_CACHE_MASK))
goto use_mds;
par = alloc_parallel(rdata); par = alloc_parallel(rdata);
if (!par) if (!par)
goto use_mds; goto use_mds;
...@@ -284,36 +291,53 @@ bl_read_pagelist(struct nfs_read_data *rdata) ...@@ -284,36 +291,53 @@ bl_read_pagelist(struct nfs_read_data *rdata)
extent_length = min(extent_length, cow_length); extent_length = min(extent_length, cow_length);
} }
} }
if (is_dio) {
pg_offset = f_offset & ~PAGE_CACHE_MASK;
if (pg_offset + bytes_left > PAGE_CACHE_SIZE)
pg_len = PAGE_CACHE_SIZE - pg_offset;
else
pg_len = bytes_left;
f_offset += pg_len;
bytes_left -= pg_len;
isect += (pg_offset >> SECTOR_SHIFT);
} else {
pg_offset = 0;
pg_len = PAGE_CACHE_SIZE;
}
hole = is_hole(be, isect); hole = is_hole(be, isect);
if (hole && !cow_read) { if (hole && !cow_read) {
bio = bl_submit_bio(READ, bio); bio = bl_submit_bio(READ, bio);
/* Fill hole w/ zeroes w/o accessing device */ /* Fill hole w/ zeroes w/o accessing device */
dprintk("%s Zeroing page for hole\n", __func__); dprintk("%s Zeroing page for hole\n", __func__);
zero_user_segment(pages[i], 0, PAGE_CACHE_SIZE); zero_user_segment(pages[i], pg_offset, pg_len);
print_page(pages[i]); print_page(pages[i]);
SetPageUptodate(pages[i]); SetPageUptodate(pages[i]);
} else { } else {
struct pnfs_block_extent *be_read; struct pnfs_block_extent *be_read;
be_read = (hole && cow_read) ? cow_read : be; be_read = (hole && cow_read) ? cow_read : be;
bio = bl_add_page_to_bio(bio, rdata->pages.npages - i, bio = do_add_page_to_bio(bio, rdata->pages.npages - i,
READ, READ,
isect, pages[i], be_read, isect, pages[i], be_read,
bl_end_io_read, par); bl_end_io_read, par,
pg_offset, pg_len);
if (IS_ERR(bio)) { if (IS_ERR(bio)) {
header->pnfs_error = PTR_ERR(bio); header->pnfs_error = PTR_ERR(bio);
bio = NULL; bio = NULL;
goto out; goto out;
} }
} }
isect += PAGE_CACHE_SECTORS; isect += (pg_len >> SECTOR_SHIFT);
extent_length -= PAGE_CACHE_SECTORS; extent_length -= PAGE_CACHE_SECTORS;
} }
if ((isect << SECTOR_SHIFT) >= header->inode->i_size) { if ((isect << SECTOR_SHIFT) >= header->inode->i_size) {
rdata->res.eof = 1; rdata->res.eof = 1;
rdata->res.count = header->inode->i_size - f_offset; rdata->res.count = header->inode->i_size - rdata->args.offset;
} else { } else {
rdata->res.count = (isect << SECTOR_SHIFT) - f_offset; rdata->res.count = (isect << SECTOR_SHIFT) - rdata->args.offset;
} }
out: out:
bl_put_extent(be); bl_put_extent(be);
...@@ -461,6 +485,106 @@ map_block(struct buffer_head *bh, sector_t isect, struct pnfs_block_extent *be) ...@@ -461,6 +485,106 @@ map_block(struct buffer_head *bh, sector_t isect, struct pnfs_block_extent *be)
return; return;
} }
static void
bl_read_single_end_io(struct bio *bio, int error)
{
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
struct page *page = bvec->bv_page;
/* Only one page in bvec */
unlock_page(page);
}
static int
bl_do_readpage_sync(struct page *page, struct pnfs_block_extent *be,
unsigned int offset, unsigned int len)
{
struct bio *bio;
struct page *shadow_page;
sector_t isect;
char *kaddr, *kshadow_addr;
int ret = 0;
dprintk("%s: offset %u len %u\n", __func__, offset, len);
shadow_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
if (shadow_page == NULL)
return -ENOMEM;
bio = bio_alloc(GFP_NOIO, 1);
if (bio == NULL)
return -ENOMEM;
isect = (page->index << PAGE_CACHE_SECTOR_SHIFT) +
(offset / SECTOR_SIZE);
bio->bi_sector = isect - be->be_f_offset + be->be_v_offset;
bio->bi_bdev = be->be_mdev;
bio->bi_end_io = bl_read_single_end_io;
lock_page(shadow_page);
if (bio_add_page(bio, shadow_page,
SECTOR_SIZE, round_down(offset, SECTOR_SIZE)) == 0) {
unlock_page(shadow_page);
bio_put(bio);
return -EIO;
}
submit_bio(READ, bio);
wait_on_page_locked(shadow_page);
if (unlikely(!test_bit(BIO_UPTODATE, &bio->bi_flags))) {
ret = -EIO;
} else {
kaddr = kmap_atomic(page);
kshadow_addr = kmap_atomic(shadow_page);
memcpy(kaddr + offset, kshadow_addr + offset, len);
kunmap_atomic(kshadow_addr);
kunmap_atomic(kaddr);
}
__free_page(shadow_page);
bio_put(bio);
return ret;
}
static int
bl_read_partial_page_sync(struct page *page, struct pnfs_block_extent *be,
unsigned int dirty_offset, unsigned int dirty_len,
bool full_page)
{
int ret = 0;
unsigned int start, end;
if (full_page) {
start = 0;
end = PAGE_CACHE_SIZE;
} else {
start = round_down(dirty_offset, SECTOR_SIZE);
end = round_up(dirty_offset + dirty_len, SECTOR_SIZE);
}
dprintk("%s: offset %u len %d\n", __func__, dirty_offset, dirty_len);
if (!be) {
zero_user_segments(page, start, dirty_offset,
dirty_offset + dirty_len, end);
if (start == 0 && end == PAGE_CACHE_SIZE &&
trylock_page(page)) {
SetPageUptodate(page);
unlock_page(page);
}
return ret;
}
if (start != dirty_offset)
ret = bl_do_readpage_sync(page, be, start, dirty_offset - start);
if (!ret && (dirty_offset + dirty_len < end))
ret = bl_do_readpage_sync(page, be, dirty_offset + dirty_len,
end - dirty_offset - dirty_len);
return ret;
}
/* Given an unmapped page, zero it or read in page for COW, page is locked /* Given an unmapped page, zero it or read in page for COW, page is locked
* by caller. * by caller.
*/ */
...@@ -494,7 +618,6 @@ init_page_for_write(struct page *page, struct pnfs_block_extent *cow_read) ...@@ -494,7 +618,6 @@ init_page_for_write(struct page *page, struct pnfs_block_extent *cow_read)
SetPageUptodate(page); SetPageUptodate(page);
cleanup: cleanup:
bl_put_extent(cow_read);
if (bh) if (bh)
free_buffer_head(bh); free_buffer_head(bh);
if (ret) { if (ret) {
...@@ -566,6 +689,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) ...@@ -566,6 +689,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
struct parallel_io *par = NULL; struct parallel_io *par = NULL;
loff_t offset = wdata->args.offset; loff_t offset = wdata->args.offset;
size_t count = wdata->args.count; size_t count = wdata->args.count;
unsigned int pg_offset, pg_len, saved_len;
struct page **pages = wdata->args.pages; struct page **pages = wdata->args.pages;
struct page *page; struct page *page;
pgoff_t index; pgoff_t index;
...@@ -574,10 +698,13 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) ...@@ -574,10 +698,13 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT; NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT;
dprintk("%s enter, %Zu@%lld\n", __func__, count, offset); dprintk("%s enter, %Zu@%lld\n", __func__, count, offset);
/* Check for alignment first */
if (!bl_check_alignment(offset, count, PAGE_CACHE_MASK))
goto out_mds;
if (header->dreq != NULL &&
(!IS_ALIGNED(offset, NFS_SERVER(header->inode)->pnfs_blksize) ||
!IS_ALIGNED(count, NFS_SERVER(header->inode)->pnfs_blksize))) {
dprintk("pnfsblock nonblock aligned DIO writes. Resend MDS\n");
goto out_mds;
}
/* At this point, wdata->pages is a (sequential) list of nfs_pages. /* At this point, wdata->pages is a (sequential) list of nfs_pages.
* We want to write each, and if there is an error set pnfs_error * We want to write each, and if there is an error set pnfs_error
* to have it redone using nfs. * to have it redone using nfs.
...@@ -674,10 +801,11 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) ...@@ -674,10 +801,11 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
if (!extent_length) { if (!extent_length) {
/* We've used up the previous extent */ /* We've used up the previous extent */
bl_put_extent(be); bl_put_extent(be);
bl_put_extent(cow_read);
bio = bl_submit_bio(WRITE, bio); bio = bl_submit_bio(WRITE, bio);
/* Get the next one */ /* Get the next one */
be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg), be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg),
isect, NULL); isect, &cow_read);
if (!be || !is_writable(be, isect)) { if (!be || !is_writable(be, isect)) {
header->pnfs_error = -EINVAL; header->pnfs_error = -EINVAL;
goto out; goto out;
...@@ -694,7 +822,26 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) ...@@ -694,7 +822,26 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
extent_length = be->be_length - extent_length = be->be_length -
(isect - be->be_f_offset); (isect - be->be_f_offset);
} }
if (be->be_state == PNFS_BLOCK_INVALID_DATA) {
dprintk("%s offset %lld count %Zu\n", __func__, offset, count);
pg_offset = offset & ~PAGE_CACHE_MASK;
if (pg_offset + count > PAGE_CACHE_SIZE)
pg_len = PAGE_CACHE_SIZE - pg_offset;
else
pg_len = count;
saved_len = pg_len;
if (be->be_state == PNFS_BLOCK_INVALID_DATA &&
!bl_is_sector_init(be->be_inval, isect)) {
ret = bl_read_partial_page_sync(pages[i], cow_read,
pg_offset, pg_len, true);
if (ret) {
dprintk("%s bl_read_partial_page_sync fail %d\n",
__func__, ret);
header->pnfs_error = ret;
goto out;
}
ret = bl_mark_sectors_init(be->be_inval, isect, ret = bl_mark_sectors_init(be->be_inval, isect,
PAGE_CACHE_SECTORS); PAGE_CACHE_SECTORS);
if (unlikely(ret)) { if (unlikely(ret)) {
...@@ -703,15 +850,35 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) ...@@ -703,15 +850,35 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
header->pnfs_error = ret; header->pnfs_error = ret;
goto out; goto out;
} }
/* Expand to full page write */
pg_offset = 0;
pg_len = PAGE_CACHE_SIZE;
} else if ((pg_offset & (SECTOR_SIZE - 1)) ||
(pg_len & (SECTOR_SIZE - 1))){
/* ahh, nasty case. We have to do sync full sector
* read-modify-write cycles.
*/
unsigned int saved_offset = pg_offset;
ret = bl_read_partial_page_sync(pages[i], be, pg_offset,
pg_len, false);
pg_offset = round_down(pg_offset, SECTOR_SIZE);
pg_len = round_up(saved_offset + pg_len, SECTOR_SIZE)
- pg_offset;
} }
bio = bl_add_page_to_bio(bio, wdata->pages.npages - i, WRITE,
bio = do_add_page_to_bio(bio, wdata->pages.npages - i, WRITE,
isect, pages[i], be, isect, pages[i], be,
bl_end_io_write, par); bl_end_io_write, par,
pg_offset, pg_len);
if (IS_ERR(bio)) { if (IS_ERR(bio)) {
header->pnfs_error = PTR_ERR(bio); header->pnfs_error = PTR_ERR(bio);
bio = NULL; bio = NULL;
goto out; goto out;
} }
offset += saved_len;
count -= saved_len;
isect += PAGE_CACHE_SECTORS; isect += PAGE_CACHE_SECTORS;
last_isect = isect; last_isect = isect;
extent_length -= PAGE_CACHE_SECTORS; extent_length -= PAGE_CACHE_SECTORS;
...@@ -729,17 +896,16 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) ...@@ -729,17 +896,16 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
} }
write_done: write_done:
wdata->res.count = (last_isect << SECTOR_SHIFT) - (offset); wdata->res.count = wdata->args.count;
if (count < wdata->res.count) {
wdata->res.count = count;
}
out: out:
bl_put_extent(be); bl_put_extent(be);
bl_put_extent(cow_read);
bl_submit_bio(WRITE, bio); bl_submit_bio(WRITE, bio);
put_parallel(par); put_parallel(par);
return PNFS_ATTEMPTED; return PNFS_ATTEMPTED;
out_mds: out_mds:
bl_put_extent(be); bl_put_extent(be);
bl_put_extent(cow_read);
kfree(par); kfree(par);
return PNFS_NOT_ATTEMPTED; return PNFS_NOT_ATTEMPTED;
} }
...@@ -874,7 +1040,7 @@ static void free_blk_mountid(struct block_mount_id *mid) ...@@ -874,7 +1040,7 @@ static void free_blk_mountid(struct block_mount_id *mid)
} }
} }
/* This is mostly copied from the filelayout's get_device_info function. /* This is mostly copied from the filelayout_get_device_info function.
* It seems much of this should be at the generic pnfs level. * It seems much of this should be at the generic pnfs level.
*/ */
static struct pnfs_block_dev * static struct pnfs_block_dev *
...@@ -1011,33 +1177,95 @@ bl_clear_layoutdriver(struct nfs_server *server) ...@@ -1011,33 +1177,95 @@ bl_clear_layoutdriver(struct nfs_server *server)
return 0; return 0;
} }
static bool
is_aligned_req(struct nfs_page *req, unsigned int alignment)
{
return IS_ALIGNED(req->wb_offset, alignment) &&
IS_ALIGNED(req->wb_bytes, alignment);
}
static void static void
bl_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) bl_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
{ {
if (!bl_check_alignment(req->wb_offset, req->wb_bytes, PAGE_CACHE_MASK)) if (pgio->pg_dreq != NULL &&
!is_aligned_req(req, SECTOR_SIZE))
nfs_pageio_reset_read_mds(pgio); nfs_pageio_reset_read_mds(pgio);
else else
pnfs_generic_pg_init_read(pgio, req); pnfs_generic_pg_init_read(pgio, req);
} }
static bool
bl_pg_test_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
struct nfs_page *req)
{
if (pgio->pg_dreq != NULL &&
!is_aligned_req(req, SECTOR_SIZE))
return false;
return pnfs_generic_pg_test(pgio, prev, req);
}
/*
* Return the number of contiguous bytes for a given inode
* starting at page frame idx.
*/
static u64 pnfs_num_cont_bytes(struct inode *inode, pgoff_t idx)
{
struct address_space *mapping = inode->i_mapping;
pgoff_t end;
/* Optimize common case that writes from 0 to end of file */
end = DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE);
if (end != NFS_I(inode)->npages) {
rcu_read_lock();
end = radix_tree_next_hole(&mapping->page_tree, idx + 1, ULONG_MAX);
rcu_read_unlock();
}
if (!end)
return i_size_read(inode) - (idx << PAGE_CACHE_SHIFT);
else
return (end - idx) << PAGE_CACHE_SHIFT;
}
static void static void
bl_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) bl_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
{ {
if (!bl_check_alignment(req->wb_offset, req->wb_bytes, PAGE_CACHE_MASK)) if (pgio->pg_dreq != NULL &&
!is_aligned_req(req, PAGE_CACHE_SIZE)) {
nfs_pageio_reset_write_mds(pgio); nfs_pageio_reset_write_mds(pgio);
else } else {
pnfs_generic_pg_init_write(pgio, req); u64 wb_size;
if (pgio->pg_dreq == NULL)
wb_size = pnfs_num_cont_bytes(pgio->pg_inode,
req->wb_index);
else
wb_size = nfs_dreq_bytes_left(pgio->pg_dreq);
pnfs_generic_pg_init_write(pgio, req, wb_size);
}
}
static bool
bl_pg_test_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
struct nfs_page *req)
{
if (pgio->pg_dreq != NULL &&
!is_aligned_req(req, PAGE_CACHE_SIZE))
return false;
return pnfs_generic_pg_test(pgio, prev, req);
} }
static const struct nfs_pageio_ops bl_pg_read_ops = { static const struct nfs_pageio_ops bl_pg_read_ops = {
.pg_init = bl_pg_init_read, .pg_init = bl_pg_init_read,
.pg_test = pnfs_generic_pg_test, .pg_test = bl_pg_test_read,
.pg_doio = pnfs_generic_pg_readpages, .pg_doio = pnfs_generic_pg_readpages,
}; };
static const struct nfs_pageio_ops bl_pg_write_ops = { static const struct nfs_pageio_ops bl_pg_write_ops = {
.pg_init = bl_pg_init_write, .pg_init = bl_pg_init_write,
.pg_test = pnfs_generic_pg_test, .pg_test = bl_pg_test_write,
.pg_doio = pnfs_generic_pg_writepages, .pg_doio = pnfs_generic_pg_writepages,
}; };
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#define PAGE_CACHE_SECTORS (PAGE_CACHE_SIZE >> SECTOR_SHIFT) #define PAGE_CACHE_SECTORS (PAGE_CACHE_SIZE >> SECTOR_SHIFT)
#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)
struct block_mount_id { struct block_mount_id {
spinlock_t bm_lock; /* protects list */ spinlock_t bm_lock; /* protects list */
...@@ -172,7 +173,6 @@ struct bl_msg_hdr { ...@@ -172,7 +173,6 @@ struct bl_msg_hdr {
/* blocklayoutdev.c */ /* blocklayoutdev.c */
ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t); ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t);
void bl_pipe_destroy_msg(struct rpc_pipe_msg *); void bl_pipe_destroy_msg(struct rpc_pipe_msg *);
struct block_device *nfs4_blkdev_get(dev_t dev);
int nfs4_blkdev_put(struct block_device *bdev); int nfs4_blkdev_put(struct block_device *bdev);
struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server, struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server,
struct pnfs_device *dev); struct pnfs_device *dev);
......
...@@ -53,22 +53,6 @@ static int decode_sector_number(__be32 **rp, sector_t *sp) ...@@ -53,22 +53,6 @@ static int decode_sector_number(__be32 **rp, sector_t *sp)
return 0; return 0;
} }
/* Open a block_device by device number. */
struct block_device *nfs4_blkdev_get(dev_t dev)
{
struct block_device *bd;
dprintk("%s enter\n", __func__);
bd = blkdev_get_by_dev(dev, FMODE_READ, NULL);
if (IS_ERR(bd))
goto fail;
return bd;
fail:
dprintk("%s failed to open device : %ld\n",
__func__, PTR_ERR(bd));
return NULL;
}
/* /*
* Release the block device * Release the block device
*/ */
...@@ -172,11 +156,12 @@ nfs4_blk_decode_device(struct nfs_server *server, ...@@ -172,11 +156,12 @@ nfs4_blk_decode_device(struct nfs_server *server,
goto out; goto out;
} }
bd = nfs4_blkdev_get(MKDEV(reply->major, reply->minor)); bd = blkdev_get_by_dev(MKDEV(reply->major, reply->minor),
FMODE_READ, NULL);
if (IS_ERR(bd)) { if (IS_ERR(bd)) {
rc = PTR_ERR(bd); dprintk("%s failed to open device : %ld\n", __func__,
dprintk("%s failed to open device : %d\n", __func__, rc); PTR_ERR(bd));
rv = ERR_PTR(rc); rv = ERR_CAST(bd);
goto out; goto out;
} }
......
...@@ -683,8 +683,7 @@ encode_pnfs_block_layoutupdate(struct pnfs_block_layout *bl, ...@@ -683,8 +683,7 @@ encode_pnfs_block_layoutupdate(struct pnfs_block_layout *bl,
p = xdr_encode_hyper(p, lce->bse_length << SECTOR_SHIFT); p = xdr_encode_hyper(p, lce->bse_length << SECTOR_SHIFT);
p = xdr_encode_hyper(p, 0LL); p = xdr_encode_hyper(p, 0LL);
*p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA); *p++ = cpu_to_be32(PNFS_BLOCK_READWRITE_DATA);
list_del(&lce->bse_node); list_move_tail(&lce->bse_node, &bl->bl_committing);
list_add_tail(&lce->bse_node, &bl->bl_committing);
bl->bl_count--; bl->bl_count--;
count++; count++;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/errno.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/kthread.h> #include <linux/kthread.h>
...@@ -23,6 +24,7 @@ ...@@ -23,6 +24,7 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "callback.h" #include "callback.h"
#include "internal.h" #include "internal.h"
#include "netns.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK #define NFSDBG_FACILITY NFSDBG_CALLBACK
...@@ -37,7 +39,32 @@ static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; ...@@ -37,7 +39,32 @@ static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
static DEFINE_MUTEX(nfs_callback_mutex); static DEFINE_MUTEX(nfs_callback_mutex);
static struct svc_program nfs4_callback_program; static struct svc_program nfs4_callback_program;
unsigned short nfs_callback_tcpport6; static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
{
int ret;
struct nfs_net *nn = net_generic(net, nfs_net_id);
ret = svc_create_xprt(serv, "tcp", net, PF_INET,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
if (ret <= 0)
goto out_err;
nn->nfs_callback_tcpport = ret;
dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
nn->nfs_callback_tcpport, PF_INET, net);
ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
if (ret > 0) {
nn->nfs_callback_tcpport6 = ret;
dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
nn->nfs_callback_tcpport6, PF_INET6, net);
} else if (ret != -EAFNOSUPPORT)
goto out_err;
return 0;
out_err:
return (ret) ? ret : -ENOMEM;
}
/* /*
* This is the NFSv4 callback kernel thread. * This is the NFSv4 callback kernel thread.
...@@ -78,38 +105,23 @@ nfs4_callback_svc(void *vrqstp) ...@@ -78,38 +105,23 @@ nfs4_callback_svc(void *vrqstp)
* Prepare to bring up the NFSv4 callback service * Prepare to bring up the NFSv4 callback service
*/ */
static struct svc_rqst * static struct svc_rqst *
nfs4_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) nfs4_callback_up(struct svc_serv *serv)
{ {
int ret;
ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
if (ret <= 0)
goto out_err;
nfs_callback_tcpport = ret;
dprintk("NFS: Callback listener port = %u (af %u)\n",
nfs_callback_tcpport, PF_INET);
ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
if (ret > 0) {
nfs_callback_tcpport6 = ret;
dprintk("NFS: Callback listener port = %u (af %u)\n",
nfs_callback_tcpport6, PF_INET6);
} else if (ret == -EAFNOSUPPORT)
ret = 0;
else
goto out_err;
return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
out_err:
if (ret == 0)
ret = -ENOMEM;
return ERR_PTR(ret);
} }
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
{
/*
* Create an svc_sock for the back channel service that shares the
* fore channel connection.
* Returns the input port (0) and sets the svc_serv bc_xprt on success
*/
return svc_create_xprt(serv, "tcp-bc", net, PF_INET, 0,
SVC_SOCK_ANONYMOUS);
}
/* /*
* The callback service for NFSv4.1 callbacks * The callback service for NFSv4.1 callbacks
*/ */
...@@ -149,28 +161,9 @@ nfs41_callback_svc(void *vrqstp) ...@@ -149,28 +161,9 @@ nfs41_callback_svc(void *vrqstp)
* Bring up the NFSv4.1 callback service * Bring up the NFSv4.1 callback service
*/ */
static struct svc_rqst * static struct svc_rqst *
nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) nfs41_callback_up(struct svc_serv *serv)
{ {
struct svc_rqst *rqstp; struct svc_rqst *rqstp;
int ret;
/*
* Create an svc_sock for the back channel service that shares the
* fore channel connection.
* Returns the input port (0) and sets the svc_serv bc_xprt on success
*/
ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
SVC_SOCK_ANONYMOUS);
if (ret < 0) {
rqstp = ERR_PTR(ret);
goto out;
}
/*
* Save the svc_serv in the transport so that it can
* be referenced when the session backchannel is initialized
*/
xprt->bc_serv = serv;
INIT_LIST_HEAD(&serv->sv_cb_list); INIT_LIST_HEAD(&serv->sv_cb_list);
spin_lock_init(&serv->sv_cb_lock); spin_lock_init(&serv->sv_cb_lock);
...@@ -180,90 +173,74 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) ...@@ -180,90 +173,74 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
svc_xprt_put(serv->sv_bc_xprt); svc_xprt_put(serv->sv_bc_xprt);
serv->sv_bc_xprt = NULL; serv->sv_bc_xprt = NULL;
} }
out:
dprintk("--> %s return %ld\n", __func__, dprintk("--> %s return %ld\n", __func__,
IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0); IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
return rqstp; return rqstp;
} }
static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
struct svc_serv *serv, struct rpc_xprt *xprt,
struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{ {
if (minorversion) { *rqstpp = nfs41_callback_up(serv);
*rqstpp = nfs41_callback_up(serv, xprt); *callback_svc = nfs41_callback_svc;
*callback_svc = nfs41_callback_svc;
}
return minorversion;
} }
static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
struct nfs_callback_data *cb_info) struct svc_serv *serv)
{ {
if (minorversion) if (minorversion)
xprt->bc_serv = cb_info->serv; /*
* Save the svc_serv in the transport so that it can
* be referenced when the session backchannel is initialized
*/
xprt->bc_serv = serv;
} }
#else #else
static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
struct svc_serv *serv, struct rpc_xprt *xprt,
struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{ {
return 0; return 0;
} }
static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{
*rqstpp = ERR_PTR(-ENOTSUPP);
*callback_svc = ERR_PTR(-ENOTSUPP);
}
static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
struct nfs_callback_data *cb_info) struct svc_serv *serv)
{ {
} }
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
/* static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
* Bring up the callback thread if it is not already up. struct svc_serv *serv)
*/
int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
{ {
struct svc_serv *serv = NULL;
struct svc_rqst *rqstp; struct svc_rqst *rqstp;
int (*callback_svc)(void *vrqstp); int (*callback_svc)(void *vrqstp);
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
char svc_name[12]; char svc_name[12];
int ret = 0; int ret;
int minorversion_setup;
struct net *net = &init_net;
mutex_lock(&nfs_callback_mutex); nfs_callback_bc_serv(minorversion, xprt, serv);
if (cb_info->users++ || cb_info->task != NULL) {
nfs_callback_bc_serv(minorversion, xprt, cb_info);
goto out;
}
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
if (!serv) {
ret = -ENOMEM;
goto out_err;
}
/* As there is only one thread we need to over-ride the
* default maximum of 80 connections
*/
serv->sv_maxconn = 1024;
ret = svc_bind(serv, net); if (cb_info->task)
if (ret < 0) { return 0;
printk(KERN_WARNING "NFS: bind callback service failed\n");
goto out_err;
}
minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, switch (minorversion) {
serv, xprt, &rqstp, &callback_svc); case 0:
if (!minorversion_setup) {
/* v4.0 callback setup */ /* v4.0 callback setup */
rqstp = nfs4_callback_up(serv, xprt); rqstp = nfs4_callback_up(serv);
callback_svc = nfs4_callback_svc; callback_svc = nfs4_callback_svc;
break;
default:
nfs_minorversion_callback_svc_setup(serv,
&rqstp, &callback_svc);
} }
if (IS_ERR(rqstp)) { if (IS_ERR(rqstp))
ret = PTR_ERR(rqstp); return PTR_ERR(rqstp);
goto out_err;
}
svc_sock_update_bufs(serv); svc_sock_update_bufs(serv);
...@@ -276,41 +253,165 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) ...@@ -276,41 +253,165 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
svc_exit_thread(cb_info->rqst); svc_exit_thread(cb_info->rqst);
cb_info->rqst = NULL; cb_info->rqst = NULL;
cb_info->task = NULL; cb_info->task = NULL;
goto out_err; return PTR_ERR(cb_info->task);
}
dprintk("nfs_callback_up: service started\n");
return 0;
}
static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
if (--nn->cb_users[minorversion])
return;
dprintk("NFS: destroy per-net callback data; net=%p\n", net);
svc_shutdown_net(serv, net);
}
static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
int ret;
if (nn->cb_users[minorversion]++)
return 0;
dprintk("NFS: create per-net callback data; net=%p\n", net);
ret = svc_bind(serv, net);
if (ret < 0) {
printk(KERN_WARNING "NFS: bind callback service failed\n");
goto err_bind;
}
switch (minorversion) {
case 0:
ret = nfs4_callback_up_net(serv, net);
break;
case 1:
ret = nfs41_callback_up_net(serv, net);
break;
default:
printk(KERN_ERR "NFS: unknown callback version: %d\n",
minorversion);
ret = -EINVAL;
break;
} }
out:
if (ret < 0) {
printk(KERN_ERR "NFS: callback service start failed\n");
goto err_socks;
}
return 0;
err_socks:
svc_rpcb_cleanup(serv, net);
err_bind:
dprintk("NFS: Couldn't create callback socket: err = %d; "
"net = %p\n", ret, net);
return ret;
}
static struct svc_serv *nfs_callback_create_svc(int minorversion)
{
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
struct svc_serv *serv;
/*
* Check whether we're already up and running.
*/
if (cb_info->task) {
/*
* Note: increase service usage, because later in case of error
* svc_destroy() will be called.
*/
svc_get(cb_info->serv);
return cb_info->serv;
}
/*
* Sanity check: if there's no task,
* we should be the first user ...
*/
if (cb_info->users)
printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
cb_info->users);
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
if (!serv) {
printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
return ERR_PTR(-ENOMEM);
}
/* As there is only one thread we need to over-ride the
* default maximum of 80 connections
*/
serv->sv_maxconn = 1024;
dprintk("nfs_callback_create_svc: service created\n");
return serv;
}
/*
* Bring up the callback thread if it is not already up.
*/
int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
{
struct svc_serv *serv;
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
int ret;
struct net *net = xprt->xprt_net;
mutex_lock(&nfs_callback_mutex);
serv = nfs_callback_create_svc(minorversion);
if (IS_ERR(serv)) {
ret = PTR_ERR(serv);
goto err_create;
}
ret = nfs_callback_up_net(minorversion, serv, net);
if (ret < 0)
goto err_net;
ret = nfs_callback_start_svc(minorversion, xprt, serv);
if (ret < 0)
goto err_start;
cb_info->users++;
/* /*
* svc_create creates the svc_serv with sv_nrthreads == 1, and then * svc_create creates the svc_serv with sv_nrthreads == 1, and then
* svc_prepare_thread increments that. So we need to call svc_destroy * svc_prepare_thread increments that. So we need to call svc_destroy
* on both success and failure so that the refcount is 1 when the * on both success and failure so that the refcount is 1 when the
* thread exits. * thread exits.
*/ */
if (serv) err_net:
svc_destroy(serv); svc_destroy(serv);
err_create:
mutex_unlock(&nfs_callback_mutex); mutex_unlock(&nfs_callback_mutex);
return ret; return ret;
out_err:
dprintk("NFS: Couldn't create callback socket or server thread; " err_start:
"err = %d\n", ret); nfs_callback_down_net(minorversion, serv, net);
cb_info->users--; dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
if (serv) goto err_net;
svc_shutdown_net(serv, net);
goto out;
} }
/* /*
* Kill the callback thread if it's no longer being used. * Kill the callback thread if it's no longer being used.
*/ */
void nfs_callback_down(int minorversion) void nfs_callback_down(int minorversion, struct net *net)
{ {
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
mutex_lock(&nfs_callback_mutex); mutex_lock(&nfs_callback_mutex);
nfs_callback_down_net(minorversion, cb_info->serv, net);
cb_info->users--; cb_info->users--;
if (cb_info->users == 0 && cb_info->task != NULL) { if (cb_info->users == 0 && cb_info->task != NULL) {
kthread_stop(cb_info->task); kthread_stop(cb_info->task);
svc_shutdown_net(cb_info->serv, &init_net); dprintk("nfs_callback_down: service stopped\n");
svc_exit_thread(cb_info->rqst); svc_exit_thread(cb_info->rqst);
dprintk("nfs_callback_down: service destroyed\n");
cb_info->serv = NULL; cb_info->serv = NULL;
cb_info->rqst = NULL; cb_info->rqst = NULL;
cb_info->task = NULL; cb_info->task = NULL;
......
...@@ -194,7 +194,7 @@ extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy, ...@@ -194,7 +194,7 @@ extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
struct cb_process_state *cps); struct cb_process_state *cps);
#if IS_ENABLED(CONFIG_NFS_V4) #if IS_ENABLED(CONFIG_NFS_V4)
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
extern void nfs_callback_down(int minorversion); extern void nfs_callback_down(int minorversion, struct net *net);
extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid); const nfs4_stateid *stateid);
extern int nfs4_set_callback_sessionid(struct nfs_client *clp); extern int nfs4_set_callback_sessionid(struct nfs_client *clp);
...@@ -209,6 +209,5 @@ extern int nfs4_set_callback_sessionid(struct nfs_client *clp); ...@@ -209,6 +209,5 @@ extern int nfs4_set_callback_sessionid(struct nfs_client *clp);
extern unsigned int nfs_callback_set_tcpport; extern unsigned int nfs_callback_set_tcpport;
extern unsigned short nfs_callback_tcpport; extern unsigned short nfs_callback_tcpport;
extern unsigned short nfs_callback_tcpport6;
#endif /* __LINUX_FS_NFS_CALLBACK_H */ #endif /* __LINUX_FS_NFS_CALLBACK_H */
...@@ -122,7 +122,15 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp, ...@@ -122,7 +122,15 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
ino = igrab(lo->plh_inode); ino = igrab(lo->plh_inode);
if (!ino) if (!ino)
continue; continue;
get_layout_hdr(lo); spin_lock(&ino->i_lock);
/* Is this layout in the process of being freed? */
if (NFS_I(ino)->layout != lo) {
spin_unlock(&ino->i_lock);
iput(ino);
continue;
}
pnfs_get_layout_hdr(lo);
spin_unlock(&ino->i_lock);
return lo; return lo;
} }
} }
...@@ -158,7 +166,7 @@ static u32 initiate_file_draining(struct nfs_client *clp, ...@@ -158,7 +166,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
ino = lo->plh_inode; ino = lo->plh_inode;
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) ||
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 else
...@@ -166,7 +174,7 @@ static u32 initiate_file_draining(struct nfs_client *clp, ...@@ -166,7 +174,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true); pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&free_me_list); pnfs_free_lseg_list(&free_me_list);
put_layout_hdr(lo); pnfs_put_layout_hdr(lo);
iput(ino); iput(ino);
return rv; return rv;
} }
...@@ -196,9 +204,18 @@ static u32 initiate_bulk_draining(struct nfs_client *clp, ...@@ -196,9 +204,18 @@ static u32 initiate_bulk_draining(struct nfs_client *clp,
continue; continue;
list_for_each_entry(lo, &server->layouts, plh_layouts) { list_for_each_entry(lo, &server->layouts, plh_layouts) {
if (!igrab(lo->plh_inode)) ino = igrab(lo->plh_inode);
if (ino)
continue;
spin_lock(&ino->i_lock);
/* Is this layout in the process of being freed? */
if (NFS_I(ino)->layout != lo) {
spin_unlock(&ino->i_lock);
iput(ino);
continue; continue;
get_layout_hdr(lo); }
pnfs_get_layout_hdr(lo);
spin_unlock(&ino->i_lock);
BUG_ON(!list_empty(&lo->plh_bulk_recall)); BUG_ON(!list_empty(&lo->plh_bulk_recall));
list_add(&lo->plh_bulk_recall, &recall_list); list_add(&lo->plh_bulk_recall, &recall_list);
} }
...@@ -211,12 +228,12 @@ static u32 initiate_bulk_draining(struct nfs_client *clp, ...@@ -211,12 +228,12 @@ static u32 initiate_bulk_draining(struct nfs_client *clp,
ino = lo->plh_inode; ino = lo->plh_inode;
spin_lock(&ino->i_lock); spin_lock(&ino->i_lock);
set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags); set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
if (mark_matching_lsegs_invalid(lo, &free_me_list, &range)) if (pnfs_mark_matching_lsegs_invalid(lo, &free_me_list, &range))
rv = NFS4ERR_DELAY; rv = NFS4ERR_DELAY;
list_del_init(&lo->plh_bulk_recall); list_del_init(&lo->plh_bulk_recall);
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&free_me_list); pnfs_free_lseg_list(&free_me_list);
put_layout_hdr(lo); pnfs_put_layout_hdr(lo);
iput(ino); iput(ino);
} }
return rv; return rv;
......
...@@ -93,10 +93,10 @@ static struct nfs_subversion *find_nfs_version(unsigned int version) ...@@ -93,10 +93,10 @@ static struct nfs_subversion *find_nfs_version(unsigned int version)
spin_unlock(&nfs_version_lock); spin_unlock(&nfs_version_lock);
return nfs; return nfs;
} }
}; }
spin_unlock(&nfs_version_lock); spin_unlock(&nfs_version_lock);
return ERR_PTR(-EPROTONOSUPPORT);; return ERR_PTR(-EPROTONOSUPPORT);
} }
struct nfs_subversion *get_nfs_version(unsigned int version) struct nfs_subversion *get_nfs_version(unsigned int version)
...@@ -498,7 +498,8 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, ...@@ -498,7 +498,8 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
return nfs_found_client(cl_init, clp); return nfs_found_client(cl_init, clp);
} }
if (new) { if (new) {
list_add(&new->cl_share_link, &nn->nfs_client_list); list_add_tail(&new->cl_share_link,
&nn->nfs_client_list);
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
new->cl_flags = cl_init->init_flags; new->cl_flags = cl_init->init_flags;
return rpc_ops->init_client(new, timeparms, ip_addr, return rpc_ops->init_client(new, timeparms, ip_addr,
...@@ -668,7 +669,8 @@ int nfs_init_server_rpcclient(struct nfs_server *server, ...@@ -668,7 +669,8 @@ int nfs_init_server_rpcclient(struct nfs_server *server,
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
server->client = rpc_clone_client(clp->cl_rpcclient); server->client = rpc_clone_client_set_auth(clp->cl_rpcclient,
pseudoflavour);
if (IS_ERR(server->client)) { if (IS_ERR(server->client)) {
dprintk("%s: couldn't create rpc_client!\n", __func__); dprintk("%s: couldn't create rpc_client!\n", __func__);
return PTR_ERR(server->client); return PTR_ERR(server->client);
...@@ -678,16 +680,6 @@ int nfs_init_server_rpcclient(struct nfs_server *server, ...@@ -678,16 +680,6 @@ int nfs_init_server_rpcclient(struct nfs_server *server,
timeo, timeo,
sizeof(server->client->cl_timeout_default)); sizeof(server->client->cl_timeout_default));
server->client->cl_timeout = &server->client->cl_timeout_default; server->client->cl_timeout = &server->client->cl_timeout_default;
if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
struct rpc_auth *auth;
auth = rpcauth_create(pseudoflavour, server->client);
if (IS_ERR(auth)) {
dprintk("%s: couldn't create credcache!\n", __func__);
return PTR_ERR(auth);
}
}
server->client->cl_softrtry = 0; server->client->cl_softrtry = 0;
if (server->flags & NFS_MOUNT_SOFT) if (server->flags & NFS_MOUNT_SOFT)
server->client->cl_softrtry = 1; server->client->cl_softrtry = 1;
...@@ -761,6 +753,8 @@ static int nfs_init_server(struct nfs_server *server, ...@@ -761,6 +753,8 @@ static int nfs_init_server(struct nfs_server *server,
data->timeo, data->retrans); data->timeo, data->retrans);
if (data->flags & NFS_MOUNT_NORESVPORT) if (data->flags & NFS_MOUNT_NORESVPORT)
set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
if (server->options & NFS_OPTION_MIGRATION)
set_bit(NFS_CS_MIGRATION, &cl_init.init_flags);
/* Allocate or find a client reference we can use */ /* Allocate or find a client reference we can use */
clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX); clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX);
...@@ -855,7 +849,6 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, ...@@ -855,7 +849,6 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
if (server->wsize > NFS_MAX_FILE_IO_SIZE) if (server->wsize > NFS_MAX_FILE_IO_SIZE)
server->wsize = NFS_MAX_FILE_IO_SIZE; server->wsize = NFS_MAX_FILE_IO_SIZE;
server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
server->pnfs_blksize = fsinfo->blksize;
server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
......
...@@ -2072,7 +2072,7 @@ static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry * ...@@ -2072,7 +2072,7 @@ static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *
nfs_access_free_entry(entry); nfs_access_free_entry(entry);
} }
static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
{ {
struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL); struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
if (cache == NULL) if (cache == NULL)
...@@ -2098,6 +2098,20 @@ static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *s ...@@ -2098,6 +2098,20 @@ static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *s
spin_unlock(&nfs_access_lru_lock); spin_unlock(&nfs_access_lru_lock);
} }
} }
EXPORT_SYMBOL_GPL(nfs_access_add_cache);
void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
{
entry->mask = 0;
if (access_result & NFS4_ACCESS_READ)
entry->mask |= MAY_READ;
if (access_result &
(NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (access_result & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
EXPORT_SYMBOL_GPL(nfs_access_set_mask);
static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{ {
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/task_io_accounting_ops.h> #include <linux/task_io_accounting_ops.h>
#include <linux/module.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_page.h> #include <linux/nfs_page.h>
...@@ -78,6 +79,7 @@ struct nfs_direct_req { ...@@ -78,6 +79,7 @@ struct nfs_direct_req {
atomic_t io_count; /* i/os we're waiting for */ atomic_t io_count; /* i/os we're waiting for */
spinlock_t lock; /* protect completion state */ spinlock_t lock; /* protect completion state */
ssize_t count, /* bytes actually processed */ ssize_t count, /* bytes actually processed */
bytes_left, /* bytes left to be sent */
error; /* any reported error */ error; /* any reported error */
struct completion completion; /* wait for i/o completion */ struct completion completion; /* wait for i/o completion */
...@@ -190,6 +192,12 @@ static void nfs_direct_req_release(struct nfs_direct_req *dreq) ...@@ -190,6 +192,12 @@ static void nfs_direct_req_release(struct nfs_direct_req *dreq)
kref_put(&dreq->kref, nfs_direct_req_free); kref_put(&dreq->kref, nfs_direct_req_free);
} }
ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq)
{
return dreq->bytes_left;
}
EXPORT_SYMBOL_GPL(nfs_dreq_bytes_left);
/* /*
* Collects and returns the final error value/byte-count. * Collects and returns the final error value/byte-count.
*/ */
...@@ -390,6 +398,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de ...@@ -390,6 +398,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de
user_addr += req_len; user_addr += req_len;
pos += req_len; pos += req_len;
count -= req_len; count -= req_len;
dreq->bytes_left -= req_len;
} }
/* The nfs_page now hold references to these pages */ /* The nfs_page now hold references to these pages */
nfs_direct_release_pages(pagevec, npages); nfs_direct_release_pages(pagevec, npages);
...@@ -450,23 +459,28 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -450,23 +459,28 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
ssize_t result = -ENOMEM; ssize_t result = -ENOMEM;
struct inode *inode = iocb->ki_filp->f_mapping->host; struct inode *inode = iocb->ki_filp->f_mapping->host;
struct nfs_direct_req *dreq; struct nfs_direct_req *dreq;
struct nfs_lock_context *l_ctx;
dreq = nfs_direct_req_alloc(); dreq = nfs_direct_req_alloc();
if (dreq == NULL) if (dreq == NULL)
goto out; goto out;
dreq->inode = inode; dreq->inode = inode;
dreq->bytes_left = iov_length(iov, nr_segs);
dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
dreq->l_ctx = nfs_get_lock_context(dreq->ctx); l_ctx = nfs_get_lock_context(dreq->ctx);
if (dreq->l_ctx == NULL) if (IS_ERR(l_ctx)) {
result = PTR_ERR(l_ctx);
goto out_release; goto out_release;
}
dreq->l_ctx = l_ctx;
if (!is_sync_kiocb(iocb)) if (!is_sync_kiocb(iocb))
dreq->iocb = iocb; dreq->iocb = iocb;
NFS_I(inode)->read_io += iov_length(iov, nr_segs);
result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio); result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio);
if (!result) if (!result)
result = nfs_direct_wait(dreq); result = nfs_direct_wait(dreq);
NFS_I(inode)->read_io += result;
out_release: out_release:
nfs_direct_req_release(dreq); nfs_direct_req_release(dreq);
out: out:
...@@ -706,6 +720,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *d ...@@ -706,6 +720,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *d
user_addr += req_len; user_addr += req_len;
pos += req_len; pos += req_len;
count -= req_len; count -= req_len;
dreq->bytes_left -= req_len;
} }
/* The nfs_page now hold references to these pages */ /* The nfs_page now hold references to these pages */
nfs_direct_release_pages(pagevec, npages); nfs_direct_release_pages(pagevec, npages);
...@@ -814,6 +829,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, ...@@ -814,6 +829,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
get_dreq(dreq); get_dreq(dreq);
atomic_inc(&inode->i_dio_count); atomic_inc(&inode->i_dio_count);
NFS_I(dreq->inode)->write_io += iov_length(iov, nr_segs);
for (seg = 0; seg < nr_segs; seg++) { for (seg = 0; seg < nr_segs; seg++) {
const struct iovec *vec = &iov[seg]; const struct iovec *vec = &iov[seg];
result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio); result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio);
...@@ -825,7 +841,6 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, ...@@ -825,7 +841,6 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
pos += vec->iov_len; pos += vec->iov_len;
} }
nfs_pageio_complete(&desc); nfs_pageio_complete(&desc);
NFS_I(dreq->inode)->write_io += desc.pg_bytes_written;
/* /*
* If no bytes were started, return the error, and let the * If no bytes were started, return the error, and let the
...@@ -849,16 +864,21 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -849,16 +864,21 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
ssize_t result = -ENOMEM; ssize_t result = -ENOMEM;
struct inode *inode = iocb->ki_filp->f_mapping->host; struct inode *inode = iocb->ki_filp->f_mapping->host;
struct nfs_direct_req *dreq; struct nfs_direct_req *dreq;
struct nfs_lock_context *l_ctx;
dreq = nfs_direct_req_alloc(); dreq = nfs_direct_req_alloc();
if (!dreq) if (!dreq)
goto out; goto out;
dreq->inode = inode; dreq->inode = inode;
dreq->bytes_left = count;
dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
dreq->l_ctx = nfs_get_lock_context(dreq->ctx); l_ctx = nfs_get_lock_context(dreq->ctx);
if (dreq->l_ctx == NULL) if (IS_ERR(l_ctx)) {
result = PTR_ERR(l_ctx);
goto out_release; goto out_release;
}
dreq->l_ctx = l_ctx;
if (!is_sync_kiocb(iocb)) if (!is_sync_kiocb(iocb))
dreq->iocb = iocb; dreq->iocb = iocb;
......
...@@ -259,7 +259,7 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -259,7 +259,7 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_open_context *ctx = nfs_file_open_context(file);
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int have_error, status; int have_error, do_resend, status;
int ret = 0; int ret = 0;
dprintk("NFS: fsync file(%s/%s) datasync %d\n", dprintk("NFS: fsync file(%s/%s) datasync %d\n",
...@@ -267,15 +267,23 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -267,15 +267,23 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
datasync); datasync);
nfs_inc_stats(inode, NFSIOS_VFSFSYNC); nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
do_resend = test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
status = nfs_commit_inode(inode, FLUSH_SYNC); status = nfs_commit_inode(inode, FLUSH_SYNC);
if (status >= 0 && ret < 0)
status = ret;
have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
if (have_error) if (have_error) {
ret = xchg(&ctx->error, 0); ret = xchg(&ctx->error, 0);
if (!ret && status < 0) if (ret)
goto out;
}
if (status < 0) {
ret = status; ret = status;
goto out;
}
do_resend |= test_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
if (do_resend)
ret = -EAGAIN;
out:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(nfs_file_fsync_commit); EXPORT_SYMBOL_GPL(nfs_file_fsync_commit);
...@@ -286,13 +294,22 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -286,13 +294,22 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
int ret; int ret;
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
ret = filemap_write_and_wait_range(inode->i_mapping, start, end); do {
if (ret != 0) ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
goto out; if (ret != 0)
mutex_lock(&inode->i_mutex); break;
ret = nfs_file_fsync_commit(file, start, end, datasync); mutex_lock(&inode->i_mutex);
mutex_unlock(&inode->i_mutex); ret = nfs_file_fsync_commit(file, start, end, datasync);
out: mutex_unlock(&inode->i_mutex);
/*
* If nfs_file_fsync_commit detected a server reboot, then
* resend all dirty pages that might have been covered by
* the NFS_CONTEXT_RESEND_WRITES flag
*/
start = 0;
end = LLONG_MAX;
} while (ret == -EAGAIN);
return ret; return ret;
} }
......
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_CLIENT #define NFSDBG_FACILITY NFSDBG_CLIENT
/* /*
......
...@@ -55,18 +55,19 @@ ...@@ -55,18 +55,19 @@
static const struct cred *id_resolver_cache; static const struct cred *id_resolver_cache;
static struct key_type key_type_id_resolver_legacy; static struct key_type key_type_id_resolver_legacy;
struct idmap {
struct rpc_pipe *idmap_pipe;
struct key_construction *idmap_key_cons;
struct mutex idmap_mutex;
};
struct idmap_legacy_upcalldata { struct idmap_legacy_upcalldata {
struct rpc_pipe_msg pipe_msg; struct rpc_pipe_msg pipe_msg;
struct idmap_msg idmap_msg; struct idmap_msg idmap_msg;
struct key_construction *key_cons;
struct idmap *idmap; struct idmap *idmap;
}; };
struct idmap {
struct rpc_pipe *idmap_pipe;
struct idmap_legacy_upcalldata *idmap_upcall_data;
struct mutex idmap_mutex;
};
/** /**
* nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
* @fattr: fully initialised struct nfs_fattr * @fattr: fully initialised struct nfs_fattr
...@@ -158,7 +159,7 @@ static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *re ...@@ -158,7 +159,7 @@ static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *re
return 0; return 0;
memcpy(buf, name, namelen); memcpy(buf, name, namelen);
buf[namelen] = '\0'; buf[namelen] = '\0';
if (strict_strtoul(buf, 0, &val) != 0) if (kstrtoul(buf, 0, &val) != 0)
return 0; return 0;
*res = val; *res = val;
return 1; return 1;
...@@ -330,7 +331,6 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, ...@@ -330,7 +331,6 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
ret = nfs_idmap_request_key(&key_type_id_resolver_legacy, ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
name, namelen, type, data, name, namelen, type, data,
data_size, idmap); data_size, idmap);
idmap->idmap_key_cons = NULL;
mutex_unlock(&idmap->idmap_mutex); mutex_unlock(&idmap->idmap_mutex);
} }
return ret; return ret;
...@@ -364,7 +364,7 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *typ ...@@ -364,7 +364,7 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *typ
if (data_size <= 0) { if (data_size <= 0) {
ret = -EINVAL; ret = -EINVAL;
} else { } else {
ret = strict_strtol(id_str, 10, &id_long); ret = kstrtol(id_str, 10, &id_long);
*id = (__u32)id_long; *id = (__u32)id_long;
} }
return ret; return ret;
...@@ -465,8 +465,6 @@ nfs_idmap_new(struct nfs_client *clp) ...@@ -465,8 +465,6 @@ nfs_idmap_new(struct nfs_client *clp)
struct rpc_pipe *pipe; struct rpc_pipe *pipe;
int error; int error;
BUG_ON(clp->cl_idmap != NULL);
idmap = kzalloc(sizeof(*idmap), GFP_KERNEL); idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);
if (idmap == NULL) if (idmap == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -510,7 +508,6 @@ static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event, ...@@ -510,7 +508,6 @@ static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event,
switch (event) { switch (event) {
case RPC_PIPEFS_MOUNT: case RPC_PIPEFS_MOUNT:
BUG_ON(clp->cl_rpcclient->cl_dentry == NULL);
err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry, err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
clp->cl_idmap, clp->cl_idmap,
clp->cl_idmap->idmap_pipe); clp->cl_idmap->idmap_pipe);
...@@ -632,9 +629,6 @@ static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap, ...@@ -632,9 +629,6 @@ static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
substring_t substr; substring_t substr;
int token, ret; int token, ret;
memset(im, 0, sizeof(*im));
memset(msg, 0, sizeof(*msg));
im->im_type = IDMAP_TYPE_GROUP; im->im_type = IDMAP_TYPE_GROUP;
token = match_token(desc, nfs_idmap_tokens, &substr); token = match_token(desc, nfs_idmap_tokens, &substr);
...@@ -665,6 +659,35 @@ static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap, ...@@ -665,6 +659,35 @@ static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
return ret; return ret;
} }
static bool
nfs_idmap_prepare_pipe_upcall(struct idmap *idmap,
struct idmap_legacy_upcalldata *data)
{
if (idmap->idmap_upcall_data != NULL) {
WARN_ON_ONCE(1);
return false;
}
idmap->idmap_upcall_data = data;
return true;
}
static void
nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret)
{
struct key_construction *cons = idmap->idmap_upcall_data->key_cons;
kfree(idmap->idmap_upcall_data);
idmap->idmap_upcall_data = NULL;
complete_request_key(cons, ret);
}
static void
nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret)
{
if (idmap->idmap_upcall_data != NULL)
nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
}
static int nfs_idmap_legacy_upcall(struct key_construction *cons, static int nfs_idmap_legacy_upcall(struct key_construction *cons,
const char *op, const char *op,
void *aux) void *aux)
...@@ -677,29 +700,28 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons, ...@@ -677,29 +700,28 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
int ret = -ENOMEM; int ret = -ENOMEM;
/* msg and im are freed in idmap_pipe_destroy_msg */ /* msg and im are freed in idmap_pipe_destroy_msg */
data = kmalloc(sizeof(*data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
goto out1; goto out1;
msg = &data->pipe_msg; msg = &data->pipe_msg;
im = &data->idmap_msg; im = &data->idmap_msg;
data->idmap = idmap; data->idmap = idmap;
data->key_cons = cons;
ret = nfs_idmap_prepare_message(key->description, idmap, im, msg); ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
if (ret < 0) if (ret < 0)
goto out2; goto out2;
BUG_ON(idmap->idmap_key_cons != NULL); ret = -EAGAIN;
idmap->idmap_key_cons = cons; if (!nfs_idmap_prepare_pipe_upcall(idmap, data))
goto out2;
ret = rpc_queue_upcall(idmap->idmap_pipe, msg); ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
if (ret < 0) if (ret < 0)
goto out3; nfs_idmap_abort_pipe_upcall(idmap, ret);
return ret; return ret;
out3:
idmap->idmap_key_cons = NULL;
out2: out2:
kfree(data); kfree(data);
out1: out1:
...@@ -714,21 +736,32 @@ static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *dat ...@@ -714,21 +736,32 @@ static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *dat
authkey); authkey);
} }
static int nfs_idmap_read_message(struct idmap_msg *im, struct key *key, struct key *authkey) static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
struct idmap_msg *upcall,
struct key *key, struct key *authkey)
{ {
char id_str[NFS_UINT_MAXLEN]; char id_str[NFS_UINT_MAXLEN];
int ret = -EINVAL; int ret = -ENOKEY;
/* ret = -ENOKEY */
if (upcall->im_type != im->im_type || upcall->im_conv != im->im_conv)
goto out;
switch (im->im_conv) { switch (im->im_conv) {
case IDMAP_CONV_NAMETOID: case IDMAP_CONV_NAMETOID:
if (strcmp(upcall->im_name, im->im_name) != 0)
break;
sprintf(id_str, "%d", im->im_id); sprintf(id_str, "%d", im->im_id);
ret = nfs_idmap_instantiate(key, authkey, id_str); ret = nfs_idmap_instantiate(key, authkey, id_str);
break; break;
case IDMAP_CONV_IDTONAME: case IDMAP_CONV_IDTONAME:
if (upcall->im_id != im->im_id)
break;
ret = nfs_idmap_instantiate(key, authkey, im->im_name); ret = nfs_idmap_instantiate(key, authkey, im->im_name);
break; break;
default:
ret = -EINVAL;
} }
out:
return ret; return ret;
} }
...@@ -740,14 +773,16 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -740,14 +773,16 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
struct key_construction *cons; struct key_construction *cons;
struct idmap_msg im; struct idmap_msg im;
size_t namelen_in; size_t namelen_in;
int ret; int ret = -ENOKEY;
/* If instantiation is successful, anyone waiting for key construction /* If instantiation is successful, anyone waiting for key construction
* will have been woken up and someone else may now have used * will have been woken up and someone else may now have used
* idmap_key_cons - so after this point we may no longer touch it. * idmap_key_cons - so after this point we may no longer touch it.
*/ */
cons = ACCESS_ONCE(idmap->idmap_key_cons); if (idmap->idmap_upcall_data == NULL)
idmap->idmap_key_cons = NULL; goto out_noupcall;
cons = idmap->idmap_upcall_data->key_cons;
if (mlen != sizeof(im)) { if (mlen != sizeof(im)) {
ret = -ENOSPC; ret = -ENOSPC;
...@@ -768,16 +803,19 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -768,16 +803,19 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) { if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
ret = nfs_idmap_read_message(&im, cons->key, cons->authkey); ret = nfs_idmap_read_and_verify_message(&im,
&idmap->idmap_upcall_data->idmap_msg,
cons->key, cons->authkey);
if (ret >= 0) { if (ret >= 0) {
key_set_timeout(cons->key, nfs_idmap_cache_timeout); key_set_timeout(cons->key, nfs_idmap_cache_timeout);
ret = mlen; ret = mlen;
} }
out: out:
complete_request_key(cons, ret); nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
out_noupcall:
return ret; return ret;
} }
...@@ -788,14 +826,9 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) ...@@ -788,14 +826,9 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
struct idmap_legacy_upcalldata, struct idmap_legacy_upcalldata,
pipe_msg); pipe_msg);
struct idmap *idmap = data->idmap; struct idmap *idmap = data->idmap;
struct key_construction *cons;
if (msg->errno) { if (msg->errno)
cons = ACCESS_ONCE(idmap->idmap_key_cons); nfs_idmap_abort_pipe_upcall(idmap, msg->errno);
idmap->idmap_key_cons = NULL;
complete_request_key(cons, msg->errno);
}
/* Free memory allocated in nfs_idmap_legacy_upcall() */
kfree(data);
} }
static void static void
...@@ -803,7 +836,8 @@ idmap_release_pipe(struct inode *inode) ...@@ -803,7 +836,8 @@ idmap_release_pipe(struct inode *inode)
{ {
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
struct idmap *idmap = (struct idmap *)rpci->private; struct idmap *idmap = (struct idmap *)rpci->private;
idmap->idmap_key_cons = NULL;
nfs_idmap_abort_pipe_upcall(idmap, -EPIPE);
} }
int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
......
...@@ -547,8 +547,8 @@ EXPORT_SYMBOL_GPL(nfs_getattr); ...@@ -547,8 +547,8 @@ EXPORT_SYMBOL_GPL(nfs_getattr);
static void nfs_init_lock_context(struct nfs_lock_context *l_ctx) static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
{ {
atomic_set(&l_ctx->count, 1); atomic_set(&l_ctx->count, 1);
l_ctx->lockowner = current->files; l_ctx->lockowner.l_owner = current->files;
l_ctx->pid = current->tgid; l_ctx->lockowner.l_pid = current->tgid;
INIT_LIST_HEAD(&l_ctx->list); INIT_LIST_HEAD(&l_ctx->list);
} }
...@@ -557,9 +557,9 @@ static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context ...@@ -557,9 +557,9 @@ static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context
struct nfs_lock_context *pos; struct nfs_lock_context *pos;
list_for_each_entry(pos, &ctx->lock_context.list, list) { list_for_each_entry(pos, &ctx->lock_context.list, list) {
if (pos->lockowner != current->files) if (pos->lockowner.l_owner != current->files)
continue; continue;
if (pos->pid != current->tgid) if (pos->lockowner.l_pid != current->tgid)
continue; continue;
atomic_inc(&pos->count); atomic_inc(&pos->count);
return pos; return pos;
...@@ -578,7 +578,7 @@ struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx) ...@@ -578,7 +578,7 @@ struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
new = kmalloc(sizeof(*new), GFP_KERNEL); new = kmalloc(sizeof(*new), GFP_KERNEL);
if (new == NULL) if (new == NULL)
return NULL; return ERR_PTR(-ENOMEM);
nfs_init_lock_context(new); nfs_init_lock_context(new);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
res = __nfs_find_lock_context(ctx); res = __nfs_find_lock_context(ctx);
......
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