forked from Minki/linux
b24413180f
Many source files in the tree are missing licensing information, which makes it harder for compliance tools to determine the correct license. By default all files without license information are under the default license of the kernel, which is GPL version 2. Update the files which contain no license information with the 'GPL-2.0' SPDX license identifier. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This patch is based on work done by Thomas Gleixner and Kate Stewart and Philippe Ombredanne. How this work was done: Patches were generated and checked against linux-4.14-rc6 for a subset of the use cases: - file had no licensing information it it. - file was a */uapi/* one with no licensing information in it, - file was a */uapi/* one with existing licensing information, Further patches will be generated in subsequent months to fix up cases where non-standard license headers were used, and references to license had to be inferred by heuristics based on keywords. The analysis to determine which SPDX License Identifier to be applied to a file was done in a spreadsheet of side by side results from of the output of two independent scanners (ScanCode & Windriver) producing SPDX tag:value files created by Philippe Ombredanne. Philippe prepared the base worksheet, and did an initial spot review of a few 1000 files. The 4.13 kernel was the starting point of the analysis with 60,537 files assessed. Kate Stewart did a file by file comparison of the scanner results in the spreadsheet to determine which SPDX license identifier(s) to be applied to the file. She confirmed any determination that was not immediately clear with lawyers working with the Linux Foundation. Criteria used to select files for SPDX license identifier tagging was: - Files considered eligible had to be source code files. - Make and config files were included as candidates if they contained >5 lines of source - File already had some variant of a license header in it (even if <5 lines). All documentation files were explicitly excluded. The following heuristics were used to determine which SPDX license identifiers to apply. - when both scanners couldn't find any license traces, file was considered to have no license information in it, and the top level COPYING file license applied. For non */uapi/* files that summary was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 11139 and resulted in the first patch in this series. If that file was a */uapi/* path one, it was "GPL-2.0 WITH Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 WITH Linux-syscall-note 930 and resulted in the second patch in this series. - if a file had some form of licensing information in it, and was one of the */uapi/* ones, it was denoted with the Linux-syscall-note if any GPL family license was found in the file or had no licensing in it (per prior point). Results summary: SPDX license identifier # files ---------------------------------------------------|------ GPL-2.0 WITH Linux-syscall-note 270 GPL-2.0+ WITH Linux-syscall-note 169 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17 LGPL-2.1+ WITH Linux-syscall-note 15 GPL-1.0+ WITH Linux-syscall-note 14 ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5 LGPL-2.0+ WITH Linux-syscall-note 4 LGPL-2.1 WITH Linux-syscall-note 3 ((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3 ((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1 and that resulted in the third patch in this series. - when the two scanners agreed on the detected license(s), that became the concluded license(s). - when there was disagreement between the two scanners (one detected a license but the other didn't, or they both detected different licenses) a manual inspection of the file occurred. - In most cases a manual inspection of the information in the file resulted in a clear resolution of the license that should apply (and which scanner probably needed to revisit its heuristics). - When it was not immediately clear, the license identifier was confirmed with lawyers working with the Linux Foundation. - If there was any question as to the appropriate license identifier, the file was flagged for further research and to be revisited later in time. In total, over 70 hours of logged manual review was done on the spreadsheet to determine the SPDX license identifiers to apply to the source files by Kate, Philippe, Thomas and, in some cases, confirmation by lawyers working with the Linux Foundation. Kate also obtained a third independent scan of the 4.13 code base from FOSSology, and compared selected files where the other two scanners disagreed against that SPDX file, to see if there was new insights. The Windriver scanner is based on an older version of FOSSology in part, so they are related. Thomas did random spot checks in about 500 files from the spreadsheets for the uapi headers and agreed with SPDX license identifier in the files he inspected. For the non-uapi files Thomas did random spot checks in about 15000 files. In initial set of patches against 4.14-rc6, 3 files were found to have copy/paste license identifier errors, and have been fixed to reflect the correct identifier. Additionally Philippe spent 10 hours this week doing a detailed manual inspection and review of the 12,461 patched files from the initial patch version early this week with: - a full scancode scan run, collecting the matched texts, detected license ids and scores - reviewing anything where there was a license detected (about 500+ files) to ensure that the applied SPDX license was correct - reviewing anything where there was no detection but the patch license was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied SPDX license was correct This produced a worksheet with 20 files needing minor correction. This worksheet was then exported into 3 different .csv files for the different types of files to be modified. These .csv files were then reviewed by Greg. Thomas wrote a script to parse the csv files and add the proper SPDX tag to the file, in the format that the file expected. This script was further refined by Greg based on the output to detect more types of files automatically and to distinguish between header and source .c files (which need different comment types.) Finally Greg ran the script using the .csv files to generate the patches. Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
647 lines
18 KiB
C
647 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Device operations for the pnfs nfs4 file layout driver.
|
|
*
|
|
* Copyright (c) 2014, Primary Data, Inc. All rights reserved.
|
|
*
|
|
* Tao Peng <bergwolf@primarydata.com>
|
|
*/
|
|
|
|
#include <linux/nfs_fs.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sunrpc/addr.h>
|
|
|
|
#include "../internal.h"
|
|
#include "../nfs4session.h"
|
|
#include "flexfilelayout.h"
|
|
|
|
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
|
|
|
|
static unsigned int dataserver_timeo = NFS_DEF_TCP_RETRANS;
|
|
static unsigned int dataserver_retrans;
|
|
|
|
static bool ff_layout_has_available_ds(struct pnfs_layout_segment *lseg);
|
|
|
|
void nfs4_ff_layout_put_deviceid(struct nfs4_ff_layout_ds *mirror_ds)
|
|
{
|
|
if (!IS_ERR_OR_NULL(mirror_ds))
|
|
nfs4_put_deviceid_node(&mirror_ds->id_node);
|
|
}
|
|
|
|
void nfs4_ff_layout_free_deviceid(struct nfs4_ff_layout_ds *mirror_ds)
|
|
{
|
|
nfs4_print_deviceid(&mirror_ds->id_node.deviceid);
|
|
nfs4_pnfs_ds_put(mirror_ds->ds);
|
|
kfree(mirror_ds->ds_versions);
|
|
kfree_rcu(mirror_ds, id_node.rcu);
|
|
}
|
|
|
|
/* Decode opaque device data and construct new_ds using it */
|
|
struct nfs4_ff_layout_ds *
|
|
nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
|
gfp_t gfp_flags)
|
|
{
|
|
struct xdr_stream stream;
|
|
struct xdr_buf buf;
|
|
struct page *scratch;
|
|
struct list_head dsaddrs;
|
|
struct nfs4_pnfs_ds_addr *da;
|
|
struct nfs4_ff_layout_ds *new_ds = NULL;
|
|
struct nfs4_ff_ds_version *ds_versions = NULL;
|
|
u32 mp_count;
|
|
u32 version_count;
|
|
__be32 *p;
|
|
int i, ret = -ENOMEM;
|
|
|
|
/* set up xdr stream */
|
|
scratch = alloc_page(gfp_flags);
|
|
if (!scratch)
|
|
goto out_err;
|
|
|
|
new_ds = kzalloc(sizeof(struct nfs4_ff_layout_ds), gfp_flags);
|
|
if (!new_ds)
|
|
goto out_scratch;
|
|
|
|
nfs4_init_deviceid_node(&new_ds->id_node,
|
|
server,
|
|
&pdev->dev_id);
|
|
INIT_LIST_HEAD(&dsaddrs);
|
|
|
|
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
|
|
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
|
|
|
/* multipath count */
|
|
p = xdr_inline_decode(&stream, 4);
|
|
if (unlikely(!p))
|
|
goto out_err_drain_dsaddrs;
|
|
mp_count = be32_to_cpup(p);
|
|
dprintk("%s: multipath ds count %d\n", __func__, mp_count);
|
|
|
|
for (i = 0; i < mp_count; i++) {
|
|
/* multipath ds */
|
|
da = nfs4_decode_mp_ds_addr(server->nfs_client->cl_net,
|
|
&stream, gfp_flags);
|
|
if (da)
|
|
list_add_tail(&da->da_node, &dsaddrs);
|
|
}
|
|
if (list_empty(&dsaddrs)) {
|
|
dprintk("%s: no suitable DS addresses found\n",
|
|
__func__);
|
|
ret = -ENOMEDIUM;
|
|
goto out_err_drain_dsaddrs;
|
|
}
|
|
|
|
/* version count */
|
|
p = xdr_inline_decode(&stream, 4);
|
|
if (unlikely(!p))
|
|
goto out_err_drain_dsaddrs;
|
|
version_count = be32_to_cpup(p);
|
|
dprintk("%s: version count %d\n", __func__, version_count);
|
|
|
|
ds_versions = kzalloc(version_count * sizeof(struct nfs4_ff_ds_version),
|
|
gfp_flags);
|
|
if (!ds_versions)
|
|
goto out_scratch;
|
|
|
|
for (i = 0; i < version_count; i++) {
|
|
/* 20 = version(4) + minor_version(4) + rsize(4) + wsize(4) +
|
|
* tightly_coupled(4) */
|
|
p = xdr_inline_decode(&stream, 20);
|
|
if (unlikely(!p))
|
|
goto out_err_drain_dsaddrs;
|
|
ds_versions[i].version = be32_to_cpup(p++);
|
|
ds_versions[i].minor_version = be32_to_cpup(p++);
|
|
ds_versions[i].rsize = nfs_block_size(be32_to_cpup(p++), NULL);
|
|
ds_versions[i].wsize = nfs_block_size(be32_to_cpup(p++), NULL);
|
|
ds_versions[i].tightly_coupled = be32_to_cpup(p);
|
|
|
|
if (ds_versions[i].rsize > NFS_MAX_FILE_IO_SIZE)
|
|
ds_versions[i].rsize = NFS_MAX_FILE_IO_SIZE;
|
|
if (ds_versions[i].wsize > NFS_MAX_FILE_IO_SIZE)
|
|
ds_versions[i].wsize = NFS_MAX_FILE_IO_SIZE;
|
|
|
|
/*
|
|
* check for valid major/minor combination.
|
|
* currently we support dataserver which talk:
|
|
* v3, v4.0, v4.1, v4.2
|
|
*/
|
|
if (!((ds_versions[i].version == 3 && ds_versions[i].minor_version == 0) ||
|
|
(ds_versions[i].version == 4 && ds_versions[i].minor_version < 3))) {
|
|
dprintk("%s: [%d] unsupported ds version %d-%d\n", __func__,
|
|
i, ds_versions[i].version,
|
|
ds_versions[i].minor_version);
|
|
ret = -EPROTONOSUPPORT;
|
|
goto out_err_drain_dsaddrs;
|
|
}
|
|
|
|
dprintk("%s: [%d] vers %u minor_ver %u rsize %u wsize %u coupled %d\n",
|
|
__func__, i, ds_versions[i].version,
|
|
ds_versions[i].minor_version,
|
|
ds_versions[i].rsize,
|
|
ds_versions[i].wsize,
|
|
ds_versions[i].tightly_coupled);
|
|
}
|
|
|
|
new_ds->ds_versions = ds_versions;
|
|
new_ds->ds_versions_cnt = version_count;
|
|
|
|
new_ds->ds = nfs4_pnfs_ds_add(&dsaddrs, gfp_flags);
|
|
if (!new_ds->ds)
|
|
goto out_err_drain_dsaddrs;
|
|
|
|
/* If DS was already in cache, free ds addrs */
|
|
while (!list_empty(&dsaddrs)) {
|
|
da = list_first_entry(&dsaddrs,
|
|
struct nfs4_pnfs_ds_addr,
|
|
da_node);
|
|
list_del_init(&da->da_node);
|
|
kfree(da->da_remotestr);
|
|
kfree(da);
|
|
}
|
|
|
|
__free_page(scratch);
|
|
return new_ds;
|
|
|
|
out_err_drain_dsaddrs:
|
|
while (!list_empty(&dsaddrs)) {
|
|
da = list_first_entry(&dsaddrs, struct nfs4_pnfs_ds_addr,
|
|
da_node);
|
|
list_del_init(&da->da_node);
|
|
kfree(da->da_remotestr);
|
|
kfree(da);
|
|
}
|
|
|
|
kfree(ds_versions);
|
|
out_scratch:
|
|
__free_page(scratch);
|
|
out_err:
|
|
kfree(new_ds);
|
|
|
|
dprintk("%s ERROR: returning %d\n", __func__, ret);
|
|
return NULL;
|
|
}
|
|
|
|
static void ff_layout_mark_devid_invalid(struct pnfs_layout_segment *lseg,
|
|
struct nfs4_deviceid_node *devid)
|
|
{
|
|
nfs4_delete_deviceid(devid->ld, devid->nfs_client, &devid->deviceid);
|
|
if (!ff_layout_has_available_ds(lseg))
|
|
pnfs_error_mark_layout_for_return(lseg->pls_layout->plh_inode,
|
|
lseg);
|
|
}
|
|
|
|
static bool ff_layout_mirror_valid(struct pnfs_layout_segment *lseg,
|
|
struct nfs4_ff_layout_mirror *mirror,
|
|
bool create)
|
|
{
|
|
if (mirror == NULL || IS_ERR(mirror->mirror_ds))
|
|
goto outerr;
|
|
if (mirror->mirror_ds == NULL) {
|
|
if (create) {
|
|
struct nfs4_deviceid_node *node;
|
|
struct pnfs_layout_hdr *lh = lseg->pls_layout;
|
|
struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV);
|
|
|
|
node = nfs4_find_get_deviceid(NFS_SERVER(lh->plh_inode),
|
|
&mirror->devid, lh->plh_lc_cred,
|
|
GFP_KERNEL);
|
|
if (node)
|
|
mirror_ds = FF_LAYOUT_MIRROR_DS(node);
|
|
|
|
/* check for race with another call to this function */
|
|
if (cmpxchg(&mirror->mirror_ds, NULL, mirror_ds) &&
|
|
mirror_ds != ERR_PTR(-ENODEV))
|
|
nfs4_put_deviceid_node(node);
|
|
} else
|
|
goto outerr;
|
|
}
|
|
|
|
if (IS_ERR(mirror->mirror_ds))
|
|
goto outerr;
|
|
|
|
if (mirror->mirror_ds->ds == NULL) {
|
|
struct nfs4_deviceid_node *devid;
|
|
devid = &mirror->mirror_ds->id_node;
|
|
ff_layout_mark_devid_invalid(lseg, devid);
|
|
return false;
|
|
}
|
|
return true;
|
|
outerr:
|
|
pnfs_error_mark_layout_for_return(lseg->pls_layout->plh_inode, lseg);
|
|
return false;
|
|
}
|
|
|
|
static void extend_ds_error(struct nfs4_ff_layout_ds_err *err,
|
|
u64 offset, u64 length)
|
|
{
|
|
u64 end;
|
|
|
|
end = max_t(u64, pnfs_end_offset(err->offset, err->length),
|
|
pnfs_end_offset(offset, length));
|
|
err->offset = min_t(u64, err->offset, offset);
|
|
err->length = end - err->offset;
|
|
}
|
|
|
|
static int
|
|
ff_ds_error_match(const struct nfs4_ff_layout_ds_err *e1,
|
|
const struct nfs4_ff_layout_ds_err *e2)
|
|
{
|
|
int ret;
|
|
|
|
if (e1->opnum != e2->opnum)
|
|
return e1->opnum < e2->opnum ? -1 : 1;
|
|
if (e1->status != e2->status)
|
|
return e1->status < e2->status ? -1 : 1;
|
|
ret = memcmp(e1->stateid.data, e2->stateid.data,
|
|
sizeof(e1->stateid.data));
|
|
if (ret != 0)
|
|
return ret;
|
|
ret = memcmp(&e1->deviceid, &e2->deviceid, sizeof(e1->deviceid));
|
|
if (ret != 0)
|
|
return ret;
|
|
if (pnfs_end_offset(e1->offset, e1->length) < e2->offset)
|
|
return -1;
|
|
if (e1->offset > pnfs_end_offset(e2->offset, e2->length))
|
|
return 1;
|
|
/* If ranges overlap or are contiguous, they are the same */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ff_layout_add_ds_error_locked(struct nfs4_flexfile_layout *flo,
|
|
struct nfs4_ff_layout_ds_err *dserr)
|
|
{
|
|
struct nfs4_ff_layout_ds_err *err, *tmp;
|
|
struct list_head *head = &flo->error_list;
|
|
int match;
|
|
|
|
/* Do insertion sort w/ merges */
|
|
list_for_each_entry_safe(err, tmp, &flo->error_list, list) {
|
|
match = ff_ds_error_match(err, dserr);
|
|
if (match < 0)
|
|
continue;
|
|
if (match > 0) {
|
|
/* Add entry "dserr" _before_ entry "err" */
|
|
head = &err->list;
|
|
break;
|
|
}
|
|
/* Entries match, so merge "err" into "dserr" */
|
|
extend_ds_error(dserr, err->offset, err->length);
|
|
list_replace(&err->list, &dserr->list);
|
|
kfree(err);
|
|
return;
|
|
}
|
|
|
|
list_add_tail(&dserr->list, head);
|
|
}
|
|
|
|
int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
|
|
struct nfs4_ff_layout_mirror *mirror, u64 offset,
|
|
u64 length, int status, enum nfs_opnum4 opnum,
|
|
gfp_t gfp_flags)
|
|
{
|
|
struct nfs4_ff_layout_ds_err *dserr;
|
|
|
|
if (status == 0)
|
|
return 0;
|
|
|
|
if (mirror->mirror_ds == NULL)
|
|
return -EINVAL;
|
|
|
|
dserr = kmalloc(sizeof(*dserr), gfp_flags);
|
|
if (!dserr)
|
|
return -ENOMEM;
|
|
|
|
INIT_LIST_HEAD(&dserr->list);
|
|
dserr->offset = offset;
|
|
dserr->length = length;
|
|
dserr->status = status;
|
|
dserr->opnum = opnum;
|
|
nfs4_stateid_copy(&dserr->stateid, &mirror->stateid);
|
|
memcpy(&dserr->deviceid, &mirror->mirror_ds->id_node.deviceid,
|
|
NFS4_DEVICEID4_SIZE);
|
|
|
|
spin_lock(&flo->generic_hdr.plh_inode->i_lock);
|
|
ff_layout_add_ds_error_locked(flo, dserr);
|
|
spin_unlock(&flo->generic_hdr.plh_inode->i_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct rpc_cred *
|
|
ff_layout_get_mirror_cred(struct nfs4_ff_layout_mirror *mirror, u32 iomode)
|
|
{
|
|
struct rpc_cred *cred, __rcu **pcred;
|
|
|
|
if (iomode == IOMODE_READ)
|
|
pcred = &mirror->ro_cred;
|
|
else
|
|
pcred = &mirror->rw_cred;
|
|
|
|
rcu_read_lock();
|
|
do {
|
|
cred = rcu_dereference(*pcred);
|
|
if (!cred)
|
|
break;
|
|
|
|
cred = get_rpccred_rcu(cred);
|
|
} while(!cred);
|
|
rcu_read_unlock();
|
|
return cred;
|
|
}
|
|
|
|
struct nfs_fh *
|
|
nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx)
|
|
{
|
|
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, mirror_idx);
|
|
struct nfs_fh *fh = NULL;
|
|
|
|
if (!ff_layout_mirror_valid(lseg, mirror, false)) {
|
|
pr_err_ratelimited("NFS: %s: No data server for mirror offset index %d\n",
|
|
__func__, mirror_idx);
|
|
goto out;
|
|
}
|
|
|
|
/* FIXME: For now assume there is only 1 version available for the DS */
|
|
fh = &mirror->fh_versions[0];
|
|
out:
|
|
return fh;
|
|
}
|
|
|
|
/**
|
|
* nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call
|
|
* @lseg: the layout segment we're operating on
|
|
* @ds_idx: index of the DS to use
|
|
* @fail_return: return layout on connect failure?
|
|
*
|
|
* Try to prepare a DS connection to accept an RPC call. This involves
|
|
* selecting a mirror to use and connecting the client to it if it's not
|
|
* already connected.
|
|
*
|
|
* Since we only need a single functioning mirror to satisfy a read, we don't
|
|
* want to return the layout if there is one. For writes though, any down
|
|
* mirror should result in a LAYOUTRETURN. @fail_return is how we distinguish
|
|
* between the two cases.
|
|
*
|
|
* Returns a pointer to a connected DS object on success or NULL on failure.
|
|
*/
|
|
struct nfs4_pnfs_ds *
|
|
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
|
|
bool fail_return)
|
|
{
|
|
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
|
|
struct nfs4_pnfs_ds *ds = NULL;
|
|
struct nfs4_deviceid_node *devid;
|
|
struct inode *ino = lseg->pls_layout->plh_inode;
|
|
struct nfs_server *s = NFS_SERVER(ino);
|
|
unsigned int max_payload;
|
|
int status;
|
|
|
|
if (!ff_layout_mirror_valid(lseg, mirror, true)) {
|
|
pr_err_ratelimited("NFS: %s: No data server for offset index %d\n",
|
|
__func__, ds_idx);
|
|
goto out;
|
|
}
|
|
|
|
devid = &mirror->mirror_ds->id_node;
|
|
if (ff_layout_test_devid_unavailable(devid))
|
|
goto out_fail;
|
|
|
|
ds = mirror->mirror_ds->ds;
|
|
/* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */
|
|
smp_rmb();
|
|
if (ds->ds_clp)
|
|
goto out;
|
|
|
|
/* FIXME: For now we assume the server sent only one version of NFS
|
|
* to use for the DS.
|
|
*/
|
|
status = nfs4_pnfs_ds_connect(s, ds, devid, dataserver_timeo,
|
|
dataserver_retrans,
|
|
mirror->mirror_ds->ds_versions[0].version,
|
|
mirror->mirror_ds->ds_versions[0].minor_version);
|
|
|
|
/* connect success, check rsize/wsize limit */
|
|
if (!status) {
|
|
max_payload =
|
|
nfs_block_size(rpc_max_payload(ds->ds_clp->cl_rpcclient),
|
|
NULL);
|
|
if (mirror->mirror_ds->ds_versions[0].rsize > max_payload)
|
|
mirror->mirror_ds->ds_versions[0].rsize = max_payload;
|
|
if (mirror->mirror_ds->ds_versions[0].wsize > max_payload)
|
|
mirror->mirror_ds->ds_versions[0].wsize = max_payload;
|
|
goto out;
|
|
}
|
|
out_fail:
|
|
ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
|
|
mirror, lseg->pls_range.offset,
|
|
lseg->pls_range.length, NFS4ERR_NXIO,
|
|
OP_ILLEGAL, GFP_NOIO);
|
|
if (fail_return || !ff_layout_has_available_ds(lseg))
|
|
pnfs_error_mark_layout_for_return(ino, lseg);
|
|
ds = NULL;
|
|
out:
|
|
return ds;
|
|
}
|
|
|
|
struct rpc_cred *
|
|
ff_layout_get_ds_cred(struct pnfs_layout_segment *lseg, u32 ds_idx,
|
|
struct rpc_cred *mdscred)
|
|
{
|
|
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
|
|
struct rpc_cred *cred;
|
|
|
|
if (mirror) {
|
|
cred = ff_layout_get_mirror_cred(mirror, lseg->pls_range.iomode);
|
|
if (!cred)
|
|
cred = get_rpccred(mdscred);
|
|
} else {
|
|
cred = get_rpccred(mdscred);
|
|
}
|
|
return cred;
|
|
}
|
|
|
|
/**
|
|
* Find or create a DS rpc client with th MDS server rpc client auth flavor
|
|
* in the nfs_client cl_ds_clients list.
|
|
*/
|
|
struct rpc_clnt *
|
|
nfs4_ff_find_or_create_ds_client(struct pnfs_layout_segment *lseg, u32 ds_idx,
|
|
struct nfs_client *ds_clp, struct inode *inode)
|
|
{
|
|
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
|
|
|
|
switch (mirror->mirror_ds->ds_versions[0].version) {
|
|
case 3:
|
|
/* For NFSv3 DS, flavor is set when creating DS connections */
|
|
return ds_clp->cl_rpcclient;
|
|
case 4:
|
|
return nfs4_find_or_create_ds_client(ds_clp, inode);
|
|
default:
|
|
BUG();
|
|
}
|
|
}
|
|
|
|
void ff_layout_free_ds_ioerr(struct list_head *head)
|
|
{
|
|
struct nfs4_ff_layout_ds_err *err;
|
|
|
|
while (!list_empty(head)) {
|
|
err = list_first_entry(head,
|
|
struct nfs4_ff_layout_ds_err,
|
|
list);
|
|
list_del(&err->list);
|
|
kfree(err);
|
|
}
|
|
}
|
|
|
|
/* called with inode i_lock held */
|
|
int ff_layout_encode_ds_ioerr(struct xdr_stream *xdr, const struct list_head *head)
|
|
{
|
|
struct nfs4_ff_layout_ds_err *err;
|
|
__be32 *p;
|
|
|
|
list_for_each_entry(err, head, list) {
|
|
/* offset(8) + length(8) + stateid(NFS4_STATEID_SIZE)
|
|
* + array length + deviceid(NFS4_DEVICEID4_SIZE)
|
|
* + status(4) + opnum(4)
|
|
*/
|
|
p = xdr_reserve_space(xdr,
|
|
28 + NFS4_STATEID_SIZE + NFS4_DEVICEID4_SIZE);
|
|
if (unlikely(!p))
|
|
return -ENOBUFS;
|
|
p = xdr_encode_hyper(p, err->offset);
|
|
p = xdr_encode_hyper(p, err->length);
|
|
p = xdr_encode_opaque_fixed(p, &err->stateid,
|
|
NFS4_STATEID_SIZE);
|
|
/* Encode 1 error */
|
|
*p++ = cpu_to_be32(1);
|
|
p = xdr_encode_opaque_fixed(p, &err->deviceid,
|
|
NFS4_DEVICEID4_SIZE);
|
|
*p++ = cpu_to_be32(err->status);
|
|
*p++ = cpu_to_be32(err->opnum);
|
|
dprintk("%s: offset %llu length %llu status %d op %d\n",
|
|
__func__, err->offset, err->length, err->status,
|
|
err->opnum);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
unsigned int do_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
|
|
const struct pnfs_layout_range *range,
|
|
struct list_head *head,
|
|
unsigned int maxnum)
|
|
{
|
|
struct nfs4_flexfile_layout *flo = FF_LAYOUT_FROM_HDR(lo);
|
|
struct inode *inode = lo->plh_inode;
|
|
struct nfs4_ff_layout_ds_err *err, *n;
|
|
unsigned int ret = 0;
|
|
|
|
spin_lock(&inode->i_lock);
|
|
list_for_each_entry_safe(err, n, &flo->error_list, list) {
|
|
if (!pnfs_is_range_intersecting(err->offset,
|
|
pnfs_end_offset(err->offset, err->length),
|
|
range->offset,
|
|
pnfs_end_offset(range->offset, range->length)))
|
|
continue;
|
|
if (!maxnum)
|
|
break;
|
|
list_move(&err->list, head);
|
|
maxnum--;
|
|
ret++;
|
|
}
|
|
spin_unlock(&inode->i_lock);
|
|
return ret;
|
|
}
|
|
|
|
unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
|
|
const struct pnfs_layout_range *range,
|
|
struct list_head *head,
|
|
unsigned int maxnum)
|
|
{
|
|
unsigned int ret;
|
|
|
|
ret = do_layout_fetch_ds_ioerr(lo, range, head, maxnum);
|
|
/* If we're over the max, discard all remaining entries */
|
|
if (ret == maxnum) {
|
|
LIST_HEAD(discard);
|
|
do_layout_fetch_ds_ioerr(lo, range, &discard, -1);
|
|
ff_layout_free_ds_ioerr(&discard);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static bool ff_read_layout_has_available_ds(struct pnfs_layout_segment *lseg)
|
|
{
|
|
struct nfs4_ff_layout_mirror *mirror;
|
|
struct nfs4_deviceid_node *devid;
|
|
u32 idx;
|
|
|
|
for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
|
|
mirror = FF_LAYOUT_COMP(lseg, idx);
|
|
if (mirror) {
|
|
if (!mirror->mirror_ds)
|
|
return true;
|
|
if (IS_ERR(mirror->mirror_ds))
|
|
continue;
|
|
devid = &mirror->mirror_ds->id_node;
|
|
if (!ff_layout_test_devid_unavailable(devid))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool ff_rw_layout_has_available_ds(struct pnfs_layout_segment *lseg)
|
|
{
|
|
struct nfs4_ff_layout_mirror *mirror;
|
|
struct nfs4_deviceid_node *devid;
|
|
u32 idx;
|
|
|
|
for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
|
|
mirror = FF_LAYOUT_COMP(lseg, idx);
|
|
if (!mirror || IS_ERR(mirror->mirror_ds))
|
|
return false;
|
|
if (!mirror->mirror_ds)
|
|
continue;
|
|
devid = &mirror->mirror_ds->id_node;
|
|
if (ff_layout_test_devid_unavailable(devid))
|
|
return false;
|
|
}
|
|
|
|
return FF_LAYOUT_MIRROR_COUNT(lseg) != 0;
|
|
}
|
|
|
|
static bool ff_layout_has_available_ds(struct pnfs_layout_segment *lseg)
|
|
{
|
|
if (lseg->pls_range.iomode == IOMODE_READ)
|
|
return ff_read_layout_has_available_ds(lseg);
|
|
/* Note: RW layout needs all mirrors available */
|
|
return ff_rw_layout_has_available_ds(lseg);
|
|
}
|
|
|
|
bool ff_layout_avoid_mds_available_ds(struct pnfs_layout_segment *lseg)
|
|
{
|
|
return ff_layout_no_fallback_to_mds(lseg) ||
|
|
ff_layout_has_available_ds(lseg);
|
|
}
|
|
|
|
bool ff_layout_avoid_read_on_rw(struct pnfs_layout_segment *lseg)
|
|
{
|
|
return lseg->pls_range.iomode == IOMODE_RW &&
|
|
ff_layout_no_read_on_rw(lseg);
|
|
}
|
|
|
|
module_param(dataserver_retrans, uint, 0644);
|
|
MODULE_PARM_DESC(dataserver_retrans, "The number of times the NFSv4.1 client "
|
|
"retries a request before it attempts further "
|
|
" recovery action.");
|
|
module_param(dataserver_timeo, uint, 0644);
|
|
MODULE_PARM_DESC(dataserver_timeo, "The time (in tenths of a second) the "
|
|
"NFSv4.1 client waits for a response from a "
|
|
" data server before it retries an NFS request.");
|