mac80211: correct legacy rates check in ieee80211_calc_rx_airtime
[platform/kernel/linux-rpi.git] / lib / iov_iter.c
index 755c10c..d0c3e93 100644 (file)
@@ -191,7 +191,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
        buf = iov->iov_base + skip;
        copy = min(bytes, iov->iov_len - skip);
 
-       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_pages_writeable(buf, copy)) {
+       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_writeable(buf, copy)) {
                kaddr = kmap_atomic(page);
                from = kaddr + offset;
 
@@ -275,7 +275,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
        buf = iov->iov_base + skip;
        copy = min(bytes, iov->iov_len - skip);
 
-       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_pages_readable(buf, copy)) {
+       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_readable(buf, copy)) {
                kaddr = kmap_atomic(page);
                to = kaddr + offset;
 
@@ -416,6 +416,7 @@ static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t by
                return 0;
 
        buf->ops = &page_cache_pipe_buf_ops;
+       buf->flags = 0;
        get_page(page);
        buf->page = page;
        buf->offset = offset;
@@ -430,35 +431,81 @@ out:
 }
 
 /*
+ * fault_in_iov_iter_readable - fault in iov iterator for reading
+ * @i: iterator
+ * @size: maximum length
+ *
  * Fault in one or more iovecs of the given iov_iter, to a maximum length of
- * bytes.  For each iovec, fault in each page that constitutes the iovec.
+ * @size.  For each iovec, fault in each page that constitutes the iovec.
+ *
+ * Returns the number of bytes not faulted in (like copy_to_user() and
+ * copy_from_user()).
  *
- * Return 0 on success, or non-zero if the memory could not be accessed (i.e.
- * because it is an invalid address).
+ * Always returns 0 for non-userspace iterators.
  */
-int iov_iter_fault_in_readable(const struct iov_iter *i, size_t bytes)
+size_t fault_in_iov_iter_readable(const struct iov_iter *i, size_t size)
 {
        if (iter_is_iovec(i)) {
+               size_t count = min(size, iov_iter_count(i));
                const struct iovec *p;
                size_t skip;
 
-               if (bytes > i->count)
-                       bytes = i->count;
-               for (p = i->iov, skip = i->iov_offset; bytes; p++, skip = 0) {
-                       size_t len = min(bytes, p->iov_len - skip);
-                       int err;
+               size -= count;
+               for (p = i->iov, skip = i->iov_offset; count; p++, skip = 0) {
+                       size_t len = min(count, p->iov_len - skip);
+                       size_t ret;
 
                        if (unlikely(!len))
                                continue;
-                       err = fault_in_pages_readable(p->iov_base + skip, len);
-                       if (unlikely(err))
-                               return err;
-                       bytes -= len;
+                       ret = fault_in_readable(p->iov_base + skip, len);
+                       count -= len - ret;
+                       if (ret)
+                               break;
                }
+               return count + size;
        }
        return 0;
 }
-EXPORT_SYMBOL(iov_iter_fault_in_readable);
+EXPORT_SYMBOL(fault_in_iov_iter_readable);
+
+/*
+ * fault_in_iov_iter_writeable - fault in iov iterator for writing
+ * @i: iterator
+ * @size: maximum length
+ *
+ * Faults in the iterator using get_user_pages(), i.e., without triggering
+ * hardware page faults.  This is primarily useful when we already know that
+ * some or all of the pages in @i aren't in memory.
+ *
+ * Returns the number of bytes not faulted in, like copy_to_user() and
+ * copy_from_user().
+ *
+ * Always returns 0 for non-user-space iterators.
+ */
+size_t fault_in_iov_iter_writeable(const struct iov_iter *i, size_t size)
+{
+       if (iter_is_iovec(i)) {
+               size_t count = min(size, iov_iter_count(i));
+               const struct iovec *p;
+               size_t skip;
+
+               size -= count;
+               for (p = i->iov, skip = i->iov_offset; count; p++, skip = 0) {
+                       size_t len = min(count, p->iov_len - skip);
+                       size_t ret;
+
+                       if (unlikely(!len))
+                               continue;
+                       ret = fault_in_safe_writeable(p->iov_base + skip, len);
+                       count -= len - ret;
+                       if (ret)
+                               break;
+               }
+               return count + size;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(fault_in_iov_iter_writeable);
 
 void iov_iter_init(struct iov_iter *i, unsigned int direction,
                        const struct iovec *iov, unsigned long nr_segs,
@@ -467,6 +514,7 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction,
        WARN_ON(direction & ~(READ | WRITE));
        *i = (struct iov_iter) {
                .iter_type = ITER_IOVEC,
+               .nofault = false,
                .data_source = direction,
                .iov = iov,
                .nr_segs = nr_segs,
@@ -532,6 +580,7 @@ static size_t push_pipe(struct iov_iter *i, size_t size,
                        break;
 
                buf->ops = &default_pipe_buf_ops;
+               buf->flags = 0;
                buf->page = page;
                buf->offset = 0;
                buf->len = min_t(ssize_t, left, PAGE_SIZE);
@@ -642,6 +691,7 @@ static size_t copy_mc_pipe_to_iter(const void *addr, size_t bytes,
        struct pipe_inode_info *pipe = i->pipe;
        unsigned int p_mask = pipe->ring_size - 1;
        unsigned int i_head;
+       unsigned int valid = pipe->head;
        size_t n, off, xfer = 0;
 
        if (!sanity(i))
@@ -655,11 +705,17 @@ static size_t copy_mc_pipe_to_iter(const void *addr, size_t bytes,
                rem = copy_mc_to_kernel(p + off, addr + xfer, chunk);
                chunk -= rem;
                kunmap_local(p);
-               i->head = i_head;
-               i->iov_offset = off + chunk;
-               xfer += chunk;
-               if (rem)
+               if (chunk) {
+                       i->head = i_head;
+                       i->iov_offset = off + chunk;
+                       xfer += chunk;
+                       valid = i_head + 1;
+               }
+               if (rem) {
+                       pipe->bufs[i_head & p_mask].len -= rem;
+                       pipe_discard_from(pipe, valid);
                        break;
+               }
                n -= chunk;
                off = 0;
                i_head++;
@@ -1387,7 +1443,7 @@ static ssize_t iter_xarray_get_pages(struct iov_iter *i,
 {
        unsigned nr, offset;
        pgoff_t index, count;
-       size_t size = maxsize, actual;
+       size_t size = maxsize;
        loff_t pos;
 
        if (!size || !maxpages)
@@ -1414,13 +1470,7 @@ static ssize_t iter_xarray_get_pages(struct iov_iter *i,
        if (nr == 0)
                return 0;
 
-       actual = PAGE_SIZE * nr;
-       actual -= offset;
-       if (nr == count && size > 0) {
-               unsigned last_offset = (nr > 1) ? 0 : offset;
-               actual -= PAGE_SIZE - (last_offset + size);
-       }
-       return actual;
+       return min_t(size_t, nr * PAGE_SIZE - offset, maxsize);
 }
 
 /* must be done on non-empty ITER_IOVEC one */
@@ -1481,14 +1531,18 @@ ssize_t iov_iter_get_pages(struct iov_iter *i,
                return 0;
 
        if (likely(iter_is_iovec(i))) {
+               unsigned int gup_flags = 0;
                unsigned long addr;
 
+               if (iov_iter_rw(i) != WRITE)
+                       gup_flags |= FOLL_WRITE;
+               if (i->nofault)
+                       gup_flags |= FOLL_NOFAULT;
+
                addr = first_iovec_segment(i, &len, start, maxsize, maxpages);
                n = DIV_ROUND_UP(len, PAGE_SIZE);
-               res = get_user_pages_fast(addr, n,
-                               iov_iter_rw(i) != WRITE ?  FOLL_WRITE : 0,
-                               pages);
-               if (unlikely(res < 0))
+               res = get_user_pages_fast(addr, n, gup_flags, pages);
+               if (unlikely(res <= 0))
                        return res;
                return (res == n ? len : res * PAGE_SIZE) - *start;
        }
@@ -1551,7 +1605,7 @@ static ssize_t iter_xarray_get_pages_alloc(struct iov_iter *i,
        struct page **p;
        unsigned nr, offset;
        pgoff_t index, count;
-       size_t size = maxsize, actual;
+       size_t size = maxsize;
        loff_t pos;
 
        if (!size)
@@ -1580,13 +1634,7 @@ static ssize_t iter_xarray_get_pages_alloc(struct iov_iter *i,
        if (nr == 0)
                return 0;
 
-       actual = PAGE_SIZE * nr;
-       actual -= offset;
-       if (nr == count && size > 0) {
-               unsigned last_offset = (nr > 1) ? 0 : offset;
-               actual -= PAGE_SIZE - (last_offset + size);
-       }
-       return actual;
+       return min_t(size_t, nr * PAGE_SIZE - offset, maxsize);
 }
 
 ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
@@ -1603,17 +1651,23 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
                return 0;
 
        if (likely(iter_is_iovec(i))) {
+               unsigned int gup_flags = 0;
                unsigned long addr;
 
+               if (iov_iter_rw(i) != WRITE)
+                       gup_flags |= FOLL_WRITE;
+               if (i->nofault)
+                       gup_flags |= FOLL_NOFAULT;
+
                addr = first_iovec_segment(i, &len, start, maxsize, ~0U);
                n = DIV_ROUND_UP(len, PAGE_SIZE);
                p = get_pages_array(n);
                if (!p)
                        return -ENOMEM;
-               res = get_user_pages_fast(addr, n,
-                               iov_iter_rw(i) != WRITE ?  FOLL_WRITE : 0, p);
-               if (unlikely(res < 0)) {
+               res = get_user_pages_fast(addr, n, gup_flags, p);
+               if (unlikely(res <= 0)) {
                        kvfree(p);
+                       *pages = NULL;
                        return res;
                }
                *pages = p;