new primitive: iov_iter_alignment()
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 5 Mar 2014 18:50:45 +0000 (13:50 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 6 May 2014 21:32:47 +0000 (17:32 -0400)
returns the value aligned as badly as the worst remaining segment
in iov_iter is.  Use instead of open-coded equivalents.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
drivers/staging/lustre/lustre/llite/rw26.c
fs/direct-io.c
include/linux/uio.h
mm/iov_iter.c

index 38a5b580e7f0a49a8ab78a914b2bd54ba100ca7a..f718585c9e08ea8542c41ec82d133aaa45aa7b36 100644 (file)
@@ -391,11 +391,8 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb,
               MAX_DIO_SIZE >> PAGE_CACHE_SHIFT);
 
        /* Check that all user buffers are aligned as well */
-       for (seg = 0; seg < iter->nr_segs; seg++) {
-               if (((unsigned long)iter->iov[seg].iov_base & ~CFS_PAGE_MASK) ||
-                   (iter->iov[seg].iov_len & ~CFS_PAGE_MASK))
-                       return -EINVAL;
-       }
+       if (iov_iter_alignment(iter) & ~CFS_PAGE_MASK)
+               return -EINVAL;
 
        env = cl_env_get(&refcheck);
        LASSERT(!IS_ERR(env));
index 1c677899b9894e84c4fb96247fc02e96d0630351..adfa1fb33456dcb9c128be2a29b5bd906a040601 100644 (file)
@@ -1112,19 +1112,18 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        dio_submit_t submit_io, int flags)
 {
        int seg;
-       size_t size;
-       unsigned long addr;
        unsigned i_blkbits = ACCESS_ONCE(inode->i_blkbits);
        unsigned blkbits = i_blkbits;
        unsigned blocksize_mask = (1 << blkbits) - 1;
        ssize_t retval = -EINVAL;
-       loff_t end = offset;
+       loff_t end = offset + iov_iter_count(iter);
        struct dio *dio;
        struct dio_submit sdio = { 0, };
        unsigned long user_addr;
        size_t bytes;
        struct buffer_head map_bh = { 0, };
        struct blk_plug plug;
+       unsigned long align = offset | iov_iter_alignment(iter);
 
        if (rw & WRITE)
                rw = WRITE_ODIRECT;
@@ -1134,32 +1133,16 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
         * the early prefetch in the caller enough time.
         */
 
-       if (offset & blocksize_mask) {
+       if (align & blocksize_mask) {
                if (bdev)
                        blkbits = blksize_bits(bdev_logical_block_size(bdev));
                blocksize_mask = (1 << blkbits) - 1;
-               if (offset & blocksize_mask)
+               if (align & blocksize_mask)
                        goto out;
        }
 
-       /* Check the memory alignment.  Blocks cannot straddle pages */
-       for (seg = 0; seg < iter->nr_segs; seg++) {
-               addr = (unsigned long)iter->iov[seg].iov_base;
-               size = iter->iov[seg].iov_len;
-               end += size;
-               if (unlikely((addr & blocksize_mask) ||
-                            (size & blocksize_mask))) {
-                       if (bdev)
-                               blkbits = blksize_bits(
-                                        bdev_logical_block_size(bdev));
-                       blocksize_mask = (1 << blkbits) - 1;
-                       if ((addr & blocksize_mask) || (size & blocksize_mask))
-                               goto out;
-               }
-       }
-
        /* watch out for a 0 len io from a tricksy fs */
-       if (rw == READ && end == offset)
+       if (rw == READ && !iov_iter_count(iter))
                return 0;
 
        dio = kmem_cache_alloc(dio_cache, GFP_KERNEL);
index abbe83ded63054a57a6a982869420bbe7f34f2b6..4ee17413fe1bacb16a5b96917af95506501585ca 100644 (file)
@@ -67,6 +67,7 @@ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
 size_t iov_iter_single_seg_count(const struct iov_iter *i);
 size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
                         struct iov_iter *i);
+unsigned long iov_iter_alignment(const struct iov_iter *i);
 
 static inline void iov_iter_init(struct iov_iter *i,
                        const struct iovec *iov, unsigned long nr_segs,
@@ -88,4 +89,5 @@ static inline size_t iov_iter_count(struct iov_iter *i)
 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len);
 
+
 #endif
index 22ec1ef068a83141e5ab4c3719eb67913cbc617a..2f762cc21080d5d7156990553b24e3402e07247b 100644 (file)
@@ -195,3 +195,28 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i)
                return min(i->count, iov->iov_len - i->iov_offset);
 }
 EXPORT_SYMBOL(iov_iter_single_seg_count);
+
+unsigned long iov_iter_alignment(const struct iov_iter *i)
+{
+       const struct iovec *iov = i->iov;
+       unsigned long res;
+       size_t size = i->count;
+       size_t n;
+
+       if (!size)
+               return 0;
+
+       res = (unsigned long)iov->iov_base + i->iov_offset;
+       n = iov->iov_len - i->iov_offset;
+       if (n >= size)
+               return res | size;
+       size -= n;
+       res |= n;
+       while (size > (++iov)->iov_len) {
+               res |= (unsigned long)iov->iov_base | iov->iov_len;
+               size -= iov->iov_len;
+       }
+       res |= (unsigned long)iov->iov_base | size;
+       return res;
+}
+EXPORT_SYMBOL(iov_iter_alignment);