diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2006-03-22 03:08:47 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-22 10:54:03 -0500 |
commit | a564da3964db3256069190c2ae95069143ac37fb (patch) | |
tree | b7b9d66c1d6dde82db23222b16b0296013b1c3db | |
parent | d15c023b44e5d323f1f4130b85d29f08e43433b1 (diff) |
[PATCH] readahead: ->prev_page can overrun the ahead window
If get_next_ra_size() does not grow fast enough, ->prev_page can overrun
the ahead window. This means the caller will read the pages from
->ahead_start + ->ahead_size to ->prev_page synchronously.
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Cc: Steven Pratt <slpratt@austin.ibm.com>
Cc: Ram Pai <linuxram@us.ibm.com>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | mm/readahead.c | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/mm/readahead.c b/mm/readahead.c index 8d6eeaaa6296..57557e294987 100644 --- a/mm/readahead.c +++ b/mm/readahead.c | |||
@@ -52,13 +52,24 @@ static inline unsigned long get_min_readahead(struct file_ra_state *ra) | |||
52 | return (VM_MIN_READAHEAD * 1024) / PAGE_CACHE_SIZE; | 52 | return (VM_MIN_READAHEAD * 1024) / PAGE_CACHE_SIZE; |
53 | } | 53 | } |
54 | 54 | ||
55 | static inline void reset_ahead_window(struct file_ra_state *ra) | ||
56 | { | ||
57 | /* | ||
58 | * ... but preserve ahead_start + ahead_size value, | ||
59 | * see 'recheck:' label in page_cache_readahead(). | ||
60 | * Note: We never use ->ahead_size as rvalue without | ||
61 | * checking ->ahead_start != 0 first. | ||
62 | */ | ||
63 | ra->ahead_size += ra->ahead_start; | ||
64 | ra->ahead_start = 0; | ||
65 | } | ||
66 | |||
55 | static inline void ra_off(struct file_ra_state *ra) | 67 | static inline void ra_off(struct file_ra_state *ra) |
56 | { | 68 | { |
57 | ra->start = 0; | 69 | ra->start = 0; |
58 | ra->flags = 0; | 70 | ra->flags = 0; |
59 | ra->size = 0; | 71 | ra->size = 0; |
60 | ra->ahead_start = 0; | 72 | reset_ahead_window(ra); |
61 | ra->ahead_size = 0; | ||
62 | return; | 73 | return; |
63 | } | 74 | } |
64 | 75 | ||
@@ -426,8 +437,7 @@ static int make_ahead_window(struct address_space *mapping, struct file *filp, | |||
426 | * congestion. The ahead window will any way be closed | 437 | * congestion. The ahead window will any way be closed |
427 | * in case we failed due to excessive page cache hits. | 438 | * in case we failed due to excessive page cache hits. |
428 | */ | 439 | */ |
429 | ra->ahead_start = 0; | 440 | reset_ahead_window(ra); |
430 | ra->ahead_size = 0; | ||
431 | } | 441 | } |
432 | 442 | ||
433 | return ret; | 443 | return ret; |
@@ -520,11 +530,11 @@ page_cache_readahead(struct address_space *mapping, struct file_ra_state *ra, | |||
520 | * If we get here we are doing sequential IO and this was not the first | 530 | * If we get here we are doing sequential IO and this was not the first |
521 | * occurence (ie we have an existing window) | 531 | * occurence (ie we have an existing window) |
522 | */ | 532 | */ |
523 | |||
524 | if (ra->ahead_start == 0) { /* no ahead window yet */ | 533 | if (ra->ahead_start == 0) { /* no ahead window yet */ |
525 | if (!make_ahead_window(mapping, filp, ra, 0)) | 534 | if (!make_ahead_window(mapping, filp, ra, 0)) |
526 | goto out; | 535 | goto recheck; |
527 | } | 536 | } |
537 | |||
528 | /* | 538 | /* |
529 | * Already have an ahead window, check if we crossed into it. | 539 | * Already have an ahead window, check if we crossed into it. |
530 | * If so, shift windows and issue a new ahead window. | 540 | * If so, shift windows and issue a new ahead window. |
@@ -536,6 +546,10 @@ page_cache_readahead(struct address_space *mapping, struct file_ra_state *ra, | |||
536 | ra->start = ra->ahead_start; | 546 | ra->start = ra->ahead_start; |
537 | ra->size = ra->ahead_size; | 547 | ra->size = ra->ahead_size; |
538 | make_ahead_window(mapping, filp, ra, 0); | 548 | make_ahead_window(mapping, filp, ra, 0); |
549 | recheck: | ||
550 | /* prev_page shouldn't overrun the ahead window */ | ||
551 | ra->prev_page = min(ra->prev_page, | ||
552 | ra->ahead_start + ra->ahead_size - 1); | ||
539 | } | 553 | } |
540 | 554 | ||
541 | out: | 555 | out: |