cgroups: implement device whitelist
[safe/jmp/linux-2.6] / fs / bio.c
index 29a44c1..6e0b6f6 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -109,11 +109,14 @@ static inline struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned lon
 
 void bio_free(struct bio *bio, struct bio_set *bio_set)
 {
-       const int pool_idx = BIO_POOL_IDX(bio);
+       if (bio->bi_io_vec) {
+               const int pool_idx = BIO_POOL_IDX(bio);
 
-       BIO_BUG_ON(pool_idx >= BIOVEC_NR_POOLS);
+               BIO_BUG_ON(pool_idx >= BIOVEC_NR_POOLS);
+
+               mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]);
+       }
 
-       mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]);
        mempool_free(bio, bio_set->bio_pool);
 }
 
@@ -127,21 +130,9 @@ static void bio_fs_destructor(struct bio *bio)
 
 void bio_init(struct bio *bio)
 {
-       bio->bi_next = NULL;
-       bio->bi_bdev = NULL;
+       memset(bio, 0, sizeof(*bio));
        bio->bi_flags = 1 << BIO_UPTODATE;
-       bio->bi_rw = 0;
-       bio->bi_vcnt = 0;
-       bio->bi_idx = 0;
-       bio->bi_phys_segments = 0;
-       bio->bi_hw_segments = 0;
-       bio->bi_hw_front_size = 0;
-       bio->bi_hw_back_size = 0;
-       bio->bi_size = 0;
-       bio->bi_max_vecs = 0;
-       bio->bi_end_io = NULL;
        atomic_set(&bio->bi_cnt, 1);
-       bio->bi_private = NULL;
 }
 
 /**
@@ -257,11 +248,13 @@ inline int bio_hw_segments(struct request_queue *q, struct bio *bio)
  */
 void __bio_clone(struct bio *bio, struct bio *bio_src)
 {
-       struct request_queue *q = bdev_get_queue(bio_src->bi_bdev);
-
        memcpy(bio->bi_io_vec, bio_src->bi_io_vec,
                bio_src->bi_max_vecs * sizeof(struct bio_vec));
 
+       /*
+        * most users will be overriding ->bi_bdev with a new target,
+        * so we don't set nor calculate new physical/hw segment counts here
+        */
        bio->bi_sector = bio_src->bi_sector;
        bio->bi_bdev = bio_src->bi_bdev;
        bio->bi_flags |= 1 << BIO_CLONED;
@@ -269,8 +262,6 @@ void __bio_clone(struct bio *bio, struct bio *bio_src)
        bio->bi_vcnt = bio_src->bi_vcnt;
        bio->bi_size = bio_src->bi_size;
        bio->bi_idx = bio_src->bi_idx;
-       bio_phys_segments(q, bio);
-       bio_hw_segments(q, bio);
 }
 
 /**
@@ -453,22 +444,27 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len,
 
 struct bio_map_data {
        struct bio_vec *iovecs;
-       void __user *userptr;
+       int nr_sgvecs;
+       struct sg_iovec *sgvecs;
 };
 
-static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio)
+static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio,
+                            struct sg_iovec *iov, int iov_count)
 {
        memcpy(bmd->iovecs, bio->bi_io_vec, sizeof(struct bio_vec) * bio->bi_vcnt);
+       memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count);
+       bmd->nr_sgvecs = iov_count;
        bio->bi_private = bmd;
 }
 
 static void bio_free_map_data(struct bio_map_data *bmd)
 {
        kfree(bmd->iovecs);
+       kfree(bmd->sgvecs);
        kfree(bmd);
 }
 
-static struct bio_map_data *bio_alloc_map_data(int nr_segs)
+static struct bio_map_data *bio_alloc_map_data(int nr_segs, int iov_count)
 {
        struct bio_map_data *bmd = kmalloc(sizeof(*bmd), GFP_KERNEL);
 
@@ -476,13 +472,71 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs)
                return NULL;
 
        bmd->iovecs = kmalloc(sizeof(struct bio_vec) * nr_segs, GFP_KERNEL);
-       if (bmd->iovecs)
+       if (!bmd->iovecs) {
+               kfree(bmd);
+               return NULL;
+       }
+
+       bmd->sgvecs = kmalloc(sizeof(struct sg_iovec) * iov_count, GFP_KERNEL);
+       if (bmd->sgvecs)
                return bmd;
 
+       kfree(bmd->iovecs);
        kfree(bmd);
        return NULL;
 }
 
+static int __bio_copy_iov(struct bio *bio, struct sg_iovec *iov, int iov_count,
+                         int uncopy)
+{
+       int ret = 0, i;
+       struct bio_vec *bvec;
+       int iov_idx = 0;
+       unsigned int iov_off = 0;
+       int read = bio_data_dir(bio) == READ;
+
+       __bio_for_each_segment(bvec, bio, i, 0) {
+               char *bv_addr = page_address(bvec->bv_page);
+               unsigned int bv_len = bvec->bv_len;
+
+               while (bv_len && iov_idx < iov_count) {
+                       unsigned int bytes;
+                       char *iov_addr;
+
+                       bytes = min_t(unsigned int,
+                                     iov[iov_idx].iov_len - iov_off, bv_len);
+                       iov_addr = iov[iov_idx].iov_base + iov_off;
+
+                       if (!ret) {
+                               if (!read && !uncopy)
+                                       ret = copy_from_user(bv_addr, iov_addr,
+                                                            bytes);
+                               if (read && uncopy)
+                                       ret = copy_to_user(iov_addr, bv_addr,
+                                                          bytes);
+
+                               if (ret)
+                                       ret = -EFAULT;
+                       }
+
+                       bv_len -= bytes;
+                       bv_addr += bytes;
+                       iov_addr += bytes;
+                       iov_off += bytes;
+
+                       if (iov[iov_idx].iov_len == iov_off) {
+                               iov_idx++;
+                               iov_off = 0;
+                       }
+               }
+
+               if (uncopy)
+                       __free_page(bvec->bv_page);
+       }
+
+       return ret;
+}
+
 /**
  *     bio_uncopy_user -       finish previously mapped bio
  *     @bio: bio being terminated
@@ -493,55 +547,56 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs)
 int bio_uncopy_user(struct bio *bio)
 {
        struct bio_map_data *bmd = bio->bi_private;
-       const int read = bio_data_dir(bio) == READ;
-       struct bio_vec *bvec;
-       int i, ret = 0;
-
-       __bio_for_each_segment(bvec, bio, i, 0) {
-               char *addr = page_address(bvec->bv_page);
-               unsigned int len = bmd->iovecs[i].bv_len;
+       int ret;
 
-               if (read && !ret && copy_to_user(bmd->userptr, addr, len))
-                       ret = -EFAULT;
+       ret = __bio_copy_iov(bio, bmd->sgvecs, bmd->nr_sgvecs, 1);
 
-               __free_page(bvec->bv_page);
-               bmd->userptr += len;
-       }
        bio_free_map_data(bmd);
        bio_put(bio);
        return ret;
 }
 
 /**
- *     bio_copy_user   -       copy user data to bio
+ *     bio_copy_user_iov       -       copy user data to bio
  *     @q: destination block queue
- *     @uaddr: start of user address
- *     @len: length in bytes
+ *     @iov:   the iovec.
+ *     @iov_count: number of elements in the iovec
  *     @write_to_vm: bool indicating writing to pages or not
  *
  *     Prepares and returns a bio for indirect user io, bouncing data
  *     to/from kernel pages as necessary. Must be paired with
  *     call bio_uncopy_user() on io completion.
  */
-struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr,
-                         unsigned int len, int write_to_vm)
+struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov,
+                             int iov_count, int write_to_vm)
 {
-       unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       unsigned long start = uaddr >> PAGE_SHIFT;
        struct bio_map_data *bmd;
        struct bio_vec *bvec;
        struct page *page;
        struct bio *bio;
        int i, ret;
+       int nr_pages = 0;
+       unsigned int len = 0;
+
+       for (i = 0; i < iov_count; i++) {
+               unsigned long uaddr;
+               unsigned long end;
+               unsigned long start;
+
+               uaddr = (unsigned long)iov[i].iov_base;
+               end = (uaddr + iov[i].iov_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+               start = uaddr >> PAGE_SHIFT;
 
-       bmd = bio_alloc_map_data(end - start);
+               nr_pages += end - start;
+               len += iov[i].iov_len;
+       }
+
+       bmd = bio_alloc_map_data(nr_pages, iov_count);
        if (!bmd)
                return ERR_PTR(-ENOMEM);
 
-       bmd->userptr = (void __user *) uaddr;
-
        ret = -ENOMEM;
-       bio = bio_alloc(GFP_KERNEL, end - start);
+       bio = bio_alloc(GFP_KERNEL, nr_pages);
        if (!bio)
                goto out_bmd;
 
@@ -573,22 +628,12 @@ struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr,
         * success
         */
        if (!write_to_vm) {
-               char __user *p = (char __user *) uaddr;
-
-               /*
-                * for a write, copy in data to kernel pages
-                */
-               ret = -EFAULT;
-               bio_for_each_segment(bvec, bio, i) {
-                       char *addr = page_address(bvec->bv_page);
-
-                       if (copy_from_user(addr, p, bvec->bv_len))
-                               goto cleanup;
-                       p += bvec->bv_len;
-               }
+               ret = __bio_copy_iov(bio, iov, iov_count, 0);
+               if (ret)
+                       goto cleanup;
        }
 
-       bio_set_map_data(bmd, bio);
+       bio_set_map_data(bmd, bio, iov, iov_count);
        return bio;
 cleanup:
        bio_for_each_segment(bvec, bio, i)
@@ -600,6 +645,28 @@ out_bmd:
        return ERR_PTR(ret);
 }
 
+/**
+ *     bio_copy_user   -       copy user data to bio
+ *     @q: destination block queue
+ *     @uaddr: start of user address
+ *     @len: length in bytes
+ *     @write_to_vm: bool indicating writing to pages or not
+ *
+ *     Prepares and returns a bio for indirect user io, bouncing data
+ *     to/from kernel pages as necessary. Must be paired with
+ *     call bio_uncopy_user() on io completion.
+ */
+struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr,
+                         unsigned int len, int write_to_vm)
+{
+       struct sg_iovec iov;
+
+       iov.iov_base = (void __user *)uaddr;
+       iov.iov_len = len;
+
+       return bio_copy_user_iov(q, &iov, 1, write_to_vm);
+}
+
 static struct bio *__bio_map_user_iov(struct request_queue *q,
                                      struct block_device *bdev,
                                      struct sg_iovec *iov, int iov_count,
@@ -798,13 +865,9 @@ void bio_unmap_user(struct bio *bio)
        bio_put(bio);
 }
 
-static int bio_map_kern_endio(struct bio *bio, unsigned int bytes_done, int err)
+static void bio_map_kern_endio(struct bio *bio, int err)
 {
-       if (bio->bi_size)
-               return 1;
-
        bio_put(bio);
-       return 0;
 }
 
 
@@ -916,7 +979,7 @@ void bio_set_pages_dirty(struct bio *bio)
        }
 }
 
-void bio_release_pages(struct bio *bio)
+static void bio_release_pages(struct bio *bio)
 {
        struct bio_vec *bvec = bio->bi_io_vec;
        int i;
@@ -1002,34 +1065,26 @@ void bio_check_pages_dirty(struct bio *bio)
 /**
  * bio_endio - end I/O on a bio
  * @bio:       bio
- * @bytes_done:        number of bytes completed
  * @error:     error, if any
  *
  * Description:
- *   bio_endio() will end I/O on @bytes_done number of bytes. This may be
- *   just a partial part of the bio, or it may be the whole bio. bio_endio()
- *   is the preferred way to end I/O on a bio, it takes care of decrementing
- *   bi_size and clearing BIO_UPTODATE on error. @error is 0 on success, and
- *   and one of the established -Exxxx (-EIO, for instance) error values in
- *   case something went wrong. Noone should call bi_end_io() directly on
- *   a bio unless they own it and thus know that it has an end_io function.
+ *   bio_endio() will end I/O on the whole bio. bio_endio() is the
+ *   preferred way to end I/O on a bio, it takes care of clearing
+ *   BIO_UPTODATE on error. @error is 0 on success, and and one of the
+ *   established -Exxxx (-EIO, for instance) error values in case
+ *   something went wrong. Noone should call bi_end_io() directly on a
+ *   bio unless they own it and thus know that it has an end_io
+ *   function.
  **/
-void bio_endio(struct bio *bio, unsigned int bytes_done, int error)
+void bio_endio(struct bio *bio, int error)
 {
        if (error)
                clear_bit(BIO_UPTODATE, &bio->bi_flags);
-
-       if (unlikely(bytes_done > bio->bi_size)) {
-               printk("%s: want %u bytes done, only %u left\n", __FUNCTION__,
-                                               bytes_done, bio->bi_size);
-               bytes_done = bio->bi_size;
-       }
-
-       bio->bi_size -= bytes_done;
-       bio->bi_sector += (bytes_done >> 9);
+       else if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
+               error = -EIO;
 
        if (bio->bi_end_io)
-               bio->bi_end_io(bio, bytes_done, error);
+               bio->bi_end_io(bio, error);
 }
 
 void bio_pair_release(struct bio_pair *bp)
@@ -1037,37 +1092,29 @@ void bio_pair_release(struct bio_pair *bp)
        if (atomic_dec_and_test(&bp->cnt)) {
                struct bio *master = bp->bio1.bi_private;
 
-               bio_endio(master, master->bi_size, bp->error);
+               bio_endio(master, bp->error);
                mempool_free(bp, bp->bio2.bi_private);
        }
 }
 
-static int bio_pair_end_1(struct bio * bi, unsigned int done, int err)
+static void bio_pair_end_1(struct bio *bi, int err)
 {
        struct bio_pair *bp = container_of(bi, struct bio_pair, bio1);
 
        if (err)
                bp->error = err;
 
-       if (bi->bi_size)
-               return 1;
-
        bio_pair_release(bp);
-       return 0;
 }
 
-static int bio_pair_end_2(struct bio * bi, unsigned int done, int err)
+static void bio_pair_end_2(struct bio *bi, int err)
 {
        struct bio_pair *bp = container_of(bi, struct bio_pair, bio2);
 
        if (err)
                bp->error = err;
 
-       if (bi->bi_size)
-               return 1;
-
        bio_pair_release(bp);
-       return 0;
 }
 
 /*
@@ -1223,6 +1270,8 @@ EXPORT_SYMBOL(bio_hw_segments);
 EXPORT_SYMBOL(bio_add_page);
 EXPORT_SYMBOL(bio_add_pc_page);
 EXPORT_SYMBOL(bio_get_nr_vecs);
+EXPORT_SYMBOL(bio_map_user);
+EXPORT_SYMBOL(bio_unmap_user);
 EXPORT_SYMBOL(bio_map_kern);
 EXPORT_SYMBOL(bio_pair_release);
 EXPORT_SYMBOL(bio_split);