Skip to content

Commit 7b2c99d

Browse files
author
Al Viro
committed
new helper: iov_iter_get_pages()
iov_iter_get_pages(iter, pages, maxsize, &start) grabs references pinning the pages of up to maxsize of (contiguous) data from iter. Returns the amount of memory grabbed or -error. In case of success, the requested area begins at offset start in pages[0] and runs through pages[1], etc. Less than requested amount might be returned - either because the contiguous area in the beginning of iterator is smaller than requested, or because the kernel failed to pin that many pages. direct-io.c switched to using iov_iter_get_pages() Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 3320c60 commit 7b2c99d

File tree

3 files changed

+67
-73
lines changed

3 files changed

+67
-73
lines changed

fs/direct-io.c

Lines changed: 38 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ struct dio_submit {
7777
unsigned blocks_available; /* At block_in_file. changes */
7878
int reap_counter; /* rate limit reaping */
7979
sector_t final_block_in_request;/* doesn't change */
80-
unsigned first_block_in_page; /* doesn't change, Used only once */
8180
int boundary; /* prev block is at a boundary */
8281
get_block_t *get_block; /* block mapping function */
8382
dio_submit_t *submit_io; /* IO submition function */
@@ -98,19 +97,14 @@ struct dio_submit {
9897
sector_t cur_page_block; /* Where it starts */
9998
loff_t cur_page_fs_offset; /* Offset in file */
10099

101-
/*
102-
* Page fetching state. These variables belong to dio_refill_pages().
103-
*/
104-
int curr_page; /* changes */
105-
int total_pages; /* doesn't change */
106-
unsigned long curr_user_address;/* changes */
107-
100+
struct iov_iter *iter;
108101
/*
109102
* Page queue. These variables belong to dio_refill_pages() and
110103
* dio_get_page().
111104
*/
112105
unsigned head; /* next page to process */
113106
unsigned tail; /* last valid page + 1 */
107+
size_t from, to;
114108
};
115109

116110
/* dio_state communicated between submission path and end_io */
@@ -163,15 +157,10 @@ static inline unsigned dio_pages_present(struct dio_submit *sdio)
163157
*/
164158
static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio)
165159
{
166-
int ret;
167-
int nr_pages;
160+
ssize_t ret;
168161

169-
nr_pages = min(sdio->total_pages - sdio->curr_page, DIO_PAGES);
170-
ret = get_user_pages_fast(
171-
sdio->curr_user_address, /* Where from? */
172-
nr_pages, /* How many pages? */
173-
dio->rw == READ, /* Write to memory? */
174-
&dio->pages[0]); /* Put results here */
162+
ret = iov_iter_get_pages(sdio->iter, dio->pages, DIO_PAGES * PAGE_SIZE,
163+
&sdio->from);
175164

176165
if (ret < 0 && sdio->blocks_available && (dio->rw & WRITE)) {
177166
struct page *page = ZERO_PAGE(0);
@@ -186,18 +175,19 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio)
186175
dio->pages[0] = page;
187176
sdio->head = 0;
188177
sdio->tail = 1;
189-
ret = 0;
190-
goto out;
178+
sdio->from = 0;
179+
sdio->to = PAGE_SIZE;
180+
return 0;
191181
}
192182

193183
if (ret >= 0) {
194-
sdio->curr_user_address += ret * PAGE_SIZE;
195-
sdio->curr_page += ret;
184+
iov_iter_advance(sdio->iter, ret);
185+
ret += sdio->from;
196186
sdio->head = 0;
197-
sdio->tail = ret;
198-
ret = 0;
187+
sdio->tail = (ret + PAGE_SIZE - 1) / PAGE_SIZE;
188+
sdio->to = ((ret - 1) & (PAGE_SIZE - 1)) + 1;
189+
return 0;
199190
}
200-
out:
201191
return ret;
202192
}
203193

@@ -208,8 +198,9 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio)
208198
* L1 cache.
209199
*/
210200
static inline struct page *dio_get_page(struct dio *dio,
211-
struct dio_submit *sdio)
201+
struct dio_submit *sdio, size_t *from, size_t *to)
212202
{
203+
int n;
213204
if (dio_pages_present(sdio) == 0) {
214205
int ret;
215206

@@ -218,7 +209,10 @@ static inline struct page *dio_get_page(struct dio *dio,
218209
return ERR_PTR(ret);
219210
BUG_ON(dio_pages_present(sdio) == 0);
220211
}
221-
return dio->pages[sdio->head++];
212+
n = sdio->head++;
213+
*from = n ? 0 : sdio->from;
214+
*to = (n == sdio->tail - 1) ? sdio->to : PAGE_SIZE;
215+
return dio->pages[n];
222216
}
223217

224218
/**
@@ -422,8 +416,8 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio)
422416
*/
423417
static inline void dio_cleanup(struct dio *dio, struct dio_submit *sdio)
424418
{
425-
while (dio_pages_present(sdio))
426-
page_cache_release(dio_get_page(dio, sdio));
419+
while (sdio->head < sdio->tail)
420+
page_cache_release(dio->pages[sdio->head++]);
427421
}
428422

429423
/*
@@ -912,23 +906,18 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
912906
struct buffer_head *map_bh)
913907
{
914908
const unsigned blkbits = sdio->blkbits;
915-
const unsigned blocks_per_page = PAGE_SIZE >> blkbits;
916-
struct page *page;
917-
unsigned block_in_page;
918909
int ret = 0;
919910

920-
/* The I/O can start at any block offset within the first page */
921-
block_in_page = sdio->first_block_in_page;
922-
923911
while (sdio->block_in_file < sdio->final_block_in_request) {
924-
page = dio_get_page(dio, sdio);
912+
struct page *page;
913+
size_t from, to;
914+
page = dio_get_page(dio, sdio, &from, &to);
925915
if (IS_ERR(page)) {
926916
ret = PTR_ERR(page);
927917
goto out;
928918
}
929919

930-
while (block_in_page < blocks_per_page) {
931-
unsigned offset_in_page = block_in_page << blkbits;
920+
while (from < to) {
932921
unsigned this_chunk_bytes; /* # of bytes mapped */
933922
unsigned this_chunk_blocks; /* # of blocks */
934923
unsigned u;
@@ -999,10 +988,9 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
999988
page_cache_release(page);
1000989
goto out;
1001990
}
1002-
zero_user(page, block_in_page << blkbits,
1003-
1 << blkbits);
991+
zero_user(page, from, 1 << blkbits);
1004992
sdio->block_in_file++;
1005-
block_in_page++;
993+
from += 1 << blkbits;
1006994
dio->result += 1 << blkbits;
1007995
goto next_block;
1008996
}
@@ -1020,7 +1008,7 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
10201008
* can add to this page
10211009
*/
10221010
this_chunk_blocks = sdio->blocks_available;
1023-
u = (PAGE_SIZE - offset_in_page) >> blkbits;
1011+
u = (to - from) >> blkbits;
10241012
if (this_chunk_blocks > u)
10251013
this_chunk_blocks = u;
10261014
u = sdio->final_block_in_request - sdio->block_in_file;
@@ -1032,7 +1020,7 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
10321020
if (this_chunk_blocks == sdio->blocks_available)
10331021
sdio->boundary = buffer_boundary(map_bh);
10341022
ret = submit_page_section(dio, sdio, page,
1035-
offset_in_page,
1023+
from,
10361024
this_chunk_bytes,
10371025
sdio->next_block_for_io,
10381026
map_bh);
@@ -1043,9 +1031,9 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
10431031
sdio->next_block_for_io += this_chunk_blocks;
10441032

10451033
sdio->block_in_file += this_chunk_blocks;
1046-
block_in_page += this_chunk_blocks;
1034+
from += this_chunk_bytes;
1035+
dio->result += this_chunk_bytes;
10471036
sdio->blocks_available -= this_chunk_blocks;
1048-
dio->result += this_chunk_blocks << blkbits;
10491037
next_block:
10501038
BUG_ON(sdio->block_in_file > sdio->final_block_in_request);
10511039
if (sdio->block_in_file == sdio->final_block_in_request)
@@ -1054,7 +1042,6 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
10541042

10551043
/* Drop the ref which was taken in get_user_pages() */
10561044
page_cache_release(page);
1057-
block_in_page = 0;
10581045
}
10591046
out:
10601047
return ret;
@@ -1122,7 +1109,6 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
11221109
struct dio *dio;
11231110
struct dio_submit sdio = { 0, };
11241111
unsigned long user_addr;
1125-
size_t bytes;
11261112
struct buffer_head map_bh = { 0, };
11271113
struct blk_plug plug;
11281114
unsigned long align = offset | iov_iter_alignment(iter);
@@ -1234,6 +1220,10 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
12341220
spin_lock_init(&dio->bio_lock);
12351221
dio->refcount = 1;
12361222

1223+
sdio.iter = iter;
1224+
sdio.final_block_in_request =
1225+
(offset + iov_iter_count(iter)) >> blkbits;
1226+
12371227
/*
12381228
* In case of non-aligned buffers, we may need 2 more
12391229
* pages since we need to zero out first and last block.
@@ -1250,34 +1240,9 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
12501240

12511241
blk_start_plug(&plug);
12521242

1253-
for (seg = 0; seg < iter->nr_segs; seg++) {
1254-
user_addr = (unsigned long)iter->iov[seg].iov_base;
1255-
sdio.size += bytes = iter->iov[seg].iov_len;
1256-
1257-
/* Index into the first page of the first block */
1258-
sdio.first_block_in_page = (user_addr & ~PAGE_MASK) >> blkbits;
1259-
sdio.final_block_in_request = sdio.block_in_file +
1260-
(bytes >> blkbits);
1261-
/* Page fetching state */
1262-
sdio.head = 0;
1263-
sdio.tail = 0;
1264-
sdio.curr_page = 0;
1265-
1266-
sdio.total_pages = 0;
1267-
if (user_addr & (PAGE_SIZE-1)) {
1268-
sdio.total_pages++;
1269-
bytes -= PAGE_SIZE - (user_addr & (PAGE_SIZE - 1));
1270-
}
1271-
sdio.total_pages += (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
1272-
sdio.curr_user_address = user_addr;
1273-
1274-
retval = do_direct_IO(dio, &sdio, &map_bh);
1275-
1276-
if (retval) {
1277-
dio_cleanup(dio, &sdio);
1278-
break;
1279-
}
1280-
} /* end iovec loop */
1243+
retval = do_direct_IO(dio, &sdio, &map_bh);
1244+
if (retval)
1245+
dio_cleanup(dio, &sdio);
12811246

12821247
if (retval == -ENOTBLK) {
12831248
/*

include/linux/uio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
7171
unsigned long iov_iter_alignment(const struct iov_iter *i);
7272
void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov,
7373
unsigned long nr_segs, size_t count);
74+
ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages,
75+
size_t maxsize, size_t *start);
7476

7577
static inline size_t iov_iter_count(struct iov_iter *i)
7678
{

mm/iov_iter.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,30 @@ void iov_iter_init(struct iov_iter *i, int direction,
235235
i->count = count;
236236
}
237237
EXPORT_SYMBOL(iov_iter_init);
238+
239+
ssize_t iov_iter_get_pages(struct iov_iter *i,
240+
struct page **pages, size_t maxsize,
241+
size_t *start)
242+
{
243+
size_t offset = i->iov_offset;
244+
const struct iovec *iov = i->iov;
245+
size_t len;
246+
unsigned long addr;
247+
int n;
248+
int res;
249+
250+
len = iov->iov_len - offset;
251+
if (len > i->count)
252+
len = i->count;
253+
if (len > maxsize)
254+
len = maxsize;
255+
addr = (unsigned long)iov->iov_base + offset;
256+
len += *start = addr & (PAGE_SIZE - 1);
257+
addr &= ~(PAGE_SIZE - 1);
258+
n = (len + PAGE_SIZE - 1) / PAGE_SIZE;
259+
res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages);
260+
if (unlikely(res < 0))
261+
return res;
262+
return (res == n ? len : res * PAGE_SIZE) - *start;
263+
}
264+
EXPORT_SYMBOL(iov_iter_get_pages);

0 commit comments

Comments
 (0)