Merge branch 'for-2.6.27' of git://git.infradead.org/users/dwmw2/firmware-2.6
[pandora-kernel.git] / fs / bio.c
index 6e0b6f6..88322b0 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
 #include <linux/blktrace_api.h>
 #include <scsi/sg.h>           /* for struct sg_iovec */
 
-#define BIO_POOL_SIZE 2
-
 static struct kmem_cache *bio_slab __read_mostly;
 
-#define BIOVEC_NR_POOLS 6
-
-/*
- * a small number of entries is fine, not going to be performance critical.
- * basically we just need to survive
- */
-#define BIO_SPLIT_ENTRIES 2
 mempool_t *bio_split_pool __read_mostly;
 
-struct biovec_slab {
-       int nr_vecs;
-       char *name; 
-       struct kmem_cache *slab;
-};
-
 /*
  * if you change this list, also change bvec_alloc or things will
  * break badly! cannot be bigger than what you can fit into an
@@ -59,24 +44,18 @@ static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = {
 };
 #undef BV
 
-/*
- * bio_set is used to allow other portions of the IO system to
- * allocate their own private memory pools for bio and iovec structures.
- * These memory pools in turn all allocate from the bio_slab
- * and the bvec_slabs[].
- */
-struct bio_set {
-       mempool_t *bio_pool;
-       mempool_t *bvec_pools[BIOVEC_NR_POOLS];
-};
-
 /*
  * fs_bio_set is the bio_set containing bio and iovec memory pools used by
  * IO code that does not need private memory pools.
  */
-static struct bio_set *fs_bio_set;
+struct bio_set *fs_bio_set;
 
-static inline struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs)
+unsigned int bvec_nr_vecs(unsigned short idx)
+{
+       return bvec_slabs[idx].nr_vecs;
+}
+
+struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs)
 {
        struct bio_vec *bvl;
 
@@ -117,6 +96,9 @@ void bio_free(struct bio *bio, struct bio_set *bio_set)
                mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]);
        }
 
+       if (bio_integrity(bio))
+               bio_integrity_free(bio, bio_set);
+
        mempool_free(bio, bio_set->bio_pool);
 }
 
@@ -158,7 +140,7 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
 
                bio_init(bio);
                if (likely(nr_iovecs)) {
-                       unsigned long idx = 0; /* shut up gcc */
+                       unsigned long uninitialized_var(idx);
 
                        bvl = bvec_alloc_bs(gfp_mask, nr_iovecs, &idx, bs);
                        if (unlikely(!bvl)) {
@@ -275,9 +257,19 @@ struct bio *bio_clone(struct bio *bio, gfp_t gfp_mask)
 {
        struct bio *b = bio_alloc_bioset(gfp_mask, bio->bi_max_vecs, fs_bio_set);
 
-       if (b) {
-               b->bi_destructor = bio_fs_destructor;
-               __bio_clone(b, bio);
+       if (!b)
+               return NULL;
+
+       b->bi_destructor = bio_fs_destructor;
+       __bio_clone(b, bio);
+
+       if (bio_integrity(bio)) {
+               int ret;
+
+               ret = bio_integrity_clone(b, bio, fs_bio_set);
+
+               if (ret < 0)
+                       return NULL;
        }
 
        return b;
@@ -333,10 +325,19 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page
                if (page == prev->bv_page &&
                    offset == prev->bv_offset + prev->bv_len) {
                        prev->bv_len += len;
-                       if (q->merge_bvec_fn &&
-                           q->merge_bvec_fn(q, bio, prev) < len) {
-                               prev->bv_len -= len;
-                               return 0;
+
+                       if (q->merge_bvec_fn) {
+                               struct bvec_merge_data bvm = {
+                                       .bi_bdev = bio->bi_bdev,
+                                       .bi_sector = bio->bi_sector,
+                                       .bi_size = bio->bi_size,
+                                       .bi_rw = bio->bi_rw,
+                               };
+
+                               if (q->merge_bvec_fn(q, &bvm, prev) < len) {
+                                       prev->bv_len -= len;
+                                       return 0;
+                               }
                        }
 
                        goto done;
@@ -377,11 +378,18 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page
         * queue to get further control
         */
        if (q->merge_bvec_fn) {
+               struct bvec_merge_data bvm = {
+                       .bi_bdev = bio->bi_bdev,
+                       .bi_sector = bio->bi_sector,
+                       .bi_size = bio->bi_size,
+                       .bi_rw = bio->bi_rw,
+               };
+
                /*
                 * merge_bvec_fn() returns number of bytes it can accept
                 * at this offset
                 */
-               if (q->merge_bvec_fn(q, bio, bvec) < len) {
+               if (q->merge_bvec_fn(q, &bvm, bvec) < len) {
                        bvec->bv_page = NULL;
                        bvec->bv_len = 0;
                        bvec->bv_offset = 0;
@@ -937,6 +945,96 @@ struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
        return ERR_PTR(-EINVAL);
 }
 
+static void bio_copy_kern_endio(struct bio *bio, int err)
+{
+       struct bio_vec *bvec;
+       const int read = bio_data_dir(bio) == READ;
+       char *p = bio->bi_private;
+       int i;
+
+       __bio_for_each_segment(bvec, bio, i, 0) {
+               char *addr = page_address(bvec->bv_page);
+
+               if (read && !err)
+                       memcpy(p, addr, bvec->bv_len);
+
+               __free_page(bvec->bv_page);
+               p += bvec->bv_len;
+       }
+
+       bio_put(bio);
+}
+
+/**
+ *     bio_copy_kern   -       copy kernel address into bio
+ *     @q: the struct request_queue for the bio
+ *     @data: pointer to buffer to copy
+ *     @len: length in bytes
+ *     @gfp_mask: allocation flags for bio and page allocation
+ *     @reading: data direction is READ
+ *
+ *     copy the kernel address into a bio suitable for io to a block
+ *     device. Returns an error pointer in case of error.
+ */
+struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len,
+                         gfp_t gfp_mask, int reading)
+{
+       unsigned long kaddr = (unsigned long)data;
+       unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       unsigned long start = kaddr >> PAGE_SHIFT;
+       const int nr_pages = end - start;
+       struct bio *bio;
+       struct bio_vec *bvec;
+       int i, ret;
+
+       bio = bio_alloc(gfp_mask, nr_pages);
+       if (!bio)
+               return ERR_PTR(-ENOMEM);
+
+       while (len) {
+               struct page *page;
+               unsigned int bytes = PAGE_SIZE;
+
+               if (bytes > len)
+                       bytes = len;
+
+               page = alloc_page(q->bounce_gfp | gfp_mask);
+               if (!page) {
+                       ret = -ENOMEM;
+                       goto cleanup;
+               }
+
+               if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) {
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+
+               len -= bytes;
+       }
+
+       if (!reading) {
+               void *p = data;
+
+               bio_for_each_segment(bvec, bio, i) {
+                       char *addr = page_address(bvec->bv_page);
+
+                       memcpy(addr, p, bvec->bv_len);
+                       p += bvec->bv_len;
+               }
+       }
+
+       bio->bi_private = data;
+       bio->bi_end_io = bio_copy_kern_endio;
+       return bio;
+cleanup:
+       bio_for_each_segment(bvec, bio, i)
+               __free_page(bvec->bv_page);
+
+       bio_put(bio);
+
+       return ERR_PTR(ret);
+}
+
 /*
  * bio_set_pages_dirty() and bio_check_pages_dirty() are support functions
  * for performing direct-IO in BIOs.
@@ -1159,6 +1257,9 @@ struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors)
        bp->bio1.bi_private = bi;
        bp->bio2.bi_private = pool;
 
+       if (bio_integrity(bi))
+               bio_integrity_split(bi, bp, first_sectors);
+
        return bp;
 }
 
@@ -1200,6 +1301,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);
 
        kfree(bs);
@@ -1216,6 +1318,9 @@ struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size)
        if (!bs->bio_pool)
                goto bad;
 
+       if (bioset_integrity_create(bs, bio_pool_size))
+               goto bad;
+
        if (!biovec_create_pools(bs, bvec_pool_size))
                return bs;
 
@@ -1242,6 +1347,7 @@ static int __init init_bio(void)
 {
        bio_slab = KMEM_CACHE(bio, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
 
+       bio_integrity_init_slab();
        biovec_init_slabs();
 
        fs_bio_set = bioset_create(BIO_POOL_SIZE, 2);
@@ -1273,6 +1379,7 @@ EXPORT_SYMBOL(bio_get_nr_vecs);
 EXPORT_SYMBOL(bio_map_user);
 EXPORT_SYMBOL(bio_unmap_user);
 EXPORT_SYMBOL(bio_map_kern);
+EXPORT_SYMBOL(bio_copy_kern);
 EXPORT_SYMBOL(bio_pair_release);
 EXPORT_SYMBOL(bio_split);
 EXPORT_SYMBOL(bio_split_pool);