Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfs/localio: avoid issuing misaligned IO using O_DIRECT

Add nfsd_file_dio_alignment and use it to avoid issuing misaligned IO
using O_DIRECT. Any misaligned DIO falls back to using buffered IO.

Because misaligned DIO is now handled safely, remove the nfs modparam
'localio_O_DIRECT_semantics' that was added to require users opt-in to
the requirement that all O_DIRECT be properly DIO-aligned.

Also, introduce nfs_iov_iter_aligned_bvec() which is a variant of
iov_iter_aligned_bvec() that also verifies the offset associated with
an iov_iter is DIO-aligned. NOTE: in a parallel effort,
iov_iter_aligned_bvec() is being removed along with
iov_iter_is_aligned().

Lastly, add pr_info_ratelimited if underlying filesystem returns
-EINVAL because it was made to try O_DIRECT for IO that is not
DIO-aligned (shouldn't happen, so its best to be louder if it does).

Fixes: 3feec68563d ("nfs/localio: add direct IO enablement with sync and async IO support")
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>

authored by

Mike Snitzer and committed by
Anna Schumaker
25ba2b84 fd6d93c2

+68 -10
+55 -10
fs/nfs/localio.c
··· 49 49 static bool localio_enabled __read_mostly = true; 50 50 module_param(localio_enabled, bool, 0644); 51 51 52 - static bool localio_O_DIRECT_semantics __read_mostly = false; 53 - module_param(localio_O_DIRECT_semantics, bool, 0644); 54 - MODULE_PARM_DESC(localio_O_DIRECT_semantics, 55 - "LOCALIO will use O_DIRECT semantics to filesystem."); 56 - 57 52 static inline bool nfs_client_is_local(const struct nfs_client *clp) 58 53 { 59 54 return !!rcu_access_pointer(clp->cl_uuid.net); ··· 317 322 return NULL; 318 323 } 319 324 320 - if (localio_O_DIRECT_semantics && 321 - test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) { 322 - iocb->kiocb.ki_filp = file; 325 + init_sync_kiocb(&iocb->kiocb, file); 326 + if (test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) 323 327 iocb->kiocb.ki_flags = IOCB_DIRECT; 324 - } else 325 - init_sync_kiocb(&iocb->kiocb, file); 326 328 327 329 iocb->kiocb.ki_pos = hdr->args.offset; 328 330 iocb->hdr = hdr; ··· 327 335 iocb->aio_complete_work = NULL; 328 336 329 337 return iocb; 338 + } 339 + 340 + static bool nfs_iov_iter_aligned_bvec(const struct iov_iter *i, 341 + loff_t offset, unsigned int addr_mask, unsigned int len_mask) 342 + { 343 + const struct bio_vec *bvec = i->bvec; 344 + size_t skip = i->iov_offset; 345 + size_t size = i->count; 346 + 347 + if ((offset | size) & len_mask) 348 + return false; 349 + do { 350 + size_t len = bvec->bv_len; 351 + 352 + if (len > size) 353 + len = size; 354 + if ((unsigned long)(bvec->bv_offset + skip) & addr_mask) 355 + return false; 356 + bvec++; 357 + size -= len; 358 + skip = 0; 359 + } while (size); 360 + 361 + return true; 330 362 } 331 363 332 364 static void ··· 362 346 hdr->args.count + hdr->args.pgbase); 363 347 if (hdr->args.pgbase != 0) 364 348 iov_iter_advance(i, hdr->args.pgbase); 349 + 350 + if (iocb->kiocb.ki_flags & IOCB_DIRECT) { 351 + u32 nf_dio_mem_align, nf_dio_offset_align, nf_dio_read_offset_align; 352 + /* Verify the IO is DIO-aligned as required */ 353 + nfs_to->nfsd_file_dio_alignment(iocb->localio, &nf_dio_mem_align, 354 + &nf_dio_offset_align, 355 + &nf_dio_read_offset_align); 356 + if (dir == READ) 357 + nf_dio_offset_align = nf_dio_read_offset_align; 358 + 359 + if (nf_dio_mem_align && nf_dio_offset_align && 360 + nfs_iov_iter_aligned_bvec(i, hdr->args.offset, 361 + nf_dio_mem_align - 1, 362 + nf_dio_offset_align - 1)) 363 + return; /* is DIO-aligned */ 364 + 365 + /* Fallback to using buffered for this misaligned IO */ 366 + iocb->kiocb.ki_flags &= ~IOCB_DIRECT; 367 + } 365 368 } 366 369 367 370 static void ··· 440 405 { 441 406 struct nfs_pgio_header *hdr = iocb->hdr; 442 407 struct file *filp = iocb->kiocb.ki_filp; 408 + 409 + if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) { 410 + /* Underlying FS will return -EINVAL if misaligned DIO is attempted. */ 411 + pr_info_ratelimited("nfs: Unexpected direct I/O read alignment failure\n"); 412 + } 443 413 444 414 nfs_local_pgio_done(hdr, status); 445 415 ··· 637 597 struct inode *inode = hdr->inode; 638 598 639 599 dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0); 600 + 601 + if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) { 602 + /* Underlying FS will return -EINVAL if misaligned DIO is attempted. */ 603 + pr_info_ratelimited("nfs: Unexpected direct I/O write alignment failure\n"); 604 + } 640 605 641 606 /* Handle short writes as if they are ENOSPC */ 642 607 if (status > 0 && status < hdr->args.count) {
+11
fs/nfsd/localio.c
··· 117 117 return localio; 118 118 } 119 119 120 + static void nfsd_file_dio_alignment(struct nfsd_file *nf, 121 + u32 *nf_dio_mem_align, 122 + u32 *nf_dio_offset_align, 123 + u32 *nf_dio_read_offset_align) 124 + { 125 + *nf_dio_mem_align = nf->nf_dio_mem_align; 126 + *nf_dio_offset_align = nf->nf_dio_offset_align; 127 + *nf_dio_read_offset_align = nf->nf_dio_read_offset_align; 128 + } 129 + 120 130 static const struct nfsd_localio_operations nfsd_localio_ops = { 121 131 .nfsd_net_try_get = nfsd_net_try_get, 122 132 .nfsd_net_put = nfsd_net_put, ··· 134 124 .nfsd_file_put_local = nfsd_file_put_local, 135 125 .nfsd_file_get_local = nfsd_file_get_local, 136 126 .nfsd_file_file = nfsd_file_file, 127 + .nfsd_file_dio_alignment = nfsd_file_dio_alignment, 137 128 }; 138 129 139 130 void nfsd_localio_ops_init(void)
+2
include/linux/nfslocalio.h
··· 65 65 struct net *(*nfsd_file_put_local)(struct nfsd_file __rcu **); 66 66 struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *); 67 67 struct file *(*nfsd_file_file)(struct nfsd_file *); 68 + void (*nfsd_file_dio_alignment)(struct nfsd_file *, 69 + u32 *, u32 *, u32 *); 68 70 } ____cacheline_aligned; 69 71 70 72 extern void nfsd_localio_ops_init(void);