Skip to content

Commit fdabe4f

Browse files
committed
Support for extents
Now the entry of index block of the file point to a extent which contain 8 consecutive physical blocks instead of a single block. Also modify the block allocator so we can allocate consecutive blocks at once (also first fit). Future work: A extent can contain up to `~(uint32_t) 0` blocks, so we may allocate more consecutive blocks for a extent with smarter block allocator and delayed allocation. Use extent tree to increase the number of extent a file can have.
1 parent f9879be commit fdabe4f

File tree

12 files changed

+239
-125
lines changed

12 files changed

+239
-125
lines changed

.clang-format

+3
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ UseTab: Never
1313
IndentWidth: 4
1414
BreakBeforeBraces: Linux
1515
AccessModifierOffset: -4
16+
ForEachMacros:
17+
- 'for_each_set_bit'
18+
- 'for_each_set_bit_from'

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
obj-m += simplefs.o
2-
simplefs-objs := fs.o super.o inode.o file.o dir.o
2+
simplefs-objs := fs.o super.o inode.o file.o dir.o extent.o
33

44
KDIR ?= /lib/modules/$(shell uname -r)/build
55

README.md

+47-20
Original file line numberDiff line numberDiff line change
@@ -79,47 +79,74 @@ Each block is 4 KiB large.
7979
The superblock is the first block of the partition (block 0). It contains the partition's metadata, such as the number of blocks, number of inodes, number of free inodes/blocks, ...
8080

8181
### Inode store
82-
Contains all the inodes of the partition. The maximum number of inodes is equal to the number of blocks of the partition. Each inode contains 40 B of data: standard data such as file size and number of used blocks, as well as a simplefs-specific field called `index_block`. This block contains:
82+
Contains all the inodes of the partition. The maximum number of inodes is equal to the number of blocks of the partition. Each inode contains 72 B of data: standard data such as file size and number of used blocks, as well as a simplefs-specific union field contain `dir_block` and `ei_block`. This block contains:
8383
- for a directory: the list of files in this directory. A directory can contain at most 128 files, and filenames are limited to 28 characters to fit in a single block.
8484
```
8585
inode
8686
+-----------------------+
8787
| i_mode = IFDIR | 0755 | block 123
88-
| index_block = 123 ----|--------> +-----------+
88+
| dir_block = 123 ----|--------> +-----------+
8989
| i_size = 4 KiB | 0 | 24 (foo) |
90-
| i_blockcs = 1 | |-----------|
90+
| i_blocks = 1 | |-----------|
9191
+-----------------------+ 1 | 45 (bar) |
9292
|-----------|
93-
...
93+
| ... |
9494
|-----------|
9595
127 | 0 |
9696
+-----------+
9797
```
98-
- for a file: the list of blocks containing the actual data of this file. Since block IDs are stored as 32-bit values, at most 1024 links fit in a single block, limiting the size of a file to 4 MiB.
98+
- for a file: the list of extents containing the actual data of this file. Since block IDs are stored as `sizeof(struct simplefs_extent)` bytes values, at most 341 links fit in a single block, limiting the size of a file to around 10.65 MiB (10912 KiB).
9999
```
100-
inode block 94
101-
+-----------------------+ +--------+
102-
| i_mode = IFDIR | 0644 | block 93 | | block 99
103-
| index_block = 93 ----|------> +---------+ | | +--------+
104-
| i_size = 10 KiB | 0 | 94 ---|-----> +--------+ | |
105-
| i_blockcs = 4 | |---------| | |
106-
+-----------------------+ 1 | 99 ---|------------------> +--------+
107-
|---------|
108-
2 | 66 ---|-----> block 66
109-
|---------| +--------+
110-
... | |
111-
|---------| | |
112-
127 | 0 | +--------+
113-
+---------+
100+
inode
101+
+-----------------------+
102+
| i_mode = IFDIR | 0644 | block 93
103+
| ei_block = 93 ----|------> +----------------+
104+
| i_size = 10 KiB | 0 | ee_block = 0 |
105+
| i_blocks = 25 | | ee_len = 8 | extent 94
106+
+-----------------------+ | ee_start = 94 |---> +--------+
107+
|----------------| | |
108+
1 | ee_block = 8 | +--------+
109+
| ee_len = 8 | extent 99
110+
| ee_start = 99 |---> +--------+
111+
|----------------| | |
112+
2 | ee_block = 16 | +--------+
113+
| ee_len = 8 | extent 66
114+
| ee_start = 66 |---> +--------+
115+
|----------------| | |
116+
| ... | +--------+
117+
|----------------|
118+
341 | ee_block = 0 |
119+
| ee_len = 0 |
120+
| ee_start = 0 |
121+
+----------------+
114122
```
123+
### Extent support
124+
The extent covers consecutive blocks, we allocate consecutive disk blocks for it at a single time. It is described by `struct simplefs_extent` which contains three members:
125+
- `ee_block`: first logical block extent covers.
126+
- `ee_len`: number of blocks covered by extent.
127+
- `ee_start`: first physical block extent covers.
128+
```
129+
struct simplefs_extent
130+
+----------------+
131+
| ee_block = 0 |
132+
| ee_len = 200| extent
133+
| ee_start = 12 |-----------> +---------+
134+
+----------------+ block 12 | |
135+
+---------+
136+
13 | |
137+
+---------+
138+
| ... |
139+
+---------+
140+
211 | |
141+
+---------+
115142
143+
```
116144
## TODO
117145

118146
- Bugs
119147
* Fail to support longer filename
120148
* Directory will be full if more than 128 files
121149
* Fail to show `.` and `..` with `ls -a` command
122-
- support for extents
123150
- journalling support
124151

125152
## License

bitmap.h

+41-31
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@
55
#include "simplefs.h"
66

77
/*
8-
* Return the first free bit (set to 1) in a given in-memory bitmap spanning
9-
* over multiple blocks and clear it.
10-
* Return 0 if no free bit found (we assume that the first bit is never free
11-
* because of the superblock and the root inode, thus allowing us to use 0 as an
12-
* error value).
8+
* Return the first bit we found and clear the the following `len` consecutive
9+
* free bit(s) (set to 1) in a given in-memory bitmap spanning over multiple
10+
* blocks. Return 0 if no enough free bit(s) were found (we assume that the
11+
* first bit is never free because of the superblock and the root inode, thus
12+
* allowing us to use 0 as an error value).
1313
*/
14-
static inline uint32_t get_first_free_bit(unsigned long *freemap,
15-
unsigned long size)
14+
static inline uint32_t get_first_free_bits(unsigned long *freemap,
15+
unsigned long size,
16+
uint32_t len)
1617
{
17-
uint32_t ino = find_first_bit(freemap, size);
18-
if (ino == size)
19-
return 0;
20-
21-
bitmap_clear(freemap, ino, 1);
22-
23-
return ino;
18+
uint32_t bit, prev = 0, count = 0;
19+
for_each_set_bit (bit, freemap, size) {
20+
if (prev != bit - 1)
21+
count = 0;
22+
prev = bit;
23+
if (++count == len) {
24+
bitmap_clear(freemap, bit - len + 1, len);
25+
return bit - len + 1;
26+
}
27+
}
28+
return 0;
2429
}
2530

2631
/*
@@ -29,54 +34,59 @@ static inline uint32_t get_first_free_bit(unsigned long *freemap,
2934
*/
3035
static inline uint32_t get_free_inode(struct simplefs_sb_info *sbi)
3136
{
32-
uint32_t ret = get_first_free_bit(sbi->ifree_bitmap, sbi->nr_inodes);
37+
uint32_t ret = get_first_free_bits(sbi->ifree_bitmap, sbi->nr_inodes, 1);
3338
if (ret)
3439
sbi->nr_free_inodes--;
3540
return ret;
3641
}
3742

3843
/*
39-
* Return an unused block number and mark it used.
40-
* Return 0 if no free block was found.
44+
* Return `len` unused block(s) number and mark it used.
45+
* Return 0 if no enough free block(s) were found.
4146
*/
42-
static inline uint32_t get_free_block(struct simplefs_sb_info *sbi)
47+
static inline uint32_t get_free_blocks(struct simplefs_sb_info *sbi,
48+
uint32_t len)
4349
{
44-
uint32_t ret = get_first_free_bit(sbi->bfree_bitmap, sbi->nr_blocks);
50+
uint32_t ret = get_first_free_bits(sbi->bfree_bitmap, sbi->nr_blocks, len);
4551
if (ret)
46-
sbi->nr_free_blocks--;
52+
sbi->nr_free_blocks -= len;
4753
return ret;
4854
}
4955

50-
/* Mark the i-th bit in freemap as free (i.e. 1) */
51-
static inline int put_free_bit(unsigned long *freemap,
52-
unsigned long size,
53-
uint32_t i)
56+
57+
/* Mark the `len` bit(s) from i-th bit in freemap as free (i.e. 1) */
58+
static inline int put_free_bits(unsigned long *freemap,
59+
unsigned long size,
60+
uint32_t i,
61+
uint32_t len)
5462
{
5563
/* i is greater than freemap size */
56-
if (i > size)
64+
if (i + len - 1 > size)
5765
return -1;
5866

59-
bitmap_set(freemap, i, 1);
67+
bitmap_set(freemap, i, len);
6068

6169
return 0;
6270
}
6371

6472
/* Mark an inode as unused */
6573
static inline void put_inode(struct simplefs_sb_info *sbi, uint32_t ino)
6674
{
67-
if (put_free_bit(sbi->ifree_bitmap, sbi->nr_inodes, ino))
75+
if (put_free_bits(sbi->ifree_bitmap, sbi->nr_inodes, ino, 1))
6876
return;
6977

7078
sbi->nr_free_inodes++;
7179
}
7280

73-
/* Mark a block as unused */
74-
static inline void put_block(struct simplefs_sb_info *sbi, uint32_t bno)
81+
/* Mark len block(s) as unused */
82+
static inline void put_blocks(struct simplefs_sb_info *sbi,
83+
uint32_t bno,
84+
uint32_t len)
7585
{
76-
if (put_free_bit(sbi->bfree_bitmap, sbi->nr_blocks, bno))
86+
if (put_free_bits(sbi->bfree_bitmap, sbi->nr_blocks, bno, len))
7787
return;
7888

79-
sbi->nr_free_blocks++;
89+
sbi->nr_free_blocks += len;
8090
}
8191

8292
#endif /* SIMPLEFS_BITMAP_H */

dir.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ static int simplefs_iterate(struct file *dir, struct dir_context *ctx)
3838
return 0;
3939

4040
/* Read the directory index block on disk */
41-
bh = sb_bread(sb, ci->index_block);
41+
bh = sb_bread(sb, ci->dir_block);
4242
if (!bh)
4343
return -EIO;
4444
dblock = (struct simplefs_dir_block *) bh->b_data;

extent.c

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <linux/fs.h>
2+
#include <linux/kernel.h>
3+
4+
#include "simplefs.h"
5+
6+
/*
7+
* Search the extent which contain the target block.
8+
* Retrun the first unused file index if not found.
9+
* Return -1 if it is out of range.
10+
* TODO: use binary search.
11+
*/
12+
uint32_t simplefs_ext_search(struct simplefs_file_ei_block *index,
13+
uint32_t iblock)
14+
{
15+
uint32_t i;
16+
for (i = 0; i < SIMPLEFS_MAX_EXTENTS; i++) {
17+
uint32_t block = index->extents[i].ee_block;
18+
uint32_t len = index->extents[i].ee_len;
19+
if (index->extents[i].ee_start == 0 ||
20+
(iblock >= block && iblock < block + len))
21+
return i;
22+
}
23+
return -1;
24+
}

file.c

+40-17
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,50 @@ static int simplefs_file_get_block(struct inode *inode,
2222
struct super_block *sb = inode->i_sb;
2323
struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb);
2424
struct simplefs_inode_info *ci = SIMPLEFS_INODE(inode);
25-
struct simplefs_file_index_block *index;
25+
struct simplefs_file_ei_block *index;
2626
struct buffer_head *bh_index;
2727
bool alloc = false;
2828
int ret = 0, bno;
29+
uint32_t extent;
2930

3031
/* If block number exceeds filesize, fail */
31-
if (iblock >= SIMPLEFS_BLOCK_SIZE >> 2)
32+
if (iblock >= SIMPLEFS_MAX_BLOCKS_PER_EXTENT * SIMPLEFS_MAX_EXTENTS)
3233
return -EFBIG;
3334

34-
/* Read index block from disk */
35-
bh_index = sb_bread(sb, ci->index_block);
35+
/* Read directory block from disk */
36+
bh_index = sb_bread(sb, ci->dir_block);
3637
if (!bh_index)
3738
return -EIO;
38-
index = (struct simplefs_file_index_block *) bh_index->b_data;
39+
index = (struct simplefs_file_ei_block *) bh_index->b_data;
40+
41+
extent = simplefs_ext_search(index, iblock);
42+
if (extent == -1) {
43+
ret = -EFBIG;
44+
goto brelse_index;
45+
}
3946

4047
/*
4148
* Check if iblock is already allocated. If not and create is true,
4249
* allocate it. Else, get the physical block number.
4350
*/
44-
if (index->blocks[iblock] == 0) {
51+
if (index->extents[extent].ee_start == 0) {
4552
if (!create)
4653
return 0;
47-
bno = get_free_block(sbi);
54+
bno = get_free_blocks(sbi, 8);
4855
if (!bno) {
4956
ret = -ENOSPC;
5057
goto brelse_index;
5158
}
52-
index->blocks[iblock] = bno;
59+
index->extents[extent].ee_start = bno;
60+
index->extents[extent].ee_len = 8;
61+
index->extents[extent].ee_block =
62+
extent ? index->extents[extent - 1].ee_block +
63+
index->extents[extent - 1].ee_len
64+
: 0;
5365
alloc = true;
5466
} else {
55-
bno = index->blocks[iblock];
67+
bno = index->extents[extent].ee_start + iblock -
68+
index->extents[extent].ee_block;
5669
}
5770

5871
/* Map the physical block to to the given buffer_head */
@@ -155,24 +168,33 @@ static int simplefs_write_end(struct file *file,
155168
if (nr_blocks_old > inode->i_blocks) {
156169
int i;
157170
struct buffer_head *bh_index;
158-
struct simplefs_file_index_block *index;
171+
struct simplefs_file_ei_block *index;
172+
uint32_t first_ext;
159173

160174
/* Free unused blocks from page cache */
161175
truncate_pagecache(inode, inode->i_size);
162176

163-
/* Read index block to remove unused blocks */
164-
bh_index = sb_bread(sb, ci->index_block);
177+
/* Read ei_block to remove unused blocks */
178+
bh_index = sb_bread(sb, ci->ei_block);
165179
if (!bh_index) {
166180
pr_err("failed truncating '%s'. we just lost %lu blocks\n",
167181
file->f_path.dentry->d_name.name,
168182
nr_blocks_old - inode->i_blocks);
169183
goto end;
170184
}
171-
index = (struct simplefs_file_index_block *) bh_index->b_data;
172-
173-
for (i = inode->i_blocks - 1; i < nr_blocks_old - 1; i++) {
174-
put_block(SIMPLEFS_SB(sb), index->blocks[i]);
175-
index->blocks[i] = 0;
185+
index = (struct simplefs_file_ei_block *) bh_index->b_data;
186+
187+
first_ext = simplefs_ext_search(index, inode->i_blocks - 1);
188+
/* Reserve unused block in last extent */
189+
if (inode->i_blocks - 1 != index->extents[first_ext].ee_block)
190+
first_ext++;
191+
192+
for (i = first_ext; i < SIMPLEFS_MAX_EXTENTS; i++) {
193+
if (!index->extents[i].ee_start)
194+
break;
195+
put_blocks(SIMPLEFS_SB(sb), index->extents[i].ee_start,
196+
index->extents[i].ee_len);
197+
memset(&index->extents[i], 0, sizeof(struct simplefs_extent));
176198
}
177199
mark_buffer_dirty(bh_index);
178200
brelse(bh_index);
@@ -193,4 +215,5 @@ const struct file_operations simplefs_file_ops = {
193215
.owner = THIS_MODULE,
194216
.read_iter = generic_file_read_iter,
195217
.write_iter = generic_file_write_iter,
218+
.fsync = generic_file_fsync,
196219
};

0 commit comments

Comments
 (0)