UPSTREAM: clocksource/drivers/rockchip: Add err handle for rk_timer_init
[firefly-linux-kernel-4.4.55.git] / lib / scatterlist.c
index a1cf8cae60e70dd298735f4feab09e55f8141e16..bafa9933fa768d6f76e4ade1296c873a3c38c139 100644 (file)
@@ -56,6 +56,38 @@ int sg_nents(struct scatterlist *sg)
 }
 EXPORT_SYMBOL(sg_nents);
 
+/**
+ * sg_nents_for_len - return total count of entries in scatterlist
+ *                    needed to satisfy the supplied length
+ * @sg:                The scatterlist
+ * @len:       The total required length
+ *
+ * Description:
+ * Determines the number of entries in sg that are required to meet
+ * the supplied length, taking into acount chaining as well
+ *
+ * Returns:
+ *   the number of sg entries needed, negative error on failure
+ *
+ **/
+int sg_nents_for_len(struct scatterlist *sg, u64 len)
+{
+       int nents;
+       u64 total;
+
+       if (!len)
+               return 0;
+
+       for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
+               nents++;
+               total += sg->length;
+               if (total >= len)
+                       return nents;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(sg_nents_for_len);
 
 /**
  * sg_last - return the last scatterlist entry in a list
@@ -73,16 +105,12 @@ EXPORT_SYMBOL(sg_nents);
  **/
 struct scatterlist *sg_last(struct scatterlist *sgl, unsigned int nents)
 {
-#ifndef ARCH_HAS_SG_CHAIN
-       struct scatterlist *ret = &sgl[nents - 1];
-#else
        struct scatterlist *sg, *ret = NULL;
        unsigned int i;
 
        for_each_sg(sgl, sg, nents, i)
                ret = sg;
 
-#endif
 #ifdef CONFIG_DEBUG_SG
        BUG_ON(sgl[0].sg_magic != SG_MAGIC);
        BUG_ON(!sg_is_last(ret));
@@ -165,6 +193,7 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents)
  * __sg_free_table - Free a previously mapped sg table
  * @table:     The sg table header to use
  * @max_ents:  The maximum number of entries per single scatterlist
+ * @skip_first_chunk: don't free the (preallocated) first scatterlist chunk
  * @free_fn:   Free function
  *
  *  Description:
@@ -174,7 +203,7 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents)
  *
  **/
 void __sg_free_table(struct sg_table *table, unsigned int max_ents,
-                    sg_free_fn *free_fn)
+                    bool skip_first_chunk, sg_free_fn *free_fn)
 {
        struct scatterlist *sgl, *next;
 
@@ -202,7 +231,10 @@ void __sg_free_table(struct sg_table *table, unsigned int max_ents,
                }
 
                table->orig_nents -= sg_size;
-               free_fn(sgl, alloc_size);
+               if (skip_first_chunk)
+                       skip_first_chunk = false;
+               else
+                       free_fn(sgl, alloc_size);
                sgl = next;
        }
 
@@ -217,7 +249,7 @@ EXPORT_SYMBOL(__sg_free_table);
  **/
 void sg_free_table(struct sg_table *table)
 {
-       __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
+       __sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree);
 }
 EXPORT_SYMBOL(sg_free_table);
 
@@ -241,19 +273,21 @@ EXPORT_SYMBOL(sg_free_table);
  *
  **/
 int __sg_alloc_table(struct sg_table *table, unsigned int nents,
-                    unsigned int max_ents, gfp_t gfp_mask,
-                    sg_alloc_fn *alloc_fn)
+                    unsigned int max_ents, struct scatterlist *first_chunk,
+                    gfp_t gfp_mask, sg_alloc_fn *alloc_fn)
 {
        struct scatterlist *sg, *prv;
        unsigned int left;
 
-#ifndef ARCH_HAS_SG_CHAIN
+       memset(table, 0, sizeof(*table));
+
+       if (nents == 0)
+               return -EINVAL;
+#ifndef CONFIG_ARCH_HAS_SG_CHAIN
        if (WARN_ON_ONCE(nents > max_ents))
                return -EINVAL;
 #endif
 
-       memset(table, 0, sizeof(*table));
-
        left = nents;
        prv = NULL;
        do {
@@ -267,7 +301,12 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
 
                left -= sg_size;
 
-               sg = alloc_fn(alloc_size, gfp_mask);
+               if (first_chunk) {
+                       sg = first_chunk;
+                       first_chunk = NULL;
+               } else {
+                       sg = alloc_fn(alloc_size, gfp_mask);
+               }
                if (unlikely(!sg)) {
                        /*
                         * Adjust entry count to reflect that the last
@@ -322,9 +361,9 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
        int ret;
 
        ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC,
-                              gfp_mask, sg_kmalloc);
+                              NULL, gfp_mask, sg_kmalloc);
        if (unlikely(ret))
-               __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
+               __sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree);
 
        return ret;
 }
@@ -453,6 +492,66 @@ void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl,
 }
 EXPORT_SYMBOL(sg_miter_start);
 
+static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
+{
+       if (!miter->__remaining) {
+               struct scatterlist *sg;
+               unsigned long pgoffset;
+
+               if (!__sg_page_iter_next(&miter->piter))
+                       return false;
+
+               sg = miter->piter.sg;
+               pgoffset = miter->piter.sg_pgoffset;
+
+               miter->__offset = pgoffset ? 0 : sg->offset;
+               miter->__remaining = sg->offset + sg->length -
+                               (pgoffset << PAGE_SHIFT) - miter->__offset;
+               miter->__remaining = min_t(unsigned long, miter->__remaining,
+                                          PAGE_SIZE - miter->__offset);
+       }
+
+       return true;
+}
+
+/**
+ * sg_miter_skip - reposition mapping iterator
+ * @miter: sg mapping iter to be skipped
+ * @offset: number of bytes to plus the current location
+ *
+ * Description:
+ *   Sets the offset of @miter to its current location plus @offset bytes.
+ *   If mapping iterator @miter has been proceeded by sg_miter_next(), this
+ *   stops @miter.
+ *
+ * Context:
+ *   Don't care if @miter is stopped, or not proceeded yet.
+ *   Otherwise, preemption disabled if the SG_MITER_ATOMIC is set.
+ *
+ * Returns:
+ *   true if @miter contains the valid mapping.  false if end of sg
+ *   list is reached.
+ */
+bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
+{
+       sg_miter_stop(miter);
+
+       while (offset) {
+               off_t consumed;
+
+               if (!sg_miter_get_next_page(miter))
+                       return false;
+
+               consumed = min_t(off_t, offset, miter->__remaining);
+               miter->__offset += consumed;
+               miter->__remaining -= consumed;
+               offset -= consumed;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(sg_miter_skip);
+
 /**
  * sg_miter_next - proceed mapping iterator to the next mapping
  * @miter: sg mapping iter to proceed
@@ -478,22 +577,9 @@ bool sg_miter_next(struct sg_mapping_iter *miter)
         * Get to the next page if necessary.
         * __remaining, __offset is adjusted by sg_miter_stop
         */
-       if (!miter->__remaining) {
-               struct scatterlist *sg;
-               unsigned long pgoffset;
-
-               if (!__sg_page_iter_next(&miter->piter))
-                       return false;
-
-               sg = miter->piter.sg;
-               pgoffset = miter->piter.sg_pgoffset;
+       if (!sg_miter_get_next_page(miter))
+               return false;
 
-               miter->__offset = pgoffset ? 0 : sg->offset;
-               miter->__remaining = sg->offset + sg->length -
-                               (pgoffset << PAGE_SHIFT) - miter->__offset;
-               miter->__remaining = min_t(unsigned long, miter->__remaining,
-                                          PAGE_SIZE - miter->__offset);
-       }
        miter->page = sg_page_iter_page(&miter->piter);
        miter->consumed = miter->length = miter->__remaining;
 
@@ -529,7 +615,8 @@ void sg_miter_stop(struct sg_mapping_iter *miter)
                miter->__offset += miter->consumed;
                miter->__remaining -= miter->consumed;
 
-               if (miter->__flags & SG_MITER_TO_SG)
+               if ((miter->__flags & SG_MITER_TO_SG) &&
+                   !PageSlab(miter->page))
                        flush_kernel_dcache_page(miter->page);
 
                if (miter->__flags & SG_MITER_ATOMIC) {
@@ -552,14 +639,15 @@ EXPORT_SYMBOL(sg_miter_stop);
  * @nents:              Number of SG entries
  * @buf:                Where to copy from
  * @buflen:             The number of bytes to copy
- * @to_buffer:                  transfer direction (non zero == from an sg list to a
- *                      buffer, 0 == from a buffer to an sg list
+ * @skip:               Number of bytes to skip before copying
+ * @to_buffer:          transfer direction (true == from an sg list to a
+ *                      buffer, false == from a buffer to an sg list
  *
  * Returns the number of copied bytes.
  *
  **/
-static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
-                            void *buf, size_t buflen, int to_buffer)
+size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf,
+                     size_t buflen, off_t skip, bool to_buffer)
 {
        unsigned int offset = 0;
        struct sg_mapping_iter miter;
@@ -573,6 +661,9 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
 
        sg_miter_start(&miter, sgl, nents, sg_flags);
 
+       if (!sg_miter_skip(&miter, skip))
+               return false;
+
        local_irq_save(flags);
 
        while (sg_miter_next(&miter) && offset < buflen) {
@@ -593,6 +684,7 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
        local_irq_restore(flags);
        return offset;
 }
+EXPORT_SYMBOL(sg_copy_buffer);
 
 /**
  * sg_copy_from_buffer - Copy from a linear buffer to an SG list
@@ -605,9 +697,9 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
  *
  **/
 size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
-                          void *buf, size_t buflen)
+                          const void *buf, size_t buflen)
 {
-       return sg_copy_buffer(sgl, nents, buf, buflen, 0);
+       return sg_copy_buffer(sgl, nents, (void *)buf, buflen, 0, false);
 }
 EXPORT_SYMBOL(sg_copy_from_buffer);
 
@@ -624,6 +716,42 @@ EXPORT_SYMBOL(sg_copy_from_buffer);
 size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                         void *buf, size_t buflen)
 {
-       return sg_copy_buffer(sgl, nents, buf, buflen, 1);
+       return sg_copy_buffer(sgl, nents, buf, buflen, 0, true);
 }
 EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/**
+ * sg_pcopy_from_buffer - Copy from a linear buffer to an SG list
+ * @sgl:                The SG list
+ * @nents:              Number of SG entries
+ * @buf:                Where to copy from
+ * @buflen:             The number of bytes to copy
+ * @skip:               Number of bytes to skip before copying
+ *
+ * Returns the number of copied bytes.
+ *
+ **/
+size_t sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents,
+                           const void *buf, size_t buflen, off_t skip)
+{
+       return sg_copy_buffer(sgl, nents, (void *)buf, buflen, skip, false);
+}
+EXPORT_SYMBOL(sg_pcopy_from_buffer);
+
+/**
+ * sg_pcopy_to_buffer - Copy from an SG list to a linear buffer
+ * @sgl:                The SG list
+ * @nents:              Number of SG entries
+ * @buf:                Where to copy to
+ * @buflen:             The number of bytes to copy
+ * @skip:               Number of bytes to skip before copying
+ *
+ * Returns the number of copied bytes.
+ *
+ **/
+size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
+                         void *buf, size_t buflen, off_t skip)
+{
+       return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
+}
+EXPORT_SYMBOL(sg_pcopy_to_buffer);