diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/gfs2/bmap.c | 2 | ||||
-rw-r--r-- | fs/gfs2/dir.c | 144 | ||||
-rw-r--r-- | fs/gfs2/dir.h | 4 |
3 files changed, 78 insertions, 72 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index c7723119acb6..68bc3be09c98 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c | |||
@@ -111,7 +111,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer, | |||
111 | if (isdir) { | 111 | if (isdir) { |
112 | block = gfs2_alloc_meta(ip); | 112 | block = gfs2_alloc_meta(ip); |
113 | 113 | ||
114 | error = gfs2_dir_get_buffer(ip, block, 1, &bh); | 114 | error = gfs2_dir_get_new_buffer(ip, block, &bh); |
115 | if (error) | 115 | if (error) |
116 | goto out_brelse; | 116 | goto out_brelse; |
117 | gfs2_buffer_copy_tail(bh, | 117 | gfs2_buffer_copy_tail(bh, |
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index eb68cdd41d48..ffc1beff6703 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c | |||
@@ -8,61 +8,59 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | /* | 10 | /* |
11 | * Implements Extendible Hashing as described in: | 11 | * Implements Extendible Hashing as described in: |
12 | * "Extendible Hashing" by Fagin, et al in | 12 | * "Extendible Hashing" by Fagin, et al in |
13 | * __ACM Trans. on Database Systems__, Sept 1979. | 13 | * __ACM Trans. on Database Systems__, Sept 1979. |
14 | * | 14 | * |
15 | * | 15 | * |
16 | * Here's the layout of dirents which is essentially the same as that of ext2 | 16 | * Here's the layout of dirents which is essentially the same as that of ext2 |
17 | * within a single block. The field de_name_len is the number of bytes | 17 | * within a single block. The field de_name_len is the number of bytes |
18 | * actually required for the name (no null terminator). The field de_rec_len | 18 | * actually required for the name (no null terminator). The field de_rec_len |
19 | * is the number of bytes allocated to the dirent. The offset of the next | 19 | * is the number of bytes allocated to the dirent. The offset of the next |
20 | * dirent in the block is (dirent + dirent->de_rec_len). When a dirent is | 20 | * dirent in the block is (dirent + dirent->de_rec_len). When a dirent is |
21 | * deleted, the preceding dirent inherits its allocated space, ie | 21 | * deleted, the preceding dirent inherits its allocated space, ie |
22 | * prev->de_rec_len += deleted->de_rec_len. Since the next dirent is obtained | 22 | * prev->de_rec_len += deleted->de_rec_len. Since the next dirent is obtained |
23 | * by adding de_rec_len to the current dirent, this essentially causes the | 23 | * by adding de_rec_len to the current dirent, this essentially causes the |
24 | * deleted dirent to get jumped over when iterating through all the dirents. | 24 | * deleted dirent to get jumped over when iterating through all the dirents. |
25 | * | 25 | * |
26 | * When deleting the first dirent in a block, there is no previous dirent so | 26 | * When deleting the first dirent in a block, there is no previous dirent so |
27 | * the field de_ino is set to zero to designate it as deleted. When allocating | 27 | * the field de_ino is set to zero to designate it as deleted. When allocating |
28 | * a dirent, gfs2_dirent_alloc iterates through the dirents in a block. If the | 28 | * a dirent, gfs2_dirent_alloc iterates through the dirents in a block. If the |
29 | * first dirent has (de_ino == 0) and de_rec_len is large enough, this first | 29 | * first dirent has (de_ino == 0) and de_rec_len is large enough, this first |
30 | * dirent is allocated. Otherwise it must go through all the 'used' dirents | 30 | * dirent is allocated. Otherwise it must go through all the 'used' dirents |
31 | * searching for one in which the amount of total space minus the amount of | 31 | * searching for one in which the amount of total space minus the amount of |
32 | * used space will provide enough space for the new dirent. | 32 | * used space will provide enough space for the new dirent. |
33 | * | 33 | * |
34 | * There are two types of blocks in which dirents reside. In a stuffed dinode, | 34 | * There are two types of blocks in which dirents reside. In a stuffed dinode, |
35 | * the dirents begin at offset sizeof(struct gfs2_dinode) from the beginning of | 35 | * the dirents begin at offset sizeof(struct gfs2_dinode) from the beginning of |
36 | * the block. In leaves, they begin at offset sizeof(struct gfs2_leaf) from the | 36 | * the block. In leaves, they begin at offset sizeof(struct gfs2_leaf) from the |
37 | * beginning of the leaf block. The dirents reside in leaves when | 37 | * beginning of the leaf block. The dirents reside in leaves when |
38 | * | 38 | * |
39 | * dip->i_di.di_flags & GFS2_DIF_EXHASH is true | 39 | * dip->i_di.di_flags & GFS2_DIF_EXHASH is true |
40 | * | 40 | * |
41 | * Otherwise, the dirents are "linear", within a single stuffed dinode block. | 41 | * Otherwise, the dirents are "linear", within a single stuffed dinode block. |
42 | * | 42 | * |
43 | * When the dirents are in leaves, the actual contents of the directory file are | 43 | * When the dirents are in leaves, the actual contents of the directory file are |
44 | * used as an array of 64-bit block pointers pointing to the leaf blocks. The | 44 | * used as an array of 64-bit block pointers pointing to the leaf blocks. The |
45 | * dirents are NOT in the directory file itself. There can be more than one block | 45 | * dirents are NOT in the directory file itself. There can be more than one |
46 | * pointer in the array that points to the same leaf. In fact, when a directory | 46 | * block pointer in the array that points to the same leaf. In fact, when a |
47 | * is first converted from linear to exhash, all of the pointers point to the | 47 | * directory is first converted from linear to exhash, all of the pointers |
48 | * same leaf. | 48 | * point to the same leaf. |
49 | * | 49 | * |
50 | * When a leaf is completely full, the size of the hash table can be | 50 | * When a leaf is completely full, the size of the hash table can be |
51 | * doubled unless it is already at the maximum size which is hard coded into | 51 | * doubled unless it is already at the maximum size which is hard coded into |
52 | * GFS2_DIR_MAX_DEPTH. After that, leaves are chained together in a linked list, | 52 | * GFS2_DIR_MAX_DEPTH. After that, leaves are chained together in a linked list, |
53 | * but never before the maximum hash table size has been reached. | 53 | * but never before the maximum hash table size has been reached. |
54 | */ | 54 | */ |
55 | 55 | ||
56 | #include <linux/sched.h> | 56 | #include <linux/sched.h> |
57 | #include <linux/slab.h> | 57 | #include <linux/slab.h> |
58 | #include <linux/spinlock.h> | 58 | #include <linux/spinlock.h> |
59 | #include <linux/completion.h> | ||
60 | #include <linux/buffer_head.h> | 59 | #include <linux/buffer_head.h> |
61 | #include <linux/sort.h> | 60 | #include <linux/sort.h> |
62 | #include <linux/gfs2_ondisk.h> | 61 | #include <linux/gfs2_ondisk.h> |
63 | #include <linux/crc32.h> | 62 | #include <linux/crc32.h> |
64 | #include <linux/vmalloc.h> | 63 | #include <linux/vmalloc.h> |
65 | #include <asm/semaphore.h> | ||
66 | 64 | ||
67 | #include "gfs2.h" | 65 | #include "gfs2.h" |
68 | #include "lm_interface.h" | 66 | #include "lm_interface.h" |
@@ -92,33 +90,36 @@ typedef int (*leaf_call_t) (struct gfs2_inode *dip, | |||
92 | uint32_t index, uint32_t len, uint64_t leaf_no, | 90 | uint32_t index, uint32_t len, uint64_t leaf_no, |
93 | void *data); | 91 | void *data); |
94 | 92 | ||
95 | int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new, | 93 | |
96 | struct buffer_head **bhp) | 94 | int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, uint64_t block, |
95 | struct buffer_head **bhp) | ||
97 | { | 96 | { |
98 | struct buffer_head *bh; | 97 | struct buffer_head *bh; |
99 | int error = 0; | ||
100 | |||
101 | if (new) { | ||
102 | bh = gfs2_meta_new(ip->i_gl, block); | ||
103 | gfs2_trans_add_bh(ip->i_gl, bh, 1); | ||
104 | gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD); | ||
105 | gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header)); | ||
106 | } else { | ||
107 | error = gfs2_meta_read(ip->i_gl, block, DIO_START | DIO_WAIT, | ||
108 | &bh); | ||
109 | if (error) | ||
110 | return error; | ||
111 | if (gfs2_metatype_check(ip->i_sbd, bh, GFS2_METATYPE_JD)) { | ||
112 | brelse(bh); | ||
113 | return -EIO; | ||
114 | } | ||
115 | } | ||
116 | 98 | ||
99 | bh = gfs2_meta_new(ip->i_gl, block); | ||
100 | gfs2_trans_add_bh(ip->i_gl, bh, 1); | ||
101 | gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD); | ||
102 | gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header)); | ||
117 | *bhp = bh; | 103 | *bhp = bh; |
118 | return 0; | 104 | return 0; |
119 | } | 105 | } |
120 | 106 | ||
107 | static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, uint64_t block, | ||
108 | struct buffer_head **bhp) | ||
109 | { | ||
110 | struct buffer_head *bh; | ||
111 | int error; | ||
121 | 112 | ||
113 | error = gfs2_meta_read(ip->i_gl, block, DIO_START | DIO_WAIT, &bh); | ||
114 | if (error) | ||
115 | return error; | ||
116 | if (gfs2_metatype_check(ip->i_sbd, bh, GFS2_METATYPE_JD)) { | ||
117 | brelse(bh); | ||
118 | return -EIO; | ||
119 | } | ||
120 | *bhp = bh; | ||
121 | return 0; | ||
122 | } | ||
122 | 123 | ||
123 | static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf, | 124 | static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf, |
124 | unsigned int offset, unsigned int size) | 125 | unsigned int offset, unsigned int size) |
@@ -205,9 +206,11 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf, | |||
205 | goto fail; | 206 | goto fail; |
206 | } | 207 | } |
207 | 208 | ||
208 | error = gfs2_dir_get_buffer(ip, dblock, | 209 | if (amount == sdp->sd_jbsize || new) |
209 | (amount == sdp->sd_jbsize) ? | 210 | error = gfs2_dir_get_new_buffer(ip, dblock, &bh); |
210 | 1 : new, &bh); | 211 | else |
212 | error = gfs2_dir_get_existing_buffer(ip, dblock, &bh); | ||
213 | |||
211 | if (error) | 214 | if (error) |
212 | goto fail; | 215 | goto fail; |
213 | 216 | ||
@@ -321,7 +324,10 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, | |||
321 | gfs2_meta_ra(ip->i_gl, dblock, extlen); | 324 | gfs2_meta_ra(ip->i_gl, dblock, extlen); |
322 | 325 | ||
323 | if (dblock) { | 326 | if (dblock) { |
324 | error = gfs2_dir_get_buffer(ip, dblock, new, &bh); | 327 | if (new) |
328 | error = gfs2_dir_get_new_buffer(ip, dblock, &bh); | ||
329 | else | ||
330 | error = gfs2_dir_get_existing_buffer(ip, dblock, &bh); | ||
325 | if (error) | 331 | if (error) |
326 | goto fail; | 332 | goto fail; |
327 | dblock++; | 333 | dblock++; |
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h index 42b3a1f34deb..d209f1fcb8ac 100644 --- a/fs/gfs2/dir.h +++ b/fs/gfs2/dir.h | |||
@@ -41,8 +41,8 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip); | |||
41 | 41 | ||
42 | int gfs2_diradd_alloc_required(struct inode *dir, | 42 | int gfs2_diradd_alloc_required(struct inode *dir, |
43 | const struct qstr *filename); | 43 | const struct qstr *filename); |
44 | int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new, | 44 | int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, uint64_t block, |
45 | struct buffer_head **bhp); | 45 | struct buffer_head **bhp); |
46 | 46 | ||
47 | static inline uint32_t gfs2_disk_hash(const char *data, int len) | 47 | static inline uint32_t gfs2_disk_hash(const char *data, int len) |
48 | { | 48 | { |