Skip to content

Commit 3d74a75

Browse files
adam900710kdave
authored andcommitted
btrfs: zlib: introduce zlib_compress_bio() helper
The new helper has the following enhancements against the existing zlib_compress_folios() - Much smaller parameter list No more shared IN/OUT members, no need to pre-allocate a compressed_folios[] array. Just a workspace and compressed_bio pointer, everything we need can be extracted from that @cb pointer. - Ready-to-be-submitted compressed bio Although the caller still needs to do some common works like rounding up and zeroing the tailing part of the last fs block. Reviewed-by: Boris Burkov <boris@bur.io> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent bba9596 commit 3d74a75

2 files changed

Lines changed: 195 additions & 0 deletions

File tree

fs/btrfs/compression.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ int btrfs_compress_filemap_get_folio(struct address_space *mapping, u64 start,
150150
int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode,
151151
u64 start, struct folio **folios, unsigned long *out_folios,
152152
unsigned long *total_in, unsigned long *total_out);
153+
int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb);
153154
int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb);
154155
int zlib_decompress(struct list_head *ws, const u8 *data_in,
155156
struct folio *dest_folio, unsigned long dest_pgoff, size_t srclen,

fs/btrfs/zlib.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,200 @@ int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode,
334334
return ret;
335335
}
336336

337+
int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb)
338+
{
339+
struct btrfs_inode *inode = cb->bbio.inode;
340+
struct btrfs_fs_info *fs_info = inode->root->fs_info;
341+
struct workspace *workspace = list_entry(ws, struct workspace, list);
342+
struct address_space *mapping = inode->vfs_inode.i_mapping;
343+
struct bio *bio = &cb->bbio.bio;
344+
u64 start = cb->start;
345+
u32 len = cb->len;
346+
const u32 min_folio_size = btrfs_min_folio_size(fs_info);
347+
int ret;
348+
char *data_in = NULL;
349+
char *cfolio_out;
350+
struct folio *in_folio = NULL;
351+
struct folio *out_folio = NULL;
352+
const u32 blocksize = fs_info->sectorsize;
353+
const u64 orig_end = start + len;
354+
355+
ret = zlib_deflateInit(&workspace->strm, workspace->level);
356+
if (unlikely(ret != Z_OK)) {
357+
btrfs_err(fs_info,
358+
"zlib compression init failed, error %d root %llu inode %llu offset %llu",
359+
ret, btrfs_root_id(inode->root), btrfs_ino(inode), start);
360+
ret = -EIO;
361+
goto out;
362+
}
363+
364+
workspace->strm.total_in = 0;
365+
workspace->strm.total_out = 0;
366+
367+
out_folio = btrfs_alloc_compr_folio(fs_info);
368+
if (out_folio == NULL) {
369+
ret = -ENOMEM;
370+
goto out;
371+
}
372+
cfolio_out = folio_address(out_folio);
373+
374+
workspace->strm.next_in = workspace->buf;
375+
workspace->strm.avail_in = 0;
376+
workspace->strm.next_out = cfolio_out;
377+
workspace->strm.avail_out = min_folio_size;
378+
379+
while (workspace->strm.total_in < len) {
380+
/*
381+
* Get next input pages and copy the contents to the workspace
382+
* buffer if required.
383+
*/
384+
if (workspace->strm.avail_in == 0) {
385+
unsigned long bytes_left = len - workspace->strm.total_in;
386+
unsigned int copy_length = min(bytes_left, workspace->buf_size);
387+
388+
/*
389+
* For s390 hardware accelerated zlib, and our folio is smaller
390+
* than the copy_length, we need to fill the buffer so that
391+
* we can take full advantage of hardware acceleration.
392+
*/
393+
if (need_special_buffer(fs_info)) {
394+
ret = copy_data_into_buffer(mapping, workspace,
395+
start, copy_length);
396+
if (ret < 0)
397+
goto out;
398+
start += copy_length;
399+
workspace->strm.next_in = workspace->buf;
400+
workspace->strm.avail_in = copy_length;
401+
} else {
402+
unsigned int cur_len;
403+
404+
if (data_in) {
405+
kunmap_local(data_in);
406+
folio_put(in_folio);
407+
data_in = NULL;
408+
}
409+
ret = btrfs_compress_filemap_get_folio(mapping,
410+
start, &in_folio);
411+
if (ret < 0)
412+
goto out;
413+
cur_len = btrfs_calc_input_length(in_folio, orig_end, start);
414+
data_in = kmap_local_folio(in_folio,
415+
offset_in_folio(in_folio, start));
416+
start += cur_len;
417+
workspace->strm.next_in = data_in;
418+
workspace->strm.avail_in = cur_len;
419+
}
420+
}
421+
422+
ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH);
423+
if (unlikely(ret != Z_OK)) {
424+
btrfs_warn(fs_info,
425+
"zlib compression failed, error %d root %llu inode %llu offset %llu",
426+
ret, btrfs_root_id(inode->root), btrfs_ino(inode),
427+
start);
428+
zlib_deflateEnd(&workspace->strm);
429+
ret = -EIO;
430+
goto out;
431+
}
432+
433+
/* We're making it bigger, give up. */
434+
if (workspace->strm.total_in > blocksize * 2 &&
435+
workspace->strm.total_in < workspace->strm.total_out) {
436+
ret = -E2BIG;
437+
goto out;
438+
}
439+
if (workspace->strm.total_out >= len) {
440+
ret = -E2BIG;
441+
goto out;
442+
}
443+
/* Queue the full folio and allocate a new one. */
444+
if (workspace->strm.avail_out == 0) {
445+
if (!bio_add_folio(bio, out_folio, folio_size(out_folio), 0)) {
446+
ret = -E2BIG;
447+
goto out;
448+
}
449+
450+
out_folio = btrfs_alloc_compr_folio(fs_info);
451+
if (out_folio == NULL) {
452+
ret = -ENOMEM;
453+
goto out;
454+
}
455+
cfolio_out = folio_address(out_folio);
456+
workspace->strm.avail_out = min_folio_size;
457+
workspace->strm.next_out = cfolio_out;
458+
}
459+
/* We're all done. */
460+
if (workspace->strm.total_in >= len)
461+
break;
462+
}
463+
464+
workspace->strm.avail_in = 0;
465+
466+
/*
467+
* Call deflate with Z_FINISH flush parameter providing more output
468+
* space but no more input data, until it returns with Z_STREAM_END.
469+
*/
470+
while (ret != Z_STREAM_END) {
471+
ret = zlib_deflate(&workspace->strm, Z_FINISH);
472+
if (ret == Z_STREAM_END)
473+
break;
474+
if (unlikely(ret != Z_OK && ret != Z_BUF_ERROR)) {
475+
zlib_deflateEnd(&workspace->strm);
476+
ret = -EIO;
477+
goto out;
478+
} else if (workspace->strm.avail_out == 0) {
479+
if (workspace->strm.total_out >= len) {
480+
ret = -E2BIG;
481+
goto out;
482+
}
483+
if (!bio_add_folio(bio, out_folio, folio_size(out_folio), 0)) {
484+
ret = -E2BIG;
485+
goto out;
486+
}
487+
/* Get another folio for the stream end. */
488+
out_folio = btrfs_alloc_compr_folio(fs_info);
489+
if (out_folio == NULL) {
490+
ret = -ENOMEM;
491+
goto out;
492+
}
493+
cfolio_out = folio_address(out_folio);
494+
workspace->strm.avail_out = min_folio_size;
495+
workspace->strm.next_out = cfolio_out;
496+
}
497+
}
498+
/* Queue the remaining part of the folio. */
499+
if (workspace->strm.total_out > bio->bi_iter.bi_size) {
500+
u32 cur_len = offset_in_folio(out_folio, workspace->strm.total_out);
501+
502+
if (!bio_add_folio(bio, out_folio, cur_len, 0)) {
503+
ret = -E2BIG;
504+
goto out;
505+
}
506+
} else {
507+
/* The last folio hasn't' been utilized. */
508+
btrfs_free_compr_folio(out_folio);
509+
}
510+
out_folio = NULL;
511+
ASSERT(bio->bi_iter.bi_size == workspace->strm.total_out);
512+
zlib_deflateEnd(&workspace->strm);
513+
514+
if (workspace->strm.total_out >= workspace->strm.total_in) {
515+
ret = -E2BIG;
516+
goto out;
517+
}
518+
519+
ret = 0;
520+
out:
521+
if (out_folio)
522+
btrfs_free_compr_folio(out_folio);
523+
if (data_in) {
524+
kunmap_local(data_in);
525+
folio_put(in_folio);
526+
}
527+
528+
return ret;
529+
}
530+
337531
int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
338532
{
339533
struct btrfs_fs_info *fs_info = cb_to_fs_info(cb);

0 commit comments

Comments
 (0)