1// SPDX-License-Identifier: GPL-2.0-or-later
2/* Direct I/O support.
3 *
4 * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
6 */
7
8#include <linux/export.h>
9#include <linux/fs.h>
10#include <linux/mm.h>
11#include <linux/pagemap.h>
12#include <linux/slab.h>
13#include <linux/uio.h>
14#include <linux/sched/mm.h>
15#include <linux/task_io_accounting_ops.h>
16#include <linux/netfs.h>
17#include "internal.h"
18
19/**
20 * netfs_unbuffered_read_iter_locked - Perform an unbuffered or direct I/O read
21 * @iocb: The I/O control descriptor describing the read
22 * @iter: The output buffer (also specifies read length)
23 *
24 * Perform an unbuffered I/O or direct I/O from the file in @iocb to the
25 * output buffer. No use is made of the pagecache.
26 *
27 * The caller must hold any appropriate locks.
28 */
29static ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *iter)
30{
31 struct netfs_io_request *rreq;
32 ssize_t ret;
33 size_t orig_count = iov_iter_count(i: iter);
34 bool async = !is_sync_kiocb(kiocb: iocb);
35
36 _enter("");
37
38 if (!orig_count)
39 return 0; /* Don't update atime */
40
41 ret = kiocb_write_and_wait(iocb, count: orig_count);
42 if (ret < 0)
43 return ret;
44 file_accessed(file: iocb->ki_filp);
45
46 rreq = netfs_alloc_request(mapping: iocb->ki_filp->f_mapping, file: iocb->ki_filp,
47 start: iocb->ki_pos, len: orig_count,
48 origin: NETFS_DIO_READ);
49 if (IS_ERR(ptr: rreq))
50 return PTR_ERR(ptr: rreq);
51
52 netfs_stat(stat: &netfs_n_rh_dio_read);
53 trace_netfs_read(rreq, start: rreq->start, len: rreq->len, what: netfs_read_trace_dio_read);
54
55 /* If this is an async op, we have to keep track of the destination
56 * buffer for ourselves as the caller's iterator will be trashed when
57 * we return.
58 *
59 * In such a case, extract an iterator to represent as much of the the
60 * output buffer as we can manage. Note that the extraction might not
61 * be able to allocate a sufficiently large bvec array and may shorten
62 * the request.
63 */
64 if (user_backed_iter(i: iter)) {
65 ret = netfs_extract_user_iter(orig: iter, orig_len: rreq->len, new: &rreq->iter, extraction_flags: 0);
66 if (ret < 0)
67 goto out;
68 rreq->direct_bv = (struct bio_vec *)rreq->iter.bvec;
69 rreq->direct_bv_count = ret;
70 rreq->direct_bv_unpin = iov_iter_extract_will_pin(iter);
71 rreq->len = iov_iter_count(i: &rreq->iter);
72 } else {
73 rreq->iter = *iter;
74 rreq->len = orig_count;
75 rreq->direct_bv_unpin = false;
76 iov_iter_advance(i: iter, bytes: orig_count);
77 }
78
79 // TODO: Set up bounce buffer if needed
80
81 if (async)
82 rreq->iocb = iocb;
83
84 ret = netfs_begin_read(rreq, sync: is_sync_kiocb(kiocb: iocb));
85 if (ret < 0)
86 goto out; /* May be -EIOCBQUEUED */
87 if (!async) {
88 // TODO: Copy from bounce buffer
89 iocb->ki_pos += rreq->transferred;
90 ret = rreq->transferred;
91 }
92
93out:
94 netfs_put_request(rreq, was_async: false, what: netfs_rreq_trace_put_return);
95 if (ret > 0)
96 orig_count -= ret;
97 if (ret != -EIOCBQUEUED)
98 iov_iter_revert(i: iter, bytes: orig_count - iov_iter_count(i: iter));
99 return ret;
100}
101
102/**
103 * netfs_unbuffered_read_iter - Perform an unbuffered or direct I/O read
104 * @iocb: The I/O control descriptor describing the read
105 * @iter: The output buffer (also specifies read length)
106 *
107 * Perform an unbuffered I/O or direct I/O from the file in @iocb to the
108 * output buffer. No use is made of the pagecache.
109 */
110ssize_t netfs_unbuffered_read_iter(struct kiocb *iocb, struct iov_iter *iter)
111{
112 struct inode *inode = file_inode(f: iocb->ki_filp);
113 ssize_t ret;
114
115 if (!iter->count)
116 return 0; /* Don't update atime */
117
118 ret = netfs_start_io_direct(inode);
119 if (ret == 0) {
120 ret = netfs_unbuffered_read_iter_locked(iocb, iter);
121 netfs_end_io_direct(inode);
122 }
123 return ret;
124}
125EXPORT_SYMBOL(netfs_unbuffered_read_iter);
126

source code of linux/fs/netfs/direct_read.c