aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2/gcinode.c
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-04-06 22:01:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-07 11:31:16 -0400
commita3d93f709e893187d301aa5458b2248db9f22bd1 (patch)
tree9097bf495de11ebac7853c0007a2f3a67fab876b /fs/nilfs2/gcinode.c
parent84ef1ecfdea2f9f1e740a4bee0fa9cd629bdda70 (diff)
nilfs2: block cache for garbage collection
This adds the cache of on-disk blocks to be moved in garbage collection. The disk blocks are held with dummy inodes (called gcinodes), and this file provides lookup function of the dummy inodes, and their buffer read function. Signed-off-by: Seiji Kihara <kihara.seiji@lab.ntt.co.jp> Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Signed-off-by: Yoshiji Amagai <amagai.yoshiji@lab.ntt.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nilfs2/gcinode.c')
-rw-r--r--fs/nilfs2/gcinode.c270
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
36static 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 */
60int 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 */
128int 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
138int 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 */
160int 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 */
183void 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
192static 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
216static 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 */
225struct 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 */
247void 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 */
256void 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}