Author: hselasky Date: Wed Feb 22 19:31:02 2017 New Revision: 314105 URL: https://svnweb.freebsd.org/changeset/base/314105
Log: Improve LinuxKPI scatter list support. The i915kms driver in Linux 4.9 reimplement parts of the scatter list functions with regards to performance. In other words there is not so much room for changing structure layouts and functionality if the i915kms should be built AS-IS. This patch aligns the scatter list support to what is expected by the i915kms driver. Remove some comments not needed while at it. Obtained from: kmacy @ MFC after: 1 week Sponsored by: Mellanox Technologies Modified: head/sys/compat/linuxkpi/common/include/linux/scatterlist.h Modified: head/sys/compat/linuxkpi/common/include/linux/scatterlist.h ============================================================================== --- head/sys/compat/linuxkpi/common/include/linux/scatterlist.h Wed Feb 22 18:44:57 2017 (r314104) +++ head/sys/compat/linuxkpi/common/include/linux/scatterlist.h Wed Feb 22 19:31:02 2017 (r314105) @@ -2,7 +2,7 @@ * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. - * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. + * Copyright (c) 2013-2017 Mellanox Technologies, Ltd. * Copyright (c) 2015 Matthew Dillon <dil...@backplane.com> * All rights reserved. * @@ -34,18 +34,17 @@ #include <linux/page.h> #include <linux/slab.h> +#include <linux/mm.h> struct scatterlist { - union { - struct page *page; - struct scatterlist *sg; - } sl_un; + unsigned long page_link; + unsigned int offset; + unsigned int length; dma_addr_t address; - unsigned long offset; - uint32_t length; - uint32_t flags; }; +CTASSERT((sizeof(struct scatterlist) & 0x3) == 0); + struct sg_table { struct scatterlist *sgl; unsigned int nents; @@ -56,58 +55,79 @@ struct sg_page_iter { struct scatterlist *sg; unsigned int sg_pgoffset; unsigned int maxents; + struct { + unsigned int nents; + int pg_advance; + } internal; }; #define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) +#define SG_MAGIC 0x87654321UL + +#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)) + #define sg_dma_address(sg) (sg)->address #define sg_dma_len(sg) (sg)->length -#define sg_page(sg) (sg)->sl_un.page -#define sg_scatternext(sg) (sg)->sl_un.sg -#define SG_END 0x01 -#define SG_CHAIN 0x02 +#define for_each_sg_page(sgl, iter, nents, pgoffset) \ + for (_sg_iter_init(sgl, iter, nents, pgoffset); \ + (iter)->sg; _sg_iter_next(iter)) + +#define for_each_sg(sglist, sg, sgmax, iter) \ + for (iter = 0, sg = (sglist); iter < (sgmax); iter++, sg = sg_next(sg)) + +typedef struct scatterlist *(sg_alloc_fn) (unsigned int, gfp_t); +typedef void (sg_free_fn) (struct scatterlist *, unsigned int); + +static inline void +sg_assign_page(struct scatterlist *sg, struct page *page) +{ + unsigned long page_link = sg->page_link & 0x3; + + 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_page(sg) = page; - sg_dma_len(sg) = len; + sg_assign_page(sg, page); sg->offset = offset; - if (offset > PAGE_SIZE) - panic("sg_set_page: Invalid offset %d\n", offset); + sg->length = len; } -static inline void -sg_set_buf(struct scatterlist *sg, const void *buf, unsigned int buflen) +static inline struct page * +sg_page(struct scatterlist *sg) { - sg_set_page(sg, virt_to_page(buf), buflen, - ((uintptr_t)buf) & (PAGE_SIZE - 1)); + return ((struct page *)((sg)->page_link & ~0x3)); } static inline void -sg_init_table(struct scatterlist *sg, unsigned int nents) +sg_set_buf(struct scatterlist *sg, const void *buf, unsigned int buflen) { - bzero(sg, sizeof(*sg) * nents); - sg[nents - 1].flags = SG_END; + sg_set_page(sg, virt_to_page(buf), buflen, + ((uintptr_t)buf) & (PAGE_SIZE - 1)); } static inline struct scatterlist * sg_next(struct scatterlist *sg) { - if (sg->flags & SG_END) + if (sg_is_last(sg)) return (NULL); sg++; - if (sg->flags & SG_CHAIN) - sg = sg_scatternext(sg); + if (sg_is_chain(sg)) + sg = sg_chain_ptr(sg); return (sg); } static inline vm_paddr_t sg_phys(struct scatterlist *sg) { - return sg_page(sg)->phys_addr + sg->offset; + return (VM_PAGE_TO_PHYS(sg_page(sg)) + sg->offset); } static inline void @@ -118,18 +138,44 @@ sg_chain(struct scatterlist *prv, unsign sg->offset = 0; sg->length = 0; - sg->flags = SG_CHAIN; - sg->sl_un.sg = sgl; + sg->page_link = ((unsigned long)sgl | 0x01) & ~0x02; } -static inline void +static inline void sg_mark_end(struct scatterlist *sg) { - sg->flags = SG_END; + sg->page_link |= 0x02; + sg->page_link &= ~0x01; +} + +static inline void +sg_init_table(struct scatterlist *sg, unsigned int nents) +{ + bzero(sg, sizeof(*sg) * nents); + sg_mark_end(&sg[nents - 1]); +} + +static struct scatterlist * +sg_kmalloc(unsigned int nents, gfp_t gfp_mask) +{ + if (nents == SG_MAX_SINGLE_ALLOC) { + return ((void *)__get_free_page(gfp_mask)); + } else + return (kmalloc(nents * sizeof(struct scatterlist), gfp_mask)); +} + +static inline void +sg_kfree(struct scatterlist *sg, unsigned int nents) +{ + if (nents == SG_MAX_SINGLE_ALLOC) { + free_page((unsigned long)sg); + } else + kfree(sg); } static inline void -__sg_free_table(struct sg_table *table, unsigned int max_ents) +__sg_free_table(struct sg_table *table, unsigned int max_ents, + bool skip_first_chunk, sg_free_fn * free_fn) { struct scatterlist *sgl, *next; @@ -142,7 +188,7 @@ __sg_free_table(struct sg_table *table, unsigned int sg_size; if (alloc_size > max_ents) { - next = sgl[max_ents - 1].sl_un.sg; + next = sg_chain_ptr(&sgl[max_ents - 1]); alloc_size = max_ents; sg_size = alloc_size - 1; } else { @@ -151,7 +197,10 @@ __sg_free_table(struct sg_table *table, } table->orig_nents -= sg_size; - kfree(sgl); + if (skip_first_chunk) + skip_first_chunk = 0; + else + free_fn(sgl, alloc_size); sgl = next; } @@ -161,12 +210,13 @@ __sg_free_table(struct sg_table *table, static inline void sg_free_table(struct sg_table *table) { - __sg_free_table(table, SG_MAX_SINGLE_ALLOC); + __sg_free_table(table, SG_MAX_SINGLE_ALLOC, 0, sg_kfree); } static inline int __sg_alloc_table(struct sg_table *table, unsigned int nents, - unsigned int max_ents, gfp_t gfp_mask) + unsigned int max_ents, struct scatterlist *first_chunk, + gfp_t gfp_mask, sg_alloc_fn *alloc_fn) { struct scatterlist *sg, *prv; unsigned int left; @@ -174,7 +224,7 @@ __sg_alloc_table(struct sg_table *table, memset(table, 0, sizeof(*table)); if (nents == 0) - return -EINVAL; + return (-EINVAL); left = nents; prv = NULL; do { @@ -189,12 +239,17 @@ __sg_alloc_table(struct sg_table *table, left -= sg_size; - sg = kmalloc(alloc_size * sizeof(struct scatterlist), gfp_mask); + if (first_chunk) { + sg = first_chunk; + first_chunk = NULL; + } else { + sg = alloc_fn(alloc_size, gfp_mask); + } if (unlikely(!sg)) { if (prv) table->nents = ++table->orig_nents; - return -ENOMEM; + return (-ENOMEM); } sg_init_table(sg, alloc_size); table->nents = table->orig_nents += sg_size; @@ -210,7 +265,7 @@ __sg_alloc_table(struct sg_table *table, prv = sg; } while (left); - return 0; + return (0); } static inline int @@ -219,11 +274,70 @@ sg_alloc_table(struct sg_table *table, u int ret; ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC, - gfp_mask); + NULL, gfp_mask, sg_kmalloc); if (unlikely(ret)) - __sg_free_table(table, SG_MAX_SINGLE_ALLOC); + __sg_free_table(table, SG_MAX_SINGLE_ALLOC, 0, sg_kfree); + + return (ret); +} + +static inline int +sg_alloc_table_from_pages(struct sg_table *sgt, + struct page **pages, unsigned int count, + unsigned long off, unsigned long size, + gfp_t gfp_mask) +{ + unsigned int i, segs, cur; + int rc; + struct scatterlist *s; + + for (segs = i = 1; i < count; ++i) { + if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) + ++segs; + } + if (__predict_false((rc = sg_alloc_table(sgt, segs, gfp_mask)))) + return (rc); + + cur = 0; + for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { + unsigned long seg_size; + unsigned int j; + + for (j = cur + 1; j < count; ++j) + if (page_to_pfn(pages[j]) != + page_to_pfn(pages[j - 1]) + 1) + break; + + seg_size = ((j - cur) << PAGE_SHIFT) - off; + sg_set_page(s, pages[cur], min(size, seg_size), off); + size -= seg_size; + off = 0; + cur = j; + } + return (0); +} + - return ret; +static inline int +sg_nents(struct scatterlist *sg) +{ + int nents; + + for (nents = 0; sg; sg = sg_next(sg)) + nents++; + return (nents); +} + +static inline void +__sg_page_iter_start(struct sg_page_iter *piter, + struct scatterlist *sglist, unsigned int nents, + unsigned long pgoffset) +{ + piter->internal.pg_advance = 0; + piter->internal.nents = nents; + + piter->sg = sglist; + piter->sg_pgoffset = pgoffset; } static inline void @@ -247,6 +361,34 @@ _sg_iter_next(struct sg_page_iter *iter) iter->sg = sg; } +static inline int +sg_page_count(struct scatterlist *sg) +{ + return (PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT); +} + +static inline bool +__sg_page_iter_next(struct sg_page_iter *piter) +{ + if (piter->internal.nents == 0) + return (0); + if (piter->sg == NULL) + return (0); + + piter->sg_pgoffset += piter->internal.pg_advance; + piter->internal.pg_advance = 1; + + while (piter->sg_pgoffset >= sg_page_count(piter->sg)) { + piter->sg_pgoffset -= sg_page_count(piter->sg); + piter->sg = sg_next(piter->sg); + if (--piter->internal.nents == 0) + return (0); + if (piter->sg == NULL) + return (0); + } + return (1); +} + static inline void _sg_iter_init(struct scatterlist *sgl, struct sg_page_iter *iter, unsigned int nents, unsigned long pgoffset) @@ -266,14 +408,14 @@ _sg_iter_init(struct scatterlist *sgl, s static inline dma_addr_t sg_page_iter_dma_address(struct sg_page_iter *spi) { - return spi->sg->address + (spi->sg_pgoffset << PAGE_SHIFT); + return (spi->sg->address + (spi->sg_pgoffset << PAGE_SHIFT)); } -#define for_each_sg_page(sgl, iter, nents, pgoffset) \ - for (_sg_iter_init(sgl, iter, nents, pgoffset); \ - (iter)->sg; _sg_iter_next(iter)) +static inline struct page * +sg_page_iter_page(struct sg_page_iter *piter) +{ + return (nth_page(sg_page(piter->sg), piter->sg_pgoffset)); +} -#define for_each_sg(sglist, sg, sgmax, _itr) \ - for (_itr = 0, sg = (sglist); _itr < (sgmax); _itr++, sg = sg_next(sg)) #endif /* _LINUX_SCATTERLIST_H_ */ _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"