Skip to content

Commit 93e72b3

Browse files
Philippe Liardtorvalds
authored andcommitted
squashfs: migrate from ll_rw_block usage to BIO
ll_rw_block() function has been deprecated in favor of BIO which appears to come with large performance improvements. This patch decreases boot time by close to 40% when using squashfs for the root file-system. This is observed at least in the context of starting an Android VM on Chrome OS using crosvm. The patch was tested on 4.19 as well as master. This patch is largely based on Adrien Schildknecht's patch that was originally sent as https://lkml.org/lkml/2017/9/22/814 though with some significant changes and simplifications while also taking Phillip Lougher's feedback into account, around preserving support for FILE_CACHE in particular. [[email protected]: fix build error reported by Randy] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Philippe Liard <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Cc: Adrien Schildknecht <[email protected]> Cc: Phillip Lougher <[email protected]> Cc: Guenter Roeck <[email protected]> Cc: Daniel Rosenberg <[email protected]> Link: https://chromium.googlesource.com/chromiumos/platform/crosvm Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9bf9511 commit 93e72b3

File tree

11 files changed

+287
-242
lines changed

11 files changed

+287
-242
lines changed

fs/squashfs/block.c

Lines changed: 146 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* datablocks and metadata blocks.
1414
*/
1515

16+
#include <linux/blkdev.h>
1617
#include <linux/fs.h>
1718
#include <linux/vfs.h>
1819
#include <linux/slab.h>
@@ -27,44 +28,103 @@
2728
#include "page_actor.h"
2829

2930
/*
30-
* Read the metadata block length, this is stored in the first two
31-
* bytes of the metadata block.
31+
* Returns the amount of bytes copied to the page actor.
3232
*/
33-
static struct buffer_head *get_block_length(struct super_block *sb,
34-
u64 *cur_index, int *offset, int *length)
33+
static int copy_bio_to_actor(struct bio *bio,
34+
struct squashfs_page_actor *actor,
35+
int offset, int req_length)
36+
{
37+
void *actor_addr = squashfs_first_page(actor);
38+
struct bvec_iter_all iter_all = {};
39+
struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
40+
int copied_bytes = 0;
41+
int actor_offset = 0;
42+
43+
if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all)))
44+
return 0;
45+
46+
while (copied_bytes < req_length) {
47+
int bytes_to_copy = min_t(int, bvec->bv_len - offset,
48+
PAGE_SIZE - actor_offset);
49+
50+
bytes_to_copy = min_t(int, bytes_to_copy,
51+
req_length - copied_bytes);
52+
memcpy(actor_addr + actor_offset,
53+
page_address(bvec->bv_page) + bvec->bv_offset + offset,
54+
bytes_to_copy);
55+
56+
actor_offset += bytes_to_copy;
57+
copied_bytes += bytes_to_copy;
58+
offset += bytes_to_copy;
59+
60+
if (actor_offset >= PAGE_SIZE) {
61+
actor_addr = squashfs_next_page(actor);
62+
if (!actor_addr)
63+
break;
64+
actor_offset = 0;
65+
}
66+
if (offset >= bvec->bv_len) {
67+
if (!bio_next_segment(bio, &iter_all))
68+
break;
69+
offset = 0;
70+
}
71+
}
72+
squashfs_finish_page(actor);
73+
return copied_bytes;
74+
}
75+
76+
static int squashfs_bio_read(struct super_block *sb, u64 index, int length,
77+
struct bio **biop, int *block_offset)
3578
{
3679
struct squashfs_sb_info *msblk = sb->s_fs_info;
37-
struct buffer_head *bh;
38-
39-
bh = sb_bread(sb, *cur_index);
40-
if (bh == NULL)
41-
return NULL;
42-
43-
if (msblk->devblksize - *offset == 1) {
44-
*length = (unsigned char) bh->b_data[*offset];
45-
put_bh(bh);
46-
bh = sb_bread(sb, ++(*cur_index));
47-
if (bh == NULL)
48-
return NULL;
49-
*length |= (unsigned char) bh->b_data[0] << 8;
50-
*offset = 1;
51-
} else {
52-
*length = (unsigned char) bh->b_data[*offset] |
53-
(unsigned char) bh->b_data[*offset + 1] << 8;
54-
*offset += 2;
55-
56-
if (*offset == msblk->devblksize) {
57-
put_bh(bh);
58-
bh = sb_bread(sb, ++(*cur_index));
59-
if (bh == NULL)
60-
return NULL;
61-
*offset = 0;
80+
const u64 read_start = round_down(index, msblk->devblksize);
81+
const sector_t block = read_start >> msblk->devblksize_log2;
82+
const u64 read_end = round_up(index + length, msblk->devblksize);
83+
const sector_t block_end = read_end >> msblk->devblksize_log2;
84+
int offset = read_start - round_down(index, PAGE_SIZE);
85+
int total_len = (block_end - block) << msblk->devblksize_log2;
86+
const int page_count = DIV_ROUND_UP(total_len + offset, PAGE_SIZE);
87+
int error, i;
88+
struct bio *bio;
89+
90+
bio = bio_alloc(GFP_NOIO, page_count);
91+
if (!bio)
92+
return -ENOMEM;
93+
94+
bio_set_dev(bio, sb->s_bdev);
95+
bio->bi_opf = READ;
96+
bio->bi_iter.bi_sector = block * (msblk->devblksize >> SECTOR_SHIFT);
97+
98+
for (i = 0; i < page_count; ++i) {
99+
unsigned int len =
100+
min_t(unsigned int, PAGE_SIZE - offset, total_len);
101+
struct page *page = alloc_page(GFP_NOIO);
102+
103+
if (!page) {
104+
error = -ENOMEM;
105+
goto out_free_bio;
106+
}
107+
if (!bio_add_page(bio, page, len, offset)) {
108+
error = -EIO;
109+
goto out_free_bio;
62110
}
111+
offset = 0;
112+
total_len -= len;
63113
}
64114

65-
return bh;
66-
}
115+
error = submit_bio_wait(bio);
116+
if (error)
117+
goto out_free_bio;
67118

119+
*biop = bio;
120+
*block_offset = index & ((1 << msblk->devblksize_log2) - 1);
121+
return 0;
122+
123+
out_free_bio:
124+
bio_free_pages(bio);
125+
bio_put(bio);
126+
return error;
127+
}
68128

69129
/*
70130
* Read and decompress a metadata block or datablock. Length is non-zero
@@ -76,129 +136,88 @@ static struct buffer_head *get_block_length(struct super_block *sb,
76136
* algorithms).
77137
*/
78138
int squashfs_read_data(struct super_block *sb, u64 index, int length,
79-
u64 *next_index, struct squashfs_page_actor *output)
139+
u64 *next_index, struct squashfs_page_actor *output)
80140
{
81141
struct squashfs_sb_info *msblk = sb->s_fs_info;
82-
struct buffer_head **bh;
83-
int offset = index & ((1 << msblk->devblksize_log2) - 1);
84-
u64 cur_index = index >> msblk->devblksize_log2;
85-
int bytes, compressed, b = 0, k = 0, avail, i;
86-
87-
bh = kcalloc(((output->length + msblk->devblksize - 1)
88-
>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
89-
if (bh == NULL)
90-
return -ENOMEM;
142+
struct bio *bio = NULL;
143+
int compressed;
144+
int res;
145+
int offset;
91146

92147
if (length) {
93148
/*
94149
* Datablock.
95150
*/
96-
bytes = -offset;
97151
compressed = SQUASHFS_COMPRESSED_BLOCK(length);
98152
length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
99-
if (next_index)
100-
*next_index = index + length;
101-
102153
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
103154
index, compressed ? "" : "un", length, output->length);
104-
105-
if (length < 0 || length > output->length ||
106-
(index + length) > msblk->bytes_used)
107-
goto read_failure;
108-
109-
for (b = 0; bytes < length; b++, cur_index++) {
110-
bh[b] = sb_getblk(sb, cur_index);
111-
if (bh[b] == NULL)
112-
goto block_release;
113-
bytes += msblk->devblksize;
114-
}
115-
ll_rw_block(REQ_OP_READ, 0, b, bh);
116155
} else {
117156
/*
118157
* Metadata block.
119158
*/
120-
if ((index + 2) > msblk->bytes_used)
121-
goto read_failure;
159+
const u8 *data;
160+
struct bvec_iter_all iter_all = {};
161+
struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
122162

123-
bh[0] = get_block_length(sb, &cur_index, &offset, &length);
124-
if (bh[0] == NULL)
125-
goto read_failure;
126-
b = 1;
163+
if (index + 2 > msblk->bytes_used) {
164+
res = -EIO;
165+
goto out;
166+
}
167+
res = squashfs_bio_read(sb, index, 2, &bio, &offset);
168+
if (res)
169+
goto out;
170+
171+
if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) {
172+
res = -EIO;
173+
goto out_free_bio;
174+
}
175+
/* Extract the length of the metadata block */
176+
data = page_address(bvec->bv_page) + bvec->bv_offset;
177+
length = data[offset];
178+
if (offset <= bvec->bv_len - 1) {
179+
length |= data[offset + 1] << 8;
180+
} else {
181+
if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) {
182+
res = -EIO;
183+
goto out_free_bio;
184+
}
185+
data = page_address(bvec->bv_page) + bvec->bv_offset;
186+
length |= data[0] << 8;
187+
}
188+
bio_free_pages(bio);
189+
bio_put(bio);
127190

128-
bytes = msblk->devblksize - offset;
129191
compressed = SQUASHFS_COMPRESSED(length);
130192
length = SQUASHFS_COMPRESSED_SIZE(length);
131-
if (next_index)
132-
*next_index = index + length + 2;
193+
index += 2;
133194

134195
TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
135-
compressed ? "" : "un", length);
136-
137-
if (length < 0 || length > output->length ||
138-
(index + length) > msblk->bytes_used)
139-
goto block_release;
140-
141-
for (; bytes < length; b++) {
142-
bh[b] = sb_getblk(sb, ++cur_index);
143-
if (bh[b] == NULL)
144-
goto block_release;
145-
bytes += msblk->devblksize;
146-
}
147-
ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
196+
compressed ? "" : "un", length);
148197
}
198+
if (next_index)
199+
*next_index = index + length;
149200

150-
for (i = 0; i < b; i++) {
151-
wait_on_buffer(bh[i]);
152-
if (!buffer_uptodate(bh[i]))
153-
goto block_release;
154-
}
201+
res = squashfs_bio_read(sb, index, length, &bio, &offset);
202+
if (res)
203+
goto out;
155204

156205
if (compressed) {
157-
if (!msblk->stream)
158-
goto read_failure;
159-
length = squashfs_decompress(msblk, bh, b, offset, length,
160-
output);
161-
if (length < 0)
162-
goto read_failure;
163-
} else {
164-
/*
165-
* Block is uncompressed.
166-
*/
167-
int in, pg_offset = 0;
168-
void *data = squashfs_first_page(output);
169-
170-
for (bytes = length; k < b; k++) {
171-
in = min(bytes, msblk->devblksize - offset);
172-
bytes -= in;
173-
while (in) {
174-
if (pg_offset == PAGE_SIZE) {
175-
data = squashfs_next_page(output);
176-
pg_offset = 0;
177-
}
178-
avail = min_t(int, in, PAGE_SIZE -
179-
pg_offset);
180-
memcpy(data + pg_offset, bh[k]->b_data + offset,
181-
avail);
182-
in -= avail;
183-
pg_offset += avail;
184-
offset += avail;
185-
}
186-
offset = 0;
187-
put_bh(bh[k]);
206+
if (!msblk->stream) {
207+
res = -EIO;
208+
goto out_free_bio;
188209
}
189-
squashfs_finish_page(output);
210+
res = squashfs_decompress(msblk, bio, offset, length, output);
211+
} else {
212+
res = copy_bio_to_actor(bio, output, offset, length);
190213
}
191214

192-
kfree(bh);
193-
return length;
194-
195-
block_release:
196-
for (; k < b; k++)
197-
put_bh(bh[k]);
215+
out_free_bio:
216+
bio_free_pages(bio);
217+
bio_put(bio);
218+
out:
219+
if (res < 0)
220+
ERROR("Failed to read block 0x%llx: %d\n", index, res);
198221

199-
read_failure:
200-
ERROR("squashfs_read_data failed to read block 0x%llx\n",
201-
(unsigned long long) index);
202-
kfree(bh);
203-
return -EIO;
222+
return res;
204223
}

fs/squashfs/decompressor.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
* decompressor.h
1111
*/
1212

13+
#include <linux/bio.h>
14+
1315
struct squashfs_decompressor {
1416
void *(*init)(struct squashfs_sb_info *, void *);
1517
void *(*comp_opts)(struct squashfs_sb_info *, void *, int);
1618
void (*free)(void *);
1719
int (*decompress)(struct squashfs_sb_info *, void *,
18-
struct buffer_head **, int, int, int,
19-
struct squashfs_page_actor *);
20+
struct bio *, int, int, struct squashfs_page_actor *);
2021
int id;
2122
char *name;
2223
int supported;

fs/squashfs/decompressor_multi.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <linux/types.h>
77
#include <linux/mutex.h>
88
#include <linux/slab.h>
9-
#include <linux/buffer_head.h>
9+
#include <linux/bio.h>
1010
#include <linux/sched.h>
1111
#include <linux/wait.h>
1212
#include <linux/cpumask.h>
@@ -180,14 +180,15 @@ static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk,
180180
}
181181

182182

183-
int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
184-
int b, int offset, int length, struct squashfs_page_actor *output)
183+
int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
184+
int offset, int length,
185+
struct squashfs_page_actor *output)
185186
{
186187
int res;
187188
struct squashfs_stream *stream = msblk->stream;
188189
struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
189190
res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
190-
bh, b, offset, length, output);
191+
bio, offset, length, output);
191192
put_decomp_stream(decomp_stream, stream);
192193
if (res < 0)
193194
ERROR("%s decompression failed, data probably corrupt\n",

fs/squashfs/decompressor_multi_percpu.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,17 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
7272
}
7373
}
7474

75-
int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
76-
int b, int offset, int length, struct squashfs_page_actor *output)
75+
int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
76+
int offset, int length, struct squashfs_page_actor *output)
7777
{
78-
struct squashfs_stream __percpu *percpu =
79-
(struct squashfs_stream __percpu *) msblk->stream;
80-
struct squashfs_stream *stream = get_cpu_ptr(percpu);
81-
int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
82-
offset, length, output);
78+
struct squashfs_stream __percpu *percpu;
79+
struct squashfs_stream *stream;
80+
int res;
81+
82+
percpu = (struct squashfs_stream __percpu *)msblk->stream;
83+
stream = get_cpu_ptr(percpu);
84+
res = msblk->decompressor->decompress(msblk, stream->stream, bio,
85+
offset, length, output);
8386
put_cpu_ptr(stream);
8487

8588
if (res < 0)

0 commit comments

Comments
 (0)