add patch add-support-for-avoiding-data-writes-during-transaction-commits
[ext4-patch-queue.git] / allow-resched-and-interruption-of-large-empty-directories
blob4df1047ae3b60288e8730b8263262cc20265f5d5
1 ext4: allow readdir()'s of large empty directories to be interrupted
3 If a directory has a large number of empty blocks, iterating over all
4 of them can take a long time, leading to scheduler warnings and users
5 getting irritated when they can't kill a process in the middle of one
6 of these long-running readdir operations.  Fix this by adding checks to
7 ext4_readdir() and ext4_htree_fill_tree().
9 This was reverted earlier due to a typo in the original commit where I
10 experimented with using signal_pending() instead of
11 fatal_signal_pending().  The test was in the wrong place if we were
12 going to return signal_pending() since we would end up returning
13 duplicant entries.  See 9f2394c9be47 for a more detailed explanation.
15 Added fix as suggested by Linus to check for signal_pending() in
16 in the filldir() functions.
18 Reported-by: Benjamin LaHaise <bcrl@kvack.org>
19 Google-Bug-Id: 27880676
20 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
21 ---
22  fs/compat.c     | 4 ++++
23  fs/ext4/dir.c   | 5 +++++
24  fs/ext4/namei.c | 5 +++++
25  fs/readdir.c    | 4 ++++
26  4 files changed, 18 insertions(+)
28 diff --git a/fs/compat.c b/fs/compat.c
29 index a71936a..f940cb2 100644
30 --- a/fs/compat.c
31 +++ b/fs/compat.c
32 @@ -936,6 +936,8 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
33         }
34         dirent = buf->previous;
35         if (dirent) {
36 +               if (signal_pending(current))
37 +                       return -EINTR;
38                 if (__put_user(offset, &dirent->d_off))
39                         goto efault;
40         }
41 @@ -1020,6 +1022,8 @@ static int compat_filldir64(struct dir_context *ctx, const char *name,
42         dirent = buf->previous;
44         if (dirent) {
45 +               if (signal_pending(current))
46 +                       return -EINTR;
47                 if (__put_user_unaligned(offset, &dirent->d_off))
48                         goto efault;
49         }
50 diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
51 index 561d730..4173bfe 100644
52 --- a/fs/ext4/dir.c
53 +++ b/fs/ext4/dir.c
54 @@ -150,6 +150,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
55         while (ctx->pos < inode->i_size) {
56                 struct ext4_map_blocks map;
58 +               if (fatal_signal_pending(current)) {
59 +                       err = -ERESTARTSYS;
60 +                       goto errout;
61 +               }
62 +               cond_resched();
63                 map.m_lblk = ctx->pos >> EXT4_BLOCK_SIZE_BITS(sb);
64                 map.m_len = 1;
65                 err = ext4_map_blocks(NULL, inode, &map, 0);
66 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
67 index 48e4b89..c07422d 100644
68 --- a/fs/ext4/namei.c
69 +++ b/fs/ext4/namei.c
70 @@ -1107,6 +1107,11 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
71         }
73         while (1) {
74 +               if (fatal_signal_pending(current)) {
75 +                       err = -ERESTARTSYS;
76 +                       goto errout;
77 +               }
78 +               cond_resched();
79                 block = dx_get_block(frame->at);
80                 ret = htree_dirblock_to_tree(dir_file, dir, block, &hinfo,
81                                              start_hash, start_minor_hash);
82 diff --git a/fs/readdir.c b/fs/readdir.c
83 index e69ef3b..5f2d4be 100644
84 --- a/fs/readdir.c
85 +++ b/fs/readdir.c
86 @@ -169,6 +169,8 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
87         }
88         dirent = buf->previous;
89         if (dirent) {
90 +               if (signal_pending(current))
91 +                       return -EINTR;
92                 if (__put_user(offset, &dirent->d_off))
93                         goto efault;
94         }
95 @@ -248,6 +250,8 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
96                 return -EINVAL;
97         dirent = buf->previous;
98         if (dirent) {
99 +               if (signal_pending(current))
100 +                       return -EINTR;
101                 if (__put_user(offset, &dirent->d_off))
102                         goto efault;
103         }