]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - include/linux/scatterlist.h
Move c_src dirs back to toplevel
[bcachefs-tools-debian] / include / linux / scatterlist.h
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
new file mode 100644 (file)
index 0000000..1e4395c
--- /dev/null
@@ -0,0 +1,109 @@
+#ifndef _LINUX_SCATTERLIST_H
+#define _LINUX_SCATTERLIST_H
+
+#include <linux/bug.h>
+#include <linux/slab.h>
+
+struct scatterlist {
+       unsigned long   page_link;
+       unsigned int    offset;
+       unsigned int    length;
+};
+
+#define sg_is_chain(sg)                ((sg)->page_link & 0x01)
+#define sg_is_last(sg)         ((sg)->page_link & 0x02)
+#define sg_chain_ptr(sg)       \
+       ((struct scatterlist *) ((sg)->page_link & ~0x03))
+
+static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
+{
+       unsigned long page_link = sg->page_link & 0x3;
+
+       /*
+        * In order for the low bit stealing approach to work, pages
+        * must be aligned at a 32-bit boundary as a minimum.
+        */
+       BUG_ON((unsigned long) page & 0x03);
+       sg->page_link = page_link | (unsigned long) page;
+}
+
+static inline void sg_set_page(struct scatterlist *sg, struct page *page,
+                              unsigned int len, unsigned int offset)
+{
+       sg_assign_page(sg, page);
+       sg->offset = offset;
+       sg->length = len;
+}
+
+static inline struct page *sg_page(struct scatterlist *sg)
+{
+       return (struct page *)((sg)->page_link & ~0x3);
+}
+
+static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
+                             unsigned int buflen)
+{
+       sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
+}
+
+static inline struct scatterlist *sg_next(struct scatterlist *sg)
+{
+       if (sg_is_last(sg))
+               return NULL;
+
+       sg++;
+       if (unlikely(sg_is_chain(sg)))
+               sg = sg_chain_ptr(sg);
+
+       return sg;
+}
+
+#define for_each_sg(sglist, sg, nr, __i)       \
+       for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
+
+static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents,
+                           struct scatterlist *sgl)
+{
+       /*
+        * offset and length are unused for chain entry.  Clear them.
+        */
+       prv[prv_nents - 1].offset = 0;
+       prv[prv_nents - 1].length = 0;
+
+       /*
+        * Set lowest bit to indicate a link pointer, and make sure to clear
+        * the termination bit if it happens to be set.
+        */
+       prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02;
+}
+
+static inline void sg_mark_end(struct scatterlist *sg)
+{
+       sg->page_link |= 0x02;
+       sg->page_link &= ~0x01;
+}
+
+static inline void sg_unmark_end(struct scatterlist *sg)
+{
+       sg->page_link &= ~0x02;
+}
+
+static inline void *sg_virt(struct scatterlist *sg)
+{
+       return page_address(sg_page(sg)) + sg->offset;
+}
+
+static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
+{
+       memset(sgl, 0, sizeof(*sgl) * nents);
+       sg_mark_end(&sgl[nents - 1]);
+}
+
+static inline void sg_init_one(struct scatterlist *sg, const void *buf,
+                              unsigned int buflen)
+{
+       sg_init_table(sg, 1);
+       sg_set_buf(sg, buf, buflen);
+}
+
+#endif /* _LINUX_SCATTERLIST_H */