More NFS Client Bugfixes for Linux 6.18-rc

Bugfixes:
  * Various fixes when using NFS with TLS
  * Localio direct-IO fixes
  * Fix error handling in nfs_atomic_open_v23()
  * Fix sysfs memory leak when nfs_client kobject add fails
  * Fix an incorrect parameter when calling nfs4_call_sync()
  * Fix a failing LTP test when using delegated timestamps
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmkXmqIACgkQ18tUv7Cl
 QOsdlg/8CgomAZvepcW9e5H/bXi0x3Q4gUTcXWV49/oqwfnpVJ1C0Xanx1zdtY6P
 QhchYwHv1r989IEJWaeO4U/ppCei8LoOSq2dkb1TzWohZuUbIg44KnpPtcbkXTgy
 cDcGZu3PNLtvwodljDOtaF4ZVLaWl0MwTYnp0I4xQJc12InHUQ9TPPCSOBb262nN
 BeFPRguhQXZl1jUOv/F0SE/bEcoAnNX8MnbXMaIN3aHy2d9Vq5m4IpJCgyeXQwle
 KvEE6hAmJGm3rG+wpkz7x56KOn7k/K/Py772T7qzEADLcr4Qmusi1++ffnxikVIc
 F9EElTw5pgAvdTuKr+sVw+gktaYWu0LeTFbcIK9dwC5N+uksb7l/TcP9cmhA6lxE
 7KVVmYZGzPV8rwDIztoO8qPSePQTaJB8P3mQmZ8XVd1bz75BQ1zi4SSz578kl84E
 YMHVLHBHiR/chA1z1YSrh8fV8inOiijwrjXU3Oa/blaRhhaH1AvEFboMbn7FyW17
 GjmtL2rfC+hg+kWuvflJiK/zlVJOSNyZBNoD3iQaHq/D4vwXqVETETZnT8002kwL
 sWVFmHdv1Y0fF3K/VsB8QFVhW46K6urJlKU694bWVzo9aK2pX7oj+ElcyXqyFv/p
 Hq1sxe4KPlsVPoG/ClVvtH2a56vNRTOO0LySUN/ZAfUruLQeVv8=
 =7pf4
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-6.18-3' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull NFS client fixes from Anna Schumaker:

 - Various fixes when using NFS with TLS

 - Localio direct-IO fixes

 - Fix error handling in nfs_atomic_open_v23()

 - Fix sysfs memory leak when nfs_client kobject add fails

 - Fix an incorrect parameter when calling nfs4_call_sync()

 - Fix a failing LTP test when using delegated timestamps

* tag 'nfs-for-6.18-3' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  NFS: Fix LTP test failures when timestamps are delegated
  NFSv4: Fix an incorrect parameter when calling nfs4_call_sync()
  NFS: sysfs: fix leak when nfs_client kobject add fails
  NFSv2/v3: Fix error handling in nfs_atomic_open_v23()
  nfs/localio: do not issue misaligned DIO out-of-order
  nfs/localio: Ensure DIO WRITE's IO on stable storage upon completion
  nfs/localio: backfill missing partial read support for misaligned DIO
  nfs/localio: add refcounting for each iocb IO associated with NFS pgio header
  nfs/localio: remove unecessary ENOTBLK handling in DIO WRITE support
  NFS: Check the TLS certificate fields in nfs_match_client()
  pnfs: Set transport security policy to RPC_XPRTSEC_NONE unless using TLS
  pnfs: Fix TLS logic in _nfs4_pnfs_v4_ds_connect()
  pnfs: Fix TLS logic in _nfs4_pnfs_v3_ds_connect()
This commit is contained in:
Linus Torvalds 2025-11-14 13:44:23 -08:00
commit 1cc41c88ef
9 changed files with 211 additions and 155 deletions

View File

@ -338,6 +338,14 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
/* Match the xprt security policy */ /* Match the xprt security policy */
if (clp->cl_xprtsec.policy != data->xprtsec.policy) if (clp->cl_xprtsec.policy != data->xprtsec.policy)
continue; continue;
if (clp->cl_xprtsec.policy == RPC_XPRTSEC_TLS_X509) {
if (clp->cl_xprtsec.cert_serial !=
data->xprtsec.cert_serial)
continue;
if (clp->cl_xprtsec.privkey_serial !=
data->xprtsec.privkey_serial)
continue;
}
refcount_inc(&clp->cl_count); refcount_inc(&clp->cl_count);
return clp; return clp;

View File

@ -2268,11 +2268,12 @@ int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry,
return -ENAMETOOLONG; return -ENAMETOOLONG;
if (open_flags & O_CREAT) { if (open_flags & O_CREAT) {
file->f_mode |= FMODE_CREATED;
error = nfs_do_create(dir, dentry, mode, open_flags); error = nfs_do_create(dir, dentry, mode, open_flags);
if (error) if (!error) {
file->f_mode |= FMODE_CREATED;
return finish_open(file, dentry, NULL);
} else if (error != -EEXIST || open_flags & O_EXCL)
return error; return error;
return finish_open(file, dentry, NULL);
} }
if (d_in_lookup(dentry)) { if (d_in_lookup(dentry)) {
/* The only flags nfs_lookup considers are /* The only flags nfs_lookup considers are

View File

@ -718,6 +718,8 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct nfs_fattr *fattr; struct nfs_fattr *fattr;
loff_t oldsize = i_size_read(inode); loff_t oldsize = i_size_read(inode);
int error = 0; int error = 0;
kuid_t task_uid = current_fsuid();
kuid_t owner_uid = inode->i_uid;
nfs_inc_stats(inode, NFSIOS_VFSSETATTR); nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
@ -739,9 +741,11 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (nfs_have_delegated_mtime(inode) && attr->ia_valid & ATTR_MTIME) { if (nfs_have_delegated_mtime(inode) && attr->ia_valid & ATTR_MTIME) {
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (attr->ia_valid & ATTR_MTIME_SET) { if (attr->ia_valid & ATTR_MTIME_SET) {
nfs_set_timestamps_to_ts(inode, attr); if (uid_eq(task_uid, owner_uid)) {
attr->ia_valid &= ~(ATTR_MTIME|ATTR_MTIME_SET| nfs_set_timestamps_to_ts(inode, attr);
attr->ia_valid &= ~(ATTR_MTIME|ATTR_MTIME_SET|
ATTR_ATIME|ATTR_ATIME_SET); ATTR_ATIME|ATTR_ATIME_SET);
}
} else { } else {
nfs_update_timestamps(inode, attr->ia_valid); nfs_update_timestamps(inode, attr->ia_valid);
attr->ia_valid &= ~(ATTR_MTIME|ATTR_ATIME); attr->ia_valid &= ~(ATTR_MTIME|ATTR_ATIME);
@ -751,10 +755,12 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
attr->ia_valid & ATTR_ATIME && attr->ia_valid & ATTR_ATIME &&
!(attr->ia_valid & ATTR_MTIME)) { !(attr->ia_valid & ATTR_MTIME)) {
if (attr->ia_valid & ATTR_ATIME_SET) { if (attr->ia_valid & ATTR_ATIME_SET) {
spin_lock(&inode->i_lock); if (uid_eq(task_uid, owner_uid)) {
nfs_set_timestamps_to_ts(inode, attr); spin_lock(&inode->i_lock);
spin_unlock(&inode->i_lock); nfs_set_timestamps_to_ts(inode, attr);
attr->ia_valid &= ~(ATTR_ATIME|ATTR_ATIME_SET); spin_unlock(&inode->i_lock);
attr->ia_valid &= ~(ATTR_ATIME|ATTR_ATIME_SET);
}
} else { } else {
nfs_update_delegated_atime(inode); nfs_update_delegated_atime(inode);
attr->ia_valid &= ~ATTR_ATIME; attr->ia_valid &= ~ATTR_ATIME;

View File

@ -42,10 +42,9 @@ struct nfs_local_kiocb {
/* Begin mostly DIO-specific members */ /* Begin mostly DIO-specific members */
size_t end_len; size_t end_len;
short int end_iter_index; short int end_iter_index;
short int n_iters; atomic_t n_iters;
bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS]; bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS];
loff_t offset[NFSLOCAL_MAX_IOS] ____cacheline_aligned; struct iov_iter iters[NFSLOCAL_MAX_IOS] ____cacheline_aligned;
struct iov_iter iters[NFSLOCAL_MAX_IOS];
/* End mostly DIO-specific members */ /* End mostly DIO-specific members */
}; };
@ -314,7 +313,9 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
init_sync_kiocb(&iocb->kiocb, file); init_sync_kiocb(&iocb->kiocb, file);
iocb->hdr = hdr; iocb->hdr = hdr;
iocb->kiocb.ki_pos = hdr->args.offset;
iocb->kiocb.ki_flags &= ~IOCB_APPEND; iocb->kiocb.ki_flags &= ~IOCB_APPEND;
iocb->kiocb.ki_complete = NULL;
iocb->aio_complete_work = NULL; iocb->aio_complete_work = NULL;
iocb->end_iter_index = -1; iocb->end_iter_index = -1;
@ -388,13 +389,24 @@ static bool nfs_iov_iter_aligned_bvec(const struct iov_iter *i,
return true; return true;
} }
static void
nfs_local_iter_setup(struct iov_iter *iter, int rw, struct bio_vec *bvec,
unsigned int nvecs, unsigned long total,
size_t start, size_t len)
{
iov_iter_bvec(iter, rw, bvec, nvecs, total);
if (start)
iov_iter_advance(iter, start);
iov_iter_truncate(iter, len);
}
/* /*
* Setup as many as 3 iov_iter based on extents described by @local_dio. * Setup as many as 3 iov_iter based on extents described by @local_dio.
* Returns the number of iov_iter that were setup. * Returns the number of iov_iter that were setup.
*/ */
static int static int
nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw, nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
unsigned int nvecs, size_t len, unsigned int nvecs, unsigned long total,
struct nfs_local_dio *local_dio) struct nfs_local_dio *local_dio)
{ {
int n_iters = 0; int n_iters = 0;
@ -402,39 +414,17 @@ nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
/* Setup misaligned start? */ /* Setup misaligned start? */
if (local_dio->start_len) { if (local_dio->start_len) {
iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len); nfs_local_iter_setup(&iters[n_iters], rw, iocb->bvec,
iters[n_iters].count = local_dio->start_len; nvecs, total, 0, local_dio->start_len);
iocb->offset[n_iters] = iocb->hdr->args.offset;
iocb->iter_is_dio_aligned[n_iters] = false;
++n_iters; ++n_iters;
} }
/* Setup misaligned end? /*
* If so, the end is purposely setup to be issued using buffered IO * Setup DIO-aligned middle, if there is no misaligned end (below)
* before the middle (which will use DIO, if DIO-aligned, with AIO). * then AIO completion is used, see nfs_local_call_{read,write}
* This creates problems if/when the end results in a partial write.
* So must save index and length of end to handle this corner case.
*/ */
if (local_dio->end_len) { nfs_local_iter_setup(&iters[n_iters], rw, iocb->bvec, nvecs,
iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len); total, local_dio->start_len, local_dio->middle_len);
iocb->offset[n_iters] = local_dio->end_offset;
iov_iter_advance(&iters[n_iters],
local_dio->start_len + local_dio->middle_len);
iocb->iter_is_dio_aligned[n_iters] = false;
/* Save index and length of end */
iocb->end_iter_index = n_iters;
iocb->end_len = local_dio->end_len;
++n_iters;
}
/* Setup DIO-aligned middle to be issued last, to allow for
* DIO with AIO completion (see nfs_local_call_{read,write}).
*/
iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
if (local_dio->start_len)
iov_iter_advance(&iters[n_iters], local_dio->start_len);
iters[n_iters].count -= local_dio->end_len;
iocb->offset[n_iters] = local_dio->middle_offset;
iocb->iter_is_dio_aligned[n_iters] = iocb->iter_is_dio_aligned[n_iters] =
nfs_iov_iter_aligned_bvec(&iters[n_iters], nfs_iov_iter_aligned_bvec(&iters[n_iters],
@ -442,12 +432,22 @@ nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
if (unlikely(!iocb->iter_is_dio_aligned[n_iters])) { if (unlikely(!iocb->iter_is_dio_aligned[n_iters])) {
trace_nfs_local_dio_misaligned(iocb->hdr->inode, trace_nfs_local_dio_misaligned(iocb->hdr->inode,
iocb->hdr->args.offset, len, local_dio); local_dio->start_len, local_dio->middle_len, local_dio);
return 0; /* no DIO-aligned IO possible */ return 0; /* no DIO-aligned IO possible */
} }
iocb->end_iter_index = n_iters;
++n_iters; ++n_iters;
iocb->n_iters = n_iters; /* Setup misaligned end? */
if (local_dio->end_len) {
nfs_local_iter_setup(&iters[n_iters], rw, iocb->bvec,
nvecs, total, local_dio->start_len +
local_dio->middle_len, local_dio->end_len);
iocb->end_iter_index = n_iters;
++n_iters;
}
atomic_set(&iocb->n_iters, n_iters);
return n_iters; return n_iters;
} }
@ -473,18 +473,26 @@ nfs_local_iters_init(struct nfs_local_kiocb *iocb, int rw)
} }
len = hdr->args.count - total; len = hdr->args.count - total;
/*
* For each iocb, iocb->n_iters is always at least 1 and we always
* end io after first nfs_local_pgio_done call unless misaligned DIO.
*/
atomic_set(&iocb->n_iters, 1);
if (test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) { if (test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) {
struct nfs_local_dio local_dio; struct nfs_local_dio local_dio;
if (nfs_is_local_dio_possible(iocb, rw, len, &local_dio) && if (nfs_is_local_dio_possible(iocb, rw, len, &local_dio) &&
nfs_local_iters_setup_dio(iocb, rw, v, len, &local_dio) != 0) nfs_local_iters_setup_dio(iocb, rw, v, len, &local_dio) != 0) {
/* Ensure DIO WRITE's IO on stable storage upon completion */
if (rw == ITER_SOURCE)
iocb->kiocb.ki_flags |= IOCB_DSYNC|IOCB_SYNC;
return; /* is DIO-aligned */ return; /* is DIO-aligned */
}
} }
/* Use buffered IO */ /* Use buffered IO */
iocb->offset[0] = hdr->args.offset;
iov_iter_bvec(&iocb->iters[0], rw, iocb->bvec, v, len); iov_iter_bvec(&iocb->iters[0], rw, iocb->bvec, v, len);
iocb->n_iters = 1;
} }
static void static void
@ -504,9 +512,11 @@ nfs_local_pgio_init(struct nfs_pgio_header *hdr,
hdr->task.tk_start = ktime_get(); hdr->task.tk_start = ktime_get();
} }
static void static bool
nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status) nfs_local_pgio_done(struct nfs_local_kiocb *iocb, long status, bool force)
{ {
struct nfs_pgio_header *hdr = iocb->hdr;
/* Must handle partial completions */ /* Must handle partial completions */
if (status >= 0) { if (status >= 0) {
hdr->res.count += status; hdr->res.count += status;
@ -517,6 +527,12 @@ nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status)
hdr->res.op_status = nfs_localio_errno_to_nfs4_stat(status); hdr->res.op_status = nfs_localio_errno_to_nfs4_stat(status);
hdr->task.tk_status = status; hdr->task.tk_status = status;
} }
if (force)
return true;
BUG_ON(atomic_read(&iocb->n_iters) <= 0);
return atomic_dec_and_test(&iocb->n_iters);
} }
static void static void
@ -547,11 +563,11 @@ static inline void nfs_local_pgio_aio_complete(struct nfs_local_kiocb *iocb)
queue_work(nfsiod_workqueue, &iocb->work); queue_work(nfsiod_workqueue, &iocb->work);
} }
static void static void nfs_local_read_done(struct nfs_local_kiocb *iocb)
nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
{ {
struct nfs_pgio_header *hdr = iocb->hdr; struct nfs_pgio_header *hdr = iocb->hdr;
struct file *filp = iocb->kiocb.ki_filp; struct file *filp = iocb->kiocb.ki_filp;
long status = hdr->task.tk_status;
if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) { if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) {
/* Underlying FS will return -EINVAL if misaligned DIO is attempted. */ /* Underlying FS will return -EINVAL if misaligned DIO is attempted. */
@ -564,20 +580,27 @@ nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
*/ */
hdr->res.replen = 0; hdr->res.replen = 0;
if (hdr->res.count != hdr->args.count || /* nfs_readpage_result() handles short read */
hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
if (hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
hdr->res.eof = true; hdr->res.eof = true;
dprintk("%s: read %ld bytes eof %d.\n", __func__, dprintk("%s: read %ld bytes eof %d.\n", __func__,
status > 0 ? status : 0, hdr->res.eof); status > 0 ? status : 0, hdr->res.eof);
} }
static inline void nfs_local_read_iocb_done(struct nfs_local_kiocb *iocb)
{
nfs_local_read_done(iocb);
nfs_local_pgio_release(iocb);
}
static void nfs_local_read_aio_complete_work(struct work_struct *work) static void nfs_local_read_aio_complete_work(struct work_struct *work)
{ {
struct nfs_local_kiocb *iocb = struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work); container_of(work, struct nfs_local_kiocb, work);
nfs_local_pgio_release(iocb); nfs_local_read_iocb_done(iocb);
} }
static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret) static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret)
@ -585,8 +608,10 @@ static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret)
struct nfs_local_kiocb *iocb = struct nfs_local_kiocb *iocb =
container_of(kiocb, struct nfs_local_kiocb, kiocb); container_of(kiocb, struct nfs_local_kiocb, kiocb);
nfs_local_pgio_done(iocb->hdr, ret); /* AIO completion of DIO read should always be last to complete */
nfs_local_read_done(iocb, ret); if (unlikely(!nfs_local_pgio_done(iocb, ret, false)))
return;
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_read_aio_complete_work */ nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_read_aio_complete_work */
} }
@ -596,32 +621,36 @@ static void nfs_local_call_read(struct work_struct *work)
container_of(work, struct nfs_local_kiocb, work); container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp; struct file *filp = iocb->kiocb.ki_filp;
const struct cred *save_cred; const struct cred *save_cred;
bool force_done = false;
ssize_t status; ssize_t status;
int n_iters;
save_cred = override_creds(filp->f_cred); save_cred = override_creds(filp->f_cred);
for (int i = 0; i < iocb->n_iters ; i++) { n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
if (iocb->iter_is_dio_aligned[i]) { if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT; iocb->kiocb.ki_flags |= IOCB_DIRECT;
iocb->kiocb.ki_complete = nfs_local_read_aio_complete; /* Only use AIO completion if DIO-aligned segment is last */
iocb->aio_complete_work = nfs_local_read_aio_complete_work; if (i == iocb->end_iter_index) {
} iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
iocb->aio_complete_work = nfs_local_read_aio_complete_work;
}
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
iocb->kiocb.ki_pos = iocb->offset[i];
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]); status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
if (status != -EIOCBQUEUED) { if (status != -EIOCBQUEUED) {
nfs_local_pgio_done(iocb->hdr, status); if (unlikely(status >= 0 && status < iocb->iters[i].count))
if (iocb->hdr->task.tk_status) force_done = true; /* Partial read */
if (nfs_local_pgio_done(iocb, status, force_done)) {
nfs_local_read_iocb_done(iocb);
break; break;
}
} }
} }
revert_creds(save_cred); revert_creds(save_cred);
if (status != -EIOCBQUEUED) {
nfs_local_read_done(iocb, status);
nfs_local_pgio_release(iocb);
}
} }
static int static int
@ -736,11 +765,10 @@ static void nfs_local_vfs_getattr(struct nfs_local_kiocb *iocb)
fattr->du.nfs3.used = stat.blocks << 9; fattr->du.nfs3.used = stat.blocks << 9;
} }
static void static void nfs_local_write_done(struct nfs_local_kiocb *iocb)
nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
{ {
struct nfs_pgio_header *hdr = iocb->hdr; struct nfs_pgio_header *hdr = iocb->hdr;
struct inode *inode = hdr->inode; long status = hdr->task.tk_status;
dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0); dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0);
@ -759,10 +787,17 @@ nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset); nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset);
status = -ENOSPC; status = -ENOSPC;
/* record -ENOSPC in terms of nfs_local_pgio_done */ /* record -ENOSPC in terms of nfs_local_pgio_done */
nfs_local_pgio_done(hdr, status); (void) nfs_local_pgio_done(iocb, status, true);
} }
if (hdr->task.tk_status < 0) if (hdr->task.tk_status < 0)
nfs_reset_boot_verifier(inode); nfs_reset_boot_verifier(hdr->inode);
}
static inline void nfs_local_write_iocb_done(struct nfs_local_kiocb *iocb)
{
nfs_local_write_done(iocb);
nfs_local_vfs_getattr(iocb);
nfs_local_pgio_release(iocb);
} }
static void nfs_local_write_aio_complete_work(struct work_struct *work) static void nfs_local_write_aio_complete_work(struct work_struct *work)
@ -770,8 +805,7 @@ static void nfs_local_write_aio_complete_work(struct work_struct *work)
struct nfs_local_kiocb *iocb = struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work); container_of(work, struct nfs_local_kiocb, work);
nfs_local_vfs_getattr(iocb); nfs_local_write_iocb_done(iocb);
nfs_local_pgio_release(iocb);
} }
static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret) static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
@ -779,8 +813,10 @@ static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
struct nfs_local_kiocb *iocb = struct nfs_local_kiocb *iocb =
container_of(kiocb, struct nfs_local_kiocb, kiocb); container_of(kiocb, struct nfs_local_kiocb, kiocb);
nfs_local_pgio_done(iocb->hdr, ret); /* AIO completion of DIO write should always be last to complete */
nfs_local_write_done(iocb, ret); if (unlikely(!nfs_local_pgio_done(iocb, ret, false)))
return;
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */ nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */
} }
@ -791,63 +827,40 @@ static void nfs_local_call_write(struct work_struct *work)
struct file *filp = iocb->kiocb.ki_filp; struct file *filp = iocb->kiocb.ki_filp;
unsigned long old_flags = current->flags; unsigned long old_flags = current->flags;
const struct cred *save_cred; const struct cred *save_cred;
bool force_done = false;
ssize_t status; ssize_t status;
int n_iters;
current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO; current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
save_cred = override_creds(filp->f_cred); save_cred = override_creds(filp->f_cred);
file_start_write(filp); file_start_write(filp);
for (int i = 0; i < iocb->n_iters ; i++) { n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
if (iocb->iter_is_dio_aligned[i]) { if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT; iocb->kiocb.ki_flags |= IOCB_DIRECT;
iocb->kiocb.ki_complete = nfs_local_write_aio_complete; /* Only use AIO completion if DIO-aligned segment is last */
iocb->aio_complete_work = nfs_local_write_aio_complete_work; if (i == iocb->end_iter_index) {
} iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
retry: iocb->aio_complete_work = nfs_local_write_aio_complete_work;
iocb->kiocb.ki_pos = iocb->offset[i]; }
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]); status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
if (status != -EIOCBQUEUED) { if (status != -EIOCBQUEUED) {
if (unlikely(status >= 0 && status < iocb->iters[i].count)) { if (unlikely(status >= 0 && status < iocb->iters[i].count))
/* partial write */ force_done = true; /* Partial write */
if (i == iocb->end_iter_index) { if (nfs_local_pgio_done(iocb, status, force_done)) {
/* Must not account partial end, otherwise, due nfs_local_write_iocb_done(iocb);
* to end being issued before middle: the partial
* write accounting in nfs_local_write_done()
* would incorrectly advance hdr->args.offset
*/
status = 0;
} else {
/* Partial write at start or buffered middle,
* exit early.
*/
nfs_local_pgio_done(iocb->hdr, status);
break;
}
} else if (unlikely(status == -ENOTBLK &&
(iocb->kiocb.ki_flags & IOCB_DIRECT))) {
/* VFS will return -ENOTBLK if DIO WRITE fails to
* invalidate the page cache. Retry using buffered IO.
*/
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
iocb->kiocb.ki_complete = NULL;
iocb->aio_complete_work = NULL;
goto retry;
}
nfs_local_pgio_done(iocb->hdr, status);
if (iocb->hdr->task.tk_status)
break; break;
}
} }
} }
file_end_write(filp); file_end_write(filp);
revert_creds(save_cred); revert_creds(save_cred);
current->flags = old_flags; current->flags = old_flags;
if (status != -EIOCBQUEUED) {
nfs_local_write_done(iocb, status);
nfs_local_vfs_getattr(iocb);
nfs_local_pgio_release(iocb);
}
} }
static int static int

View File

@ -2,6 +2,7 @@
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h> #include <linux/nfs_mount.h>
#include <linux/sunrpc/addr.h> #include <linux/sunrpc/addr.h>
#include <net/handshake.h>
#include "internal.h" #include "internal.h"
#include "nfs3_fs.h" #include "nfs3_fs.h"
#include "netns.h" #include "netns.h"
@ -98,7 +99,11 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
.net = mds_clp->cl_net, .net = mds_clp->cl_net,
.timeparms = &ds_timeout, .timeparms = &ds_timeout,
.cred = mds_srv->cred, .cred = mds_srv->cred,
.xprtsec = mds_clp->cl_xprtsec, .xprtsec = {
.policy = RPC_XPRTSEC_NONE,
.cert_serial = TLS_NO_CERT,
.privkey_serial = TLS_NO_PRIVKEY,
},
.connect_timeout = connect_timeout, .connect_timeout = connect_timeout,
.reconnect_timeout = connect_timeout, .reconnect_timeout = connect_timeout,
}; };
@ -111,9 +116,14 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
cl_init.hostname = buf; cl_init.hostname = buf;
switch (ds_proto) { switch (ds_proto) {
case XPRT_TRANSPORT_TCP_TLS:
if (mds_clp->cl_xprtsec.policy != RPC_XPRTSEC_NONE)
cl_init.xprtsec = mds_clp->cl_xprtsec;
else
ds_proto = XPRT_TRANSPORT_TCP;
fallthrough;
case XPRT_TRANSPORT_RDMA: case XPRT_TRANSPORT_RDMA:
case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_TCP:
case XPRT_TRANSPORT_TCP_TLS:
if (mds_clp->cl_nconnect > 1) if (mds_clp->cl_nconnect > 1)
cl_init.nconnect = mds_clp->cl_nconnect; cl_init.nconnect = mds_clp->cl_nconnect;
} }

View File

@ -11,6 +11,7 @@
#include <linux/sunrpc/xprt.h> #include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/bc_xprt.h> #include <linux/sunrpc/bc_xprt.h>
#include <linux/sunrpc/rpc_pipe_fs.h> #include <linux/sunrpc/rpc_pipe_fs.h>
#include <net/handshake.h>
#include "internal.h" #include "internal.h"
#include "callback.h" #include "callback.h"
#include "delegation.h" #include "delegation.h"
@ -983,7 +984,11 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
.net = mds_clp->cl_net, .net = mds_clp->cl_net,
.timeparms = &ds_timeout, .timeparms = &ds_timeout,
.cred = mds_srv->cred, .cred = mds_srv->cred,
.xprtsec = mds_srv->nfs_client->cl_xprtsec, .xprtsec = {
.policy = RPC_XPRTSEC_NONE,
.cert_serial = TLS_NO_CERT,
.privkey_serial = TLS_NO_PRIVKEY,
},
}; };
char buf[INET6_ADDRSTRLEN + 1]; char buf[INET6_ADDRSTRLEN + 1];
@ -992,9 +997,14 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
cl_init.hostname = buf; cl_init.hostname = buf;
switch (ds_proto) { switch (ds_proto) {
case XPRT_TRANSPORT_TCP_TLS:
if (mds_srv->nfs_client->cl_xprtsec.policy != RPC_XPRTSEC_NONE)
cl_init.xprtsec = mds_srv->nfs_client->cl_xprtsec;
else
ds_proto = XPRT_TRANSPORT_TCP;
fallthrough;
case XPRT_TRANSPORT_RDMA: case XPRT_TRANSPORT_RDMA:
case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_TCP:
case XPRT_TRANSPORT_TCP_TLS:
if (mds_clp->cl_nconnect > 1) { if (mds_clp->cl_nconnect > 1) {
cl_init.nconnect = mds_clp->cl_nconnect; cl_init.nconnect = mds_clp->cl_nconnect;
cl_init.max_connect = NFS_MAX_TRANSPORTS; cl_init.max_connect = NFS_MAX_TRANSPORTS;

View File

@ -4715,16 +4715,19 @@ static int _nfs4_proc_lookupp(struct inode *inode,
}; };
unsigned short task_flags = 0; unsigned short task_flags = 0;
if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL) if (server->flags & NFS_MOUNT_SOFTREVAL)
task_flags |= RPC_TASK_TIMEOUT; task_flags |= RPC_TASK_TIMEOUT;
if (server->caps & NFS_CAP_MOVEABLE)
task_flags |= RPC_TASK_MOVEABLE;
args.bitmask = nfs4_bitmask(server, fattr->label); args.bitmask = nfs4_bitmask(server, fattr->label);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
dprintk("NFS call lookupp ino=0x%lx\n", inode->i_ino); dprintk("NFS call lookupp ino=0x%lx\n", inode->i_ino);
status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, status = nfs4_do_call_sync(clnt, server, &msg, &args.seq_args,
&res.seq_res, task_flags); &res.seq_res, task_flags);
dprintk("NFS reply lookupp: %d\n", status); dprintk("NFS reply lookupp: %d\n", status);
return status; return status;
} }

View File

@ -809,8 +809,11 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
unsigned int retrans) unsigned int retrans)
{ {
struct nfs_client *clp = ERR_PTR(-EIO); struct nfs_client *clp = ERR_PTR(-EIO);
struct nfs_client *mds_clp = mds_srv->nfs_client;
enum xprtsec_policies xprtsec_policy = mds_clp->cl_xprtsec.policy;
struct nfs4_pnfs_ds_addr *da; struct nfs4_pnfs_ds_addr *da;
unsigned long connect_timeout = timeo * (retrans + 1) * HZ / 10; unsigned long connect_timeout = timeo * (retrans + 1) * HZ / 10;
int ds_proto;
int status = 0; int status = 0;
dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr); dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
@ -834,27 +837,28 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
.xprtsec = clp->cl_xprtsec, .xprtsec = clp->cl_xprtsec,
}; };
if (da->da_transport != clp->cl_proto && if (xprt_args.ident == XPRT_TRANSPORT_TCP &&
clp->cl_proto != XPRT_TRANSPORT_TCP_TLS) clp->cl_proto == XPRT_TRANSPORT_TCP_TLS)
continue;
if (da->da_transport == XPRT_TRANSPORT_TCP &&
mds_srv->nfs_client->cl_proto == XPRT_TRANSPORT_TCP_TLS)
xprt_args.ident = XPRT_TRANSPORT_TCP_TLS; xprt_args.ident = XPRT_TRANSPORT_TCP_TLS;
if (da->da_addr.ss_family != clp->cl_addr.ss_family) if (xprt_args.ident != clp->cl_proto)
continue;
if (xprt_args.dstaddr->sa_family !=
clp->cl_addr.ss_family)
continue; continue;
/* Add this address as an alias */ /* Add this address as an alias */
rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args, rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
rpc_clnt_test_and_add_xprt, NULL); rpc_clnt_test_and_add_xprt, NULL);
continue; continue;
} }
if (da->da_transport == XPRT_TRANSPORT_TCP &&
mds_srv->nfs_client->cl_proto == XPRT_TRANSPORT_TCP_TLS) ds_proto = da->da_transport;
da->da_transport = XPRT_TRANSPORT_TCP_TLS; if (ds_proto == XPRT_TRANSPORT_TCP &&
clp = get_v3_ds_connect(mds_srv, xprtsec_policy != RPC_XPRTSEC_NONE)
&da->da_addr, ds_proto = XPRT_TRANSPORT_TCP_TLS;
da->da_addrlen, da->da_transport,
timeo, retrans); clp = get_v3_ds_connect(mds_srv, &da->da_addr, da->da_addrlen,
ds_proto, timeo, retrans);
if (IS_ERR(clp)) if (IS_ERR(clp))
continue; continue;
clp->cl_rpcclient->cl_softerr = 0; clp->cl_rpcclient->cl_softerr = 0;
@ -880,7 +884,10 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
u32 minor_version) u32 minor_version)
{ {
struct nfs_client *clp = ERR_PTR(-EIO); struct nfs_client *clp = ERR_PTR(-EIO);
struct nfs_client *mds_clp = mds_srv->nfs_client;
enum xprtsec_policies xprtsec_policy = mds_clp->cl_xprtsec.policy;
struct nfs4_pnfs_ds_addr *da; struct nfs4_pnfs_ds_addr *da;
int ds_proto;
int status = 0; int status = 0;
dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr); dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
@ -908,12 +915,8 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
.data = &xprtdata, .data = &xprtdata,
}; };
if (da->da_transport != clp->cl_proto && if (xprt_args.ident == XPRT_TRANSPORT_TCP &&
clp->cl_proto != XPRT_TRANSPORT_TCP_TLS) clp->cl_proto == XPRT_TRANSPORT_TCP_TLS) {
continue;
if (da->da_transport == XPRT_TRANSPORT_TCP &&
mds_srv->nfs_client->cl_proto ==
XPRT_TRANSPORT_TCP_TLS) {
struct sockaddr *addr = struct sockaddr *addr =
(struct sockaddr *)&da->da_addr; (struct sockaddr *)&da->da_addr;
struct sockaddr_in *sin = struct sockaddr_in *sin =
@ -944,7 +947,10 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
xprt_args.ident = XPRT_TRANSPORT_TCP_TLS; xprt_args.ident = XPRT_TRANSPORT_TCP_TLS;
xprt_args.servername = servername; xprt_args.servername = servername;
} }
if (da->da_addr.ss_family != clp->cl_addr.ss_family) if (xprt_args.ident != clp->cl_proto)
continue;
if (xprt_args.dstaddr->sa_family !=
clp->cl_addr.ss_family)
continue; continue;
/** /**
@ -958,15 +964,14 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
if (xprtdata.cred) if (xprtdata.cred)
put_cred(xprtdata.cred); put_cred(xprtdata.cred);
} else { } else {
if (da->da_transport == XPRT_TRANSPORT_TCP && ds_proto = da->da_transport;
mds_srv->nfs_client->cl_proto == if (ds_proto == XPRT_TRANSPORT_TCP &&
XPRT_TRANSPORT_TCP_TLS) xprtsec_policy != RPC_XPRTSEC_NONE)
da->da_transport = XPRT_TRANSPORT_TCP_TLS; ds_proto = XPRT_TRANSPORT_TCP_TLS;
clp = nfs4_set_ds_client(mds_srv,
&da->da_addr, clp = nfs4_set_ds_client(mds_srv, &da->da_addr,
da->da_addrlen, da->da_addrlen, ds_proto,
da->da_transport, timeo, timeo, retrans, minor_version);
retrans, minor_version);
if (IS_ERR(clp)) if (IS_ERR(clp))
continue; continue;
@ -977,7 +982,6 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
clp = ERR_PTR(-EIO); clp = ERR_PTR(-EIO);
continue; continue;
} }
} }
} }

View File

@ -189,6 +189,7 @@ static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
return p; return p;
kobject_put(&p->kobject); kobject_put(&p->kobject);
kobject_put(&p->nfs_net_kobj);
} }
return NULL; return NULL;
} }