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 | */ |
29 | static 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 | |
93 | out: |
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 | */ |
110 | ssize_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 | } |
125 | EXPORT_SYMBOL(netfs_unbuffered_read_iter); |
126 | |