]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: David Chinner <david@fromorbit.com> |
2 | References: bnc#450658 | |
3 | Subject: [XFS] Account for allocated blocks when expanding directories | |
4 | Patch-mainline: ? | |
5 | ||
6 | When we create a directory, we reserve a number of blocks for the maximum | |
7 | possible expansion of of the directory due to various btree splits, | |
8 | freespace allocation, etc. Unfortunately, each allocation is not reflected | |
9 | in the total number of blocks still available to the transaction, so the | |
10 | maximal reservation is used over and over again. | |
11 | ||
12 | This leads to problems where an allocation group has only enough blocks | |
13 | for *some* of the allocations required for the directory modification. | |
14 | After the first N allocations, the remaining blocks in the allocation | |
15 | group drops below the total reservation, and subsequent allocations fail | |
16 | because the allocator will not allow the allocation to proceed if the AG | |
17 | does not have the enough blocks available for the entire allocation total. | |
18 | ||
19 | This results in an ENOSPC occurring after an allocation has already | |
20 | occurred. This results in aborting the directory operation (leaving the | |
21 | directory in an inconsistent state) and cancelling a dirty transaction, | |
22 | which results in a filesystem shutdown. | |
23 | ||
24 | Avoid the problem by reflecting the number of blocks allocated in any | |
25 | directory expansion in the total number of blocks available to the | |
26 | modification in progress. This prevents a directory modification from | |
27 | being aborted part way through with an ENOSPC. | |
28 | ||
29 | SGI-PV: 988144 | |
30 | ||
31 | SGI-Modid: xfs-linux-melb:xfs-kern:32340a | |
32 | ||
33 | Signed-off-by: David Chinner <david@fromorbit.com> | |
34 | Signed-off-by: Lachlan McIlroy <lachlan@sgi.com> | |
35 | Acked-by: Jan Kara <jack@suse.cz> | |
36 | ||
37 | diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c | |
38 | index 9e561a9..a11a839 100644 | |
39 | --- a/fs/xfs/xfs_da_btree.c | |
40 | +++ b/fs/xfs/xfs_da_btree.c | |
41 | @@ -1566,11 +1566,14 @@ xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno) | |
42 | int nmap, error, w, count, c, got, i, mapi; | |
43 | xfs_trans_t *tp; | |
44 | xfs_mount_t *mp; | |
45 | + xfs_drfsbno_t nblks; | |
46 | ||
47 | dp = args->dp; | |
48 | mp = dp->i_mount; | |
49 | w = args->whichfork; | |
50 | tp = args->trans; | |
51 | + nblks = dp->i_d.di_nblocks; | |
52 | + | |
53 | /* | |
54 | * For new directories adjust the file offset and block count. | |
55 | */ | |
56 | @@ -1647,6 +1650,8 @@ xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno) | |
57 | } | |
58 | if (mapp != &map) | |
59 | kmem_free(mapp); | |
60 | + /* account for newly allocated blocks in reserved blocks total */ | |
61 | + args->total -= dp->i_d.di_nblocks - nblks; | |
62 | *new_blkno = (xfs_dablk_t)bno; | |
63 | return 0; | |
64 | } | |
65 | diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c | |
66 | index 80e0dc5..1afb122 100644 | |
67 | --- a/fs/xfs/xfs_dir2.c | |
68 | +++ b/fs/xfs/xfs_dir2.c | |
69 | @@ -525,11 +525,13 @@ xfs_dir2_grow_inode( | |
70 | xfs_mount_t *mp; | |
71 | int nmap; /* number of bmap entries */ | |
72 | xfs_trans_t *tp; | |
73 | + xfs_drfsbno_t nblks; | |
74 | ||
75 | xfs_dir2_trace_args_s("grow_inode", args, space); | |
76 | dp = args->dp; | |
77 | tp = args->trans; | |
78 | mp = dp->i_mount; | |
79 | + nblks = dp->i_d.di_nblocks; | |
80 | /* | |
81 | * Set lowest possible block in the space requested. | |
82 | */ | |
83 | @@ -622,7 +624,11 @@ xfs_dir2_grow_inode( | |
84 | */ | |
85 | if (mapp != &map) | |
86 | kmem_free(mapp); | |
87 | + | |
88 | + /* account for newly allocated blocks in reserved blocks total */ | |
89 | + args->total -= dp->i_d.di_nblocks - nblks; | |
90 | *dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno); | |
91 | + | |
92 | /* | |
93 | * Update file's size if this is the data space and it grew. | |
94 | */ |