diff options
Diffstat (limited to 'fs/nilfs2/gcinode.c')
-rw-r--r-- | fs/nilfs2/gcinode.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c new file mode 100644 index 000000000000..001395266325 --- /dev/null +++ b/fs/nilfs2/gcinode.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * gcinode.c - NILFS memory inode for GC | ||
3 | * | ||
4 | * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | * | ||
20 | * Written by Seiji Kihara <kihara@osrg.net>, Amagai Yoshiji <amagai@osrg.net>, | ||
21 | * and Ryusuke Konishi <ryusuke@osrg.net>. | ||
22 | * Revised by Ryusuke Konishi <ryusuke@osrg.net>. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/buffer_head.h> | ||
27 | #include <linux/mpage.h> | ||
28 | #include <linux/hash.h> | ||
29 | #include <linux/swap.h> | ||
30 | #include "nilfs.h" | ||
31 | #include "page.h" | ||
32 | #include "mdt.h" | ||
33 | #include "dat.h" | ||
34 | #include "ifile.h" | ||
35 | |||
36 | static struct address_space_operations def_gcinode_aops = {}; | ||
37 | /* XXX need def_gcinode_iops/fops? */ | ||
38 | |||
39 | /* | ||
40 | * nilfs_gccache_submit_read_data() - add data buffer and submit read request | ||
41 | * @inode - gc inode | ||
42 | * @blkoff - dummy offset treated as the key for the page cache | ||
43 | * @pbn - physical block number of the block | ||
44 | * @vbn - virtual block number of the block, 0 for non-virtual block | ||
45 | * @out_bh - indirect pointer to a buffer_head struct to receive the results | ||
46 | * | ||
47 | * Description: nilfs_gccache_submit_read_data() registers the data buffer | ||
48 | * specified by @pbn to the GC pagecache with the key @blkoff. | ||
49 | * This function sets @vbn (@pbn if @vbn is zero) in b_blocknr of the buffer. | ||
50 | * | ||
51 | * Return Value: On success, 0 is returned. On Error, one of the following | ||
52 | * negative error code is returned. | ||
53 | * | ||
54 | * %-EIO - I/O error. | ||
55 | * | ||
56 | * %-ENOMEM - Insufficient amount of memory available. | ||
57 | * | ||
58 | * %-ENOENT - The block specified with @pbn does not exist. | ||
59 | */ | ||
60 | int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff, | ||
61 | sector_t pbn, __u64 vbn, | ||
62 | struct buffer_head **out_bh) | ||
63 | { | ||
64 | struct buffer_head *bh; | ||
65 | int err; | ||
66 | |||
67 | bh = nilfs_grab_buffer(inode, inode->i_mapping, blkoff, 0); | ||
68 | if (unlikely(!bh)) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | if (buffer_uptodate(bh)) | ||
72 | goto out; | ||
73 | |||
74 | if (pbn == 0) { | ||
75 | struct inode *dat_inode = NILFS_I_NILFS(inode)->ns_dat; | ||
76 | /* use original dat, not gc dat. */ | ||
77 | err = nilfs_dat_translate(dat_inode, vbn, &pbn); | ||
78 | if (unlikely(err)) { /* -EIO, -ENOMEM, -ENOENT */ | ||
79 | brelse(bh); | ||
80 | goto failed; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | lock_buffer(bh); | ||
85 | if (buffer_uptodate(bh)) { | ||
86 | unlock_buffer(bh); | ||
87 | goto out; | ||
88 | } | ||
89 | |||
90 | if (!buffer_mapped(bh)) { | ||
91 | bh->b_bdev = NILFS_I_NILFS(inode)->ns_bdev; | ||
92 | set_buffer_mapped(bh); | ||
93 | } | ||
94 | bh->b_blocknr = pbn; | ||
95 | bh->b_end_io = end_buffer_read_sync; | ||
96 | get_bh(bh); | ||
97 | submit_bh(READ, bh); | ||
98 | if (vbn) | ||
99 | bh->b_blocknr = vbn; | ||
100 | out: | ||
101 | err = 0; | ||
102 | *out_bh = bh; | ||
103 | |||
104 | failed: | ||
105 | unlock_page(bh->b_page); | ||
106 | page_cache_release(bh->b_page); | ||
107 | return err; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * nilfs_gccache_submit_read_node() - add node buffer and submit read request | ||
112 | * @inode - gc inode | ||
113 | * @pbn - physical block number for the block | ||
114 | * @vbn - virtual block number for the block | ||
115 | * @out_bh - indirect pointer to a buffer_head struct to receive the results | ||
116 | * | ||
117 | * Description: nilfs_gccache_submit_read_node() registers the node buffer | ||
118 | * specified by @vbn to the GC pagecache. @pbn can be supplied by the | ||
119 | * caller to avoid translation of the disk block address. | ||
120 | * | ||
121 | * Return Value: On success, 0 is returned. On Error, one of the following | ||
122 | * negative error code is returned. | ||
123 | * | ||
124 | * %-EIO - I/O error. | ||
125 | * | ||
126 | * %-ENOMEM - Insufficient amount of memory available. | ||
127 | */ | ||
128 | int nilfs_gccache_submit_read_node(struct inode *inode, sector_t pbn, | ||
129 | __u64 vbn, struct buffer_head **out_bh) | ||
130 | { | ||
131 | int ret = nilfs_btnode_submit_block(&NILFS_I(inode)->i_btnode_cache, | ||
132 | vbn ? : pbn, pbn, out_bh, 0); | ||
133 | if (ret == -EEXIST) /* internal code (cache hit) */ | ||
134 | ret = 0; | ||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *bh) | ||
139 | { | ||
140 | wait_on_buffer(bh); | ||
141 | if (!buffer_uptodate(bh)) | ||
142 | return -EIO; | ||
143 | if (buffer_dirty(bh)) | ||
144 | return -EEXIST; | ||
145 | |||
146 | if (buffer_nilfs_node(bh)) | ||
147 | nilfs_btnode_mark_dirty(bh); | ||
148 | else | ||
149 | nilfs_mdt_mark_buffer_dirty(bh); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * nilfs_init_gccache() - allocate and initialize gc_inode hash table | ||
155 | * @nilfs - the_nilfs | ||
156 | * | ||
157 | * Return Value: On success, 0. | ||
158 | * On error, a negative error code is returned. | ||
159 | */ | ||
160 | int nilfs_init_gccache(struct the_nilfs *nilfs) | ||
161 | { | ||
162 | int loop; | ||
163 | |||
164 | BUG_ON(nilfs->ns_gc_inodes_h); | ||
165 | |||
166 | INIT_LIST_HEAD(&nilfs->ns_gc_inodes); | ||
167 | |||
168 | nilfs->ns_gc_inodes_h = | ||
169 | kmalloc(sizeof(struct hlist_head) * NILFS_GCINODE_HASH_SIZE, | ||
170 | GFP_NOFS); | ||
171 | if (nilfs->ns_gc_inodes_h == NULL) | ||
172 | return -ENOMEM; | ||
173 | |||
174 | for (loop = 0; loop < NILFS_GCINODE_HASH_SIZE; loop++) | ||
175 | INIT_HLIST_HEAD(&nilfs->ns_gc_inodes_h[loop]); | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * nilfs_destroy_gccache() - free gc_inode hash table | ||
181 | * @nilfs - the nilfs | ||
182 | */ | ||
183 | void nilfs_destroy_gccache(struct the_nilfs *nilfs) | ||
184 | { | ||
185 | if (nilfs->ns_gc_inodes_h) { | ||
186 | nilfs_remove_all_gcinode(nilfs); | ||
187 | kfree(nilfs->ns_gc_inodes_h); | ||
188 | nilfs->ns_gc_inodes_h = NULL; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static struct inode *alloc_gcinode(struct the_nilfs *nilfs, ino_t ino, | ||
193 | __u64 cno) | ||
194 | { | ||
195 | struct inode *inode = nilfs_mdt_new_common(nilfs, NULL, ino, GFP_NOFS); | ||
196 | struct nilfs_inode_info *ii; | ||
197 | |||
198 | if (!inode) | ||
199 | return NULL; | ||
200 | |||
201 | inode->i_op = NULL; | ||
202 | inode->i_fop = NULL; | ||
203 | inode->i_mapping->a_ops = &def_gcinode_aops; | ||
204 | |||
205 | ii = NILFS_I(inode); | ||
206 | ii->i_cno = cno; | ||
207 | ii->i_flags = 0; | ||
208 | ii->i_state = 1 << NILFS_I_GCINODE; | ||
209 | ii->i_bh = NULL; | ||
210 | ii->i_dtime = 0; | ||
211 | nilfs_bmap_init_gc(ii->i_bmap); | ||
212 | |||
213 | return inode; | ||
214 | } | ||
215 | |||
216 | static unsigned long ihash(ino_t ino, __u64 cno) | ||
217 | { | ||
218 | return hash_long((unsigned long)((ino << 2) + cno), | ||
219 | NILFS_GCINODE_HASH_BITS); | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * nilfs_gc_iget() - find or create gc inode with specified (ino,cno) | ||
224 | */ | ||
225 | struct inode *nilfs_gc_iget(struct the_nilfs *nilfs, ino_t ino, __u64 cno) | ||
226 | { | ||
227 | struct hlist_head *head = nilfs->ns_gc_inodes_h + ihash(ino, cno); | ||
228 | struct hlist_node *node; | ||
229 | struct inode *inode; | ||
230 | |||
231 | hlist_for_each_entry(inode, node, head, i_hash) { | ||
232 | if (inode->i_ino == ino && NILFS_I(inode)->i_cno == cno) | ||
233 | return inode; | ||
234 | } | ||
235 | |||
236 | inode = alloc_gcinode(nilfs, ino, cno); | ||
237 | if (likely(inode)) { | ||
238 | hlist_add_head(&inode->i_hash, head); | ||
239 | list_add(&NILFS_I(inode)->i_dirty, &nilfs->ns_gc_inodes); | ||
240 | } | ||
241 | return inode; | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * nilfs_clear_gcinode() - clear and free a gc inode | ||
246 | */ | ||
247 | void nilfs_clear_gcinode(struct inode *inode) | ||
248 | { | ||
249 | nilfs_mdt_clear(inode); | ||
250 | nilfs_mdt_destroy(inode); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * nilfs_remove_all_gcinode() - remove all inodes from the_nilfs | ||
255 | */ | ||
256 | void nilfs_remove_all_gcinode(struct the_nilfs *nilfs) | ||
257 | { | ||
258 | struct hlist_head *head = nilfs->ns_gc_inodes_h; | ||
259 | struct hlist_node *node, *n; | ||
260 | struct inode *inode; | ||
261 | int loop; | ||
262 | |||
263 | for (loop = 0; loop < NILFS_GCINODE_HASH_SIZE; loop++, head++) { | ||
264 | hlist_for_each_entry_safe(inode, node, n, head, i_hash) { | ||
265 | hlist_del_init(&inode->i_hash); | ||
266 | list_del_init(&NILFS_I(inode)->i_dirty); | ||
267 | nilfs_clear_gcinode(inode); /* might sleep */ | ||
268 | } | ||
269 | } | ||
270 | } | ||