Skip to content

Commit

Permalink
block: Create bip slabs with embedded integrity vectors
Browse files Browse the repository at this point in the history
This patch restores stacking ability to the block layer integrity
infrastructure by creating a set of dedicated bip slabs.  Each bip slab
has an embedded bio_vec array at the end.  This cuts down on memory
allocations and also simplifies the code compared to the original bvec
version.  Only the largest bip slab is backed by a mempool.  The pool is
contained in the bio_set so stacking drivers can ensure forward
progress.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Jens Axboe <axboe@carl.(none)>
  • Loading branch information
martinkpetersen authored and Jens Axboe committed Jul 1, 2009
1 parent 6118b70 commit 7878cba
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 57 deletions.
2 changes: 1 addition & 1 deletion block/blk-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2365,7 +2365,7 @@ int blk_rq_prep_clone(struct request *rq, struct request *rq_src,
__bio_clone(bio, bio_src);

if (bio_integrity(bio_src) &&
bio_integrity_clone(bio, bio_src, gfp_mask))
bio_integrity_clone(bio, bio_src, gfp_mask, bs))
goto free_and_out;

if (bio_ctr && bio_ctr(bio, bio_src, data))
Expand Down
4 changes: 2 additions & 2 deletions drivers/md/dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector,
clone->bi_flags |= 1 << BIO_CLONED;

if (bio_integrity(bio)) {
bio_integrity_clone(clone, bio, GFP_NOIO);
bio_integrity_clone(clone, bio, GFP_NOIO, bs);
bio_integrity_trim(clone,
bio_sector_offset(bio, idx, offset), len);
}
Expand Down Expand Up @@ -1045,7 +1045,7 @@ static struct bio *clone_bio(struct bio *bio, sector_t sector,
clone->bi_flags &= ~(1 << BIO_SEG_VALID);

if (bio_integrity(bio)) {
bio_integrity_clone(clone, bio, GFP_NOIO);
bio_integrity_clone(clone, bio, GFP_NOIO, bs);

if (idx != bio->bi_idx || clone->bi_size < bio->bi_size)
bio_integrity_trim(clone,
Expand Down
170 changes: 125 additions & 45 deletions fs/bio-integrity.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* bio-integrity.c - bio data integrity extensions
*
* Copyright (C) 2007, 2008 Oracle Corporation
* Copyright (C) 2007, 2008, 2009 Oracle Corporation
* Written by: Martin K. Petersen <martin.petersen@oracle.com>
*
* This program is free software; you can redistribute it and/or
Expand All @@ -25,63 +25,121 @@
#include <linux/bio.h>
#include <linux/workqueue.h>

static struct kmem_cache *bio_integrity_slab __read_mostly;
static mempool_t *bio_integrity_pool;
static struct bio_set *integrity_bio_set;
struct integrity_slab {
struct kmem_cache *slab;
unsigned short nr_vecs;
char name[8];
};

#define IS(x) { .nr_vecs = x, .name = "bip-"__stringify(x) }
struct integrity_slab bip_slab[BIOVEC_NR_POOLS] __read_mostly = {
IS(1), IS(4), IS(16), IS(64), IS(128), IS(BIO_MAX_PAGES),
};
#undef IS

static struct workqueue_struct *kintegrityd_wq;

static inline unsigned int vecs_to_idx(unsigned int nr)
{
switch (nr) {
case 1:
return 0;
case 2 ... 4:
return 1;
case 5 ... 16:
return 2;
case 17 ... 64:
return 3;
case 65 ... 128:
return 4;
case 129 ... BIO_MAX_PAGES:
return 5;
default:
BUG();
}
}

static inline int use_bip_pool(unsigned int idx)
{
if (idx == BIOVEC_NR_POOLS)
return 1;

return 0;
}

/**
* bio_integrity_alloc - Allocate integrity payload and attach it to bio
* bio_integrity_alloc_bioset - Allocate integrity payload and attach it to bio
* @bio: bio to attach integrity metadata to
* @gfp_mask: Memory allocation mask
* @nr_vecs: Number of integrity metadata scatter-gather elements
* @bs: bio_set to allocate from
*
* Description: This function prepares a bio for attaching integrity
* metadata. nr_vecs specifies the maximum number of pages containing
* integrity metadata that can be attached.
*/
struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio,
gfp_t gfp_mask,
unsigned int nr_vecs)
struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *bio,
gfp_t gfp_mask,
unsigned int nr_vecs,
struct bio_set *bs)
{
struct bio_integrity_payload *bip;
struct bio_vec *iv;
unsigned long idx;
unsigned int idx = vecs_to_idx(nr_vecs);

BUG_ON(bio == NULL);
bip = NULL;

bip = mempool_alloc(bio_integrity_pool, gfp_mask);
if (unlikely(bip == NULL)) {
printk(KERN_ERR "%s: could not alloc bip\n", __func__);
return NULL;
}
/* Lower order allocations come straight from slab */
if (!use_bip_pool(idx))
bip = kmem_cache_alloc(bip_slab[idx].slab, gfp_mask);

memset(bip, 0, sizeof(*bip));
/* Use mempool if lower order alloc failed or max vecs were requested */
if (bip == NULL) {
bip = mempool_alloc(bs->bio_integrity_pool, gfp_mask);

iv = bvec_alloc_bs(gfp_mask, nr_vecs, &idx, integrity_bio_set);
if (unlikely(iv == NULL)) {
printk(KERN_ERR "%s: could not alloc bip_vec\n", __func__);
mempool_free(bip, bio_integrity_pool);
return NULL;
if (unlikely(bip == NULL)) {
printk(KERN_ERR "%s: could not alloc bip\n", __func__);
return NULL;
}
}

bip->bip_pool = idx;
bip->bip_vec = iv;
memset(bip, 0, sizeof(*bip));

bip->bip_slab = idx;
bip->bip_bio = bio;
bio->bi_integrity = bip;

return bip;
}
EXPORT_SYMBOL(bio_integrity_alloc_bioset);

/**
* bio_integrity_alloc - Allocate integrity payload and attach it to bio
* @bio: bio to attach integrity metadata to
* @gfp_mask: Memory allocation mask
* @nr_vecs: Number of integrity metadata scatter-gather elements
*
* Description: This function prepares a bio for attaching integrity
* metadata. nr_vecs specifies the maximum number of pages containing
* integrity metadata that can be attached.
*/
struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio,
gfp_t gfp_mask,
unsigned int nr_vecs)
{
return bio_integrity_alloc_bioset(bio, gfp_mask, nr_vecs, fs_bio_set);
}
EXPORT_SYMBOL(bio_integrity_alloc);

/**
* bio_integrity_free - Free bio integrity payload
* @bio: bio containing bip to be freed
* @bs: bio_set this bio was allocated from
*
* Description: Used to free the integrity portion of a bio. Usually
* called from bio_free().
*/
void bio_integrity_free(struct bio *bio)
void bio_integrity_free(struct bio *bio, struct bio_set *bs)
{
struct bio_integrity_payload *bip = bio->bi_integrity;

Expand All @@ -92,8 +150,10 @@ void bio_integrity_free(struct bio *bio)
&& bip->bip_buf != NULL)
kfree(bip->bip_buf);

bvec_free_bs(integrity_bio_set, bip->bip_vec, bip->bip_pool);
mempool_free(bip, bio_integrity_pool);
if (use_bip_pool(bip->bip_slab))
mempool_free(bip, bs->bio_integrity_pool);
else
kmem_cache_free(bip_slab[bip->bip_slab].slab, bip);

bio->bi_integrity = NULL;
}
Expand All @@ -114,7 +174,7 @@ int bio_integrity_add_page(struct bio *bio, struct page *page,
struct bio_integrity_payload *bip = bio->bi_integrity;
struct bio_vec *iv;

if (bip->bip_vcnt >= bvec_nr_vecs(bip->bip_pool)) {
if (bip->bip_vcnt >= bvec_nr_vecs(bip->bip_slab)) {
printk(KERN_ERR "%s: bip_vec full\n", __func__);
return 0;
}
Expand Down Expand Up @@ -647,8 +707,8 @@ void bio_integrity_split(struct bio *bio, struct bio_pair *bp, int sectors)
bp->iv1 = bip->bip_vec[0];
bp->iv2 = bip->bip_vec[0];

bp->bip1.bip_vec = &bp->iv1;
bp->bip2.bip_vec = &bp->iv2;
bp->bip1.bip_vec[0] = bp->iv1;
bp->bip2.bip_vec[0] = bp->iv2;

bp->iv1.bv_len = sectors * bi->tuple_size;
bp->iv2.bv_offset += sectors * bi->tuple_size;
Expand All @@ -667,17 +727,19 @@ EXPORT_SYMBOL(bio_integrity_split);
* @bio: New bio
* @bio_src: Original bio
* @gfp_mask: Memory allocation mask
* @bs: bio_set to allocate bip from
*
* Description: Called to allocate a bip when cloning a bio
*/
int bio_integrity_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp_mask)
int bio_integrity_clone(struct bio *bio, struct bio *bio_src,
gfp_t gfp_mask, struct bio_set *bs)
{
struct bio_integrity_payload *bip_src = bio_src->bi_integrity;
struct bio_integrity_payload *bip;

BUG_ON(bip_src == NULL);

bip = bio_integrity_alloc(bio, gfp_mask, bip_src->bip_vcnt);
bip = bio_integrity_alloc_bioset(bio, gfp_mask, bip_src->bip_vcnt, bs);

if (bip == NULL)
return -EIO;
Expand All @@ -693,25 +755,43 @@ int bio_integrity_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp_mask)
}
EXPORT_SYMBOL(bio_integrity_clone);

static int __init bio_integrity_init(void)
int bioset_integrity_create(struct bio_set *bs, int pool_size)
{
kintegrityd_wq = create_workqueue("kintegrityd");
unsigned int max_slab = vecs_to_idx(BIO_MAX_PAGES);

bs->bio_integrity_pool =
mempool_create_slab_pool(pool_size, bip_slab[max_slab].slab);

if (!bs->bio_integrity_pool)
return -1;

return 0;
}
EXPORT_SYMBOL(bioset_integrity_create);

void bioset_integrity_free(struct bio_set *bs)
{
if (bs->bio_integrity_pool)
mempool_destroy(bs->bio_integrity_pool);
}
EXPORT_SYMBOL(bioset_integrity_free);

void __init bio_integrity_init(void)
{
unsigned int i;

kintegrityd_wq = create_workqueue("kintegrityd");
if (!kintegrityd_wq)
panic("Failed to create kintegrityd\n");

bio_integrity_slab = KMEM_CACHE(bio_integrity_payload,
SLAB_HWCACHE_ALIGN|SLAB_PANIC);
for (i = 0 ; i < BIOVEC_NR_POOLS ; i++) {
unsigned int size;

bio_integrity_pool = mempool_create_slab_pool(BIO_POOL_SIZE,
bio_integrity_slab);
if (!bio_integrity_pool)
panic("bio_integrity: can't allocate bip pool\n");
size = sizeof(struct bio_integrity_payload)
+ bip_slab[i].nr_vecs * sizeof(struct bio_vec);

integrity_bio_set = bioset_create(BIO_POOL_SIZE, 0);
if (!integrity_bio_set)
panic("bio_integrity: can't allocate bio_set\n");

return 0;
bip_slab[i].slab =
kmem_cache_create(bip_slab[i].name, size, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
}
}
subsys_initcall(bio_integrity_init);
11 changes: 8 additions & 3 deletions fs/bio.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ void bio_free(struct bio *bio, struct bio_set *bs)
bvec_free_bs(bs, bio->bi_io_vec, BIO_POOL_IDX(bio));

if (bio_integrity(bio))
bio_integrity_free(bio);
bio_integrity_free(bio, bs);

/*
* If we have front padding, adjust the bio pointer before freeing
Expand Down Expand Up @@ -341,7 +341,7 @@ struct bio *bio_alloc(gfp_t gfp_mask, int nr_iovecs)
static void bio_kmalloc_destructor(struct bio *bio)
{
if (bio_integrity(bio))
bio_integrity_free(bio);
bio_integrity_free(bio, fs_bio_set);
kfree(bio);
}

Expand Down Expand Up @@ -472,7 +472,7 @@ struct bio *bio_clone(struct bio *bio, gfp_t gfp_mask)
if (bio_integrity(bio)) {
int ret;

ret = bio_integrity_clone(b, bio, gfp_mask);
ret = bio_integrity_clone(b, bio, gfp_mask, fs_bio_set);

if (ret < 0) {
bio_put(b);
Expand Down Expand Up @@ -1539,6 +1539,7 @@ void bioset_free(struct bio_set *bs)
if (bs->bio_pool)
mempool_destroy(bs->bio_pool);

bioset_integrity_free(bs);
biovec_free_pools(bs);
bio_put_slab(bs);

Expand Down Expand Up @@ -1579,6 +1580,9 @@ struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad)
if (!bs->bio_pool)
goto bad;

if (bioset_integrity_create(bs, pool_size))
goto bad;

if (!biovec_create_pools(bs, pool_size))
return bs;

Expand Down Expand Up @@ -1616,6 +1620,7 @@ static int __init init_bio(void)
if (!bio_slabs)
panic("bio: can't allocate bios\n");

bio_integrity_init();
biovec_init_slabs();

fs_bio_set = bioset_create(BIO_POOL_SIZE, 0);
Expand Down
Loading

0 comments on commit 7878cba

Please sign in to comment.