[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH 1/1] lib/uksglist: A scatter/gather list data structure
A scatter/gather list is a list of vectors, each of which gives the location and length of one segment in the overall read or write request. The sg list provides support for the following operations: uk_sglist_init uk_sglist_reset uk_sglist_count uk_sglist_alloc uk_sglist_free uk_sglist_append uk_sglist_append_sglist uk_sglist_build uk_sglist_clone uk_sglist_length uk_sglist_split uk_sglist_join uk_sglist_slice Taken and modified from freebsd source commit-id: 4736ccfd9c3 Signed-off-by: Sharan Santhanam <sharan.santhanam@xxxxxxxxx> NOTE: Please be aware that this patch uses the update uk_pr_* macros. Please make sure the patch series "Update libukdebug printing" is reviewed before this patch. --- include/uk/arch/types.h | 3 + lib/Config.uk | 1 + lib/Makefile.uk | 1 + lib/uksglist/Config.uk | 8 + lib/uksglist/Makefile.uk | 7 + lib/uksglist/exportsyms.uk | 11 + lib/uksglist/include/uk/sglist.h | 272 ++++++++++++++++++++ lib/uksglist/sglist.c | 533 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 836 insertions(+) create mode 100644 lib/uksglist/Config.uk create mode 100644 lib/uksglist/Makefile.uk create mode 100644 lib/uksglist/exportsyms.uk create mode 100644 lib/uksglist/include/uk/sglist.h create mode 100644 lib/uksglist/sglist.c diff --git a/include/uk/arch/types.h b/include/uk/arch/types.h index 8b97baf..b108c8b 100644 --- a/include/uk/arch/types.h +++ b/include/uk/arch/types.h @@ -212,14 +212,17 @@ typedef __sptr __ssz; /* ssize_t equivalent */ typedef __sptr __off; /* off_t equivalent */ #if (defined __PHY_ADDR_IS_16) +typedef __u16 __vm_offset; typedef __u16 __phys_addr; #define __PRIpaddr __PRIx16 #define __HAVE_PHYS_ADDR__ #elif (defined __PHY_ADDR_IS_32) +typedef __u32 __vm_offset; typedef __u32 __phys_addr; #define __PRIpaddr __PRIx32 #define __HAVE_PHYS_ADDR__ #elif (defined __PHY_ADDR_IS_64) +typedef __u64 __vm_offset; typedef __u64 __phys_addr; #define __PRIpaddr __PRIx64 #define __HAVE_PHYS_ADDR__ diff --git a/lib/Config.uk b/lib/Config.uk index d0e614a..481d7ab 100644 --- a/lib/Config.uk +++ b/lib/Config.uk @@ -38,3 +38,4 @@ source "lib/uklock/Config.uk" source "lib/ukmpi/Config.uk" source "lib/ukswrand/Config.uk" source "lib/ukbus/Config.uk" +source "lib/uksglist/Config.uk" diff --git a/lib/Makefile.uk b/lib/Makefile.uk index 40c65d0..c7db848 100644 --- a/lib/Makefile.uk +++ b/lib/Makefile.uk @@ -19,3 +19,4 @@ $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/vfscore)) $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/uklock)) $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/ukmpi)) $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/ukbus)) +$(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/uksglist)) diff --git a/lib/uksglist/Config.uk b/lib/uksglist/Config.uk new file mode 100644 index 0000000..ee967e4 --- /dev/null +++ b/lib/uksglist/Config.uk @@ -0,0 +1,8 @@ +menuconfig LIBUKSGLIST + bool "uksglist: Scatter Gather List" + default n + select LIBUKDEBUG + help + A scatter/gather list is a list of vectors, each of which gives the + location and length of one segment in the overall + read or write request. diff --git a/lib/uksglist/Makefile.uk b/lib/uksglist/Makefile.uk new file mode 100644 index 0000000..3d7c21b --- /dev/null +++ b/lib/uksglist/Makefile.uk @@ -0,0 +1,7 @@ +$(eval $(call addlib_s,libuksglist,$(CONFIG_LIBUKSGLIST))) + +CINCLUDES-$(CONFIG_LIBUKSGLIST) += -I$(LIBUKSGLIST_BASE)/include +CXXINCLUDES-$(CONFIG_LIBUKSGLIST) += -I$(LIBUKSGLIST_BASE)/include + +LIBUKSGLIST_SRCS-y += $(LIBUKSGLIST_BASE)/sglist.c + diff --git a/lib/uksglist/exportsyms.uk b/lib/uksglist/exportsyms.uk new file mode 100644 index 0000000..a1e9b37 --- /dev/null +++ b/lib/uksglist/exportsyms.uk @@ -0,0 +1,11 @@ +uk_sglist_count +uk_sglist_alloc +uk_sglist_free +uk_sglist_append +uk_sglist_append_sglist +uk_sglist_build +uk_sglist_clone +uk_sglist_length +uk_sglist_split +uk_sglist_join +uk_sglist_slice diff --git a/lib/uksglist/include/uk/sglist.h b/lib/uksglist/include/uk/sglist.h new file mode 100644 index 0000000..a89b10d --- /dev/null +++ b/lib/uksglist/include/uk/sglist.h @@ -0,0 +1,272 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin <jhb@xxxxxxxxxxx> + * Sharan Santhanam <sharan.santhanam@xxxxxxxxx> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/** + * Taken and modified from FreeBSD + * Commit id: 4736ccfd9c34 + */ + +#ifndef UK__SGLIST_H__ +#define UK__SGLIST_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <stdint.h> +#include <uk/arch/types.h> +#include <uk/refcount.h> +#ifdef CONFIG_LIBUKALLOC +#include <uk/alloc.h> +#endif /* CONFIG_LIBUKALLOC */ + +struct uk_sglist_seg { + __phys_addr ss_paddr; /* Physical address */ + size_t ss_len; /* Length of the buffer */ +}; + +struct uk_sglist { + struct uk_sglist_seg *sg_segs; /* Segment management */ + __atomic sg_refs; /* Reference count for the sg list */ + uint16_t sg_nseg; /* Number of segment in the sg list */ + uint16_t sg_maxseg; /* Maximum number of segment in the sg list */ +}; + +/** + * Initialize the sg list. + * @param sg + * A reference to sg list. + * @param maxsegs + * The max nr of segments. + * @param segs + * An array of segments. + */ +static inline void uk_sglist_init(struct uk_sglist *sg, + uint16_t maxsegs, struct uk_sglist_seg *segs) +{ + sg->sg_segs = segs; + sg->sg_nseg = 0; + sg->sg_maxseg = maxsegs; + uk_refcount_init(&sg->sg_refs, 1); +} + +/** + * Reset the sg list. + * @param sg + * A reference to the sg list. + */ +static inline void uk_sglist_reset(struct uk_sglist *sg) +{ + sg->sg_nseg = 0; +} + +/** + * Determine the number of scatter/gather list elements needed to describe a + * kernel virtual address range. + * @param buf + * The virtual address of the scatter gather list. + * @param len + * The size of the scatter gather list. + * + * @return + * - 0: The length was zero. + * - int: The number of segments + */ +int uk_sglist_count(void *buf, size_t len); + +/** + * Append the segments to describe a single kernel virtual address range to a + * scatter gather list. + * + * @param sg + * A reference to the scatter gather list. + * @param buf + * A reference to data buffer associated with the scatter gather list. + * @return + * - EINVAL: Invalid sg list. + * - EFBIG : Insufficient segments. + * - 0: Buffer was appended to the list. + */ +int uk_sglist_append(struct uk_sglist *sg, void *buf, size_t len); + +/** + * Append the subset of the sg list 'source' to sg list 'sg'. + * + * @param sg + * A reference to the update sg list. + * @param source + * A reference to the source sg list. + * @param offset + * The offset in the source from which th segment have to be copied. + * @param length + * The length to be copied. + * @return + * EINVAL: Invalid sg list or length parameter. + * EFBIG: Insufficient segments. + * 0 : Successful in appending subset of the list. + */ +int uk_sglist_append_sglist(struct uk_sglist *sg, + const struct uk_sglist *source, + size_t offset, size_t length); + +/** + * Calculate the total length of the segments described in a sg list. + * @param sg + * A reference to the sg list. + * @return + * size_t : The total length in the segments. + */ +size_t uk_sglist_length(struct uk_sglist *sg); + +/** + * Append the scatter/gather list elements in 'second' to the + * scatter/gather list 'first'. + * @param first + * The first sg list + * @param second + * The second sg list. + * @return + * EFBIG: Insufficient space. + * 0 : Successful joining of the list. + */ +int uk_sglist_join(struct uk_sglist *first, struct uk_sglist *second); + +#ifdef CONFIG_LIBUKALLOC +/** + * Allocate a scatter/gather list along with 'nsegs' segments. + * + * @param a + * The allocator to allocate memory for the scatter gather list. + * @param nsegs + * The max number of segments. + * @return + * - NULL: Allocation failed. + * - (struct uk_sglist *): reference to scatter/gather list. + */ +struct uk_sglist *uk_sglist_alloc(struct uk_alloc *a, int nsegs); + +/** + * Free the scatter/gather list. + * @param sg + * A reference to the scatter gather list. + * @param a + * The allocator to allocate memory for the scatter gather list. + */ +void uk_sglist_free(struct uk_sglist *sg, struct uk_alloc *a); + +/** + * Allocate and populate a scatter/gather list to describe a single kernel + * virtual address range. + * @param a + * The allocator to used allocate the sg list. + * @param buf + * The virtual address of the buffer. + * @param len + * The length of the buffer. + * @return + * - NULL: Failed to create the sg list. + * - (struct uk_sglist *) reference to scatter/gather list. + */ +struct uk_sglist *uk_sglist_build(struct uk_alloc *a, void *buf, + size_t len); + +/** + * Clone a new copy of a scatter/gather list. + * @param sg + * A reference to the sg list to be cloned. + * @param a + * The allocator to use to allocate the sg list. + * @return + * NULL: Failed to clone the list. + * (struct uk_sglist *): reference to the sg list. + */ +struct uk_sglist *uk_sglist_clone(struct uk_sglist *sg, + struct uk_alloc *a); + +/** + * Split a scatter/gather list into two lists. The scatter/gather + * entries for the first 'length' bytes of the 'original' list are + * stored in the '*head' list and are removed from 'original'. + * + * @param original + * A reference to the sg list. + * @param head + * A reference to the head of the sg list. If NULL a new list will be + * allocated. If '*head' is not NULL, it should point to an empty sglist. + * @param a + * A reference to the allocator for maintaining the list. + * @param length + * The length of the list. + * + * @return + * EINVAL: Invalid sg list. + * ENOMEM: Allocation fails. + * EFBIG: Insufficient space. + * 0: Successful split of the list. + */ +int uk_sglist_split(struct uk_sglist *original, struct uk_sglist **head, + struct uk_alloc *a, size_t length); + +/** + * Generate a new scatter/gather list from a range of an existing + * scatter/gather list. The 'offset' and 'length' parameters specify + * the logical range of the 'original' list to extract. + * + * @param original + * The reference to a sg list to be extracted. + * @param slice + * If '*slice' is NULL, then a new list will be allocated. + * If '*slice' is not NULL, it should point to an empty sglist. If it + * does not have enough room for the remaining space + * @param a + * The allocator to be used. + * @param offset + * The offset from which the sglist had to be spliced. + * @param length + * The length of buffer in the original list. + * @return + * EINVAL: Invalid sg list. + * ENOMEM: No memory + * EFBIG: Insufficient space + * 0: Successful in splicing the list. + */ +int uk_sglist_slice(struct uk_sglist *original, struct uk_sglist **slice, + struct uk_alloc *a, size_t offset, size_t length); +#endif /* CONFIG_LIBUKALLOC */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UK__SGLIST_H__ */ diff --git a/lib/uksglist/sglist.c b/lib/uksglist/sglist.c new file mode 100644 index 0000000..0ec2105 --- /dev/null +++ b/lib/uksglist/sglist.c @@ -0,0 +1,533 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin <jhb@xxxxxxxxxxx> + * Sharan Santhanam <sharan.santhanam@xxxxxxxxx> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/** + * Taken and modified from FreeBSD + * Commit id: 4736ccfd9c34 + */ + +#include <errno.h> +#include <string.h> +#include <uk/plat/io.h> +#include <uk/arch/types.h> +#include <uk/arch/limits.h> +#include <uk/refcount.h> +#include <uk/assert.h> +#ifdef CONFIG_LIBUKALLOC +#include <uk/alloc.h> +#endif /* CONFIG_LIBUKALLOC */ +#include <uk/sglist.h> + +/* + * Convenience macros to save the state of an sglist so it can be restored + * if an append attempt fails. Since sglist's only grow we only need to + * save the current count of segments and the length of the ending segment. + * Earlier segments will not be changed by an append, and the only change + * that can occur to the ending segment is that it can be extended. + */ +struct sgsave { + __u16 sg_nseg; + size_t ss_len; +}; + +#define page_off(x) ((unsigned long)(x) & (__PAGE_SIZE - 1)) +#define trunc_page(x) ((unsigned long)(x) & (__PAGE_MASK)) + +#define SGLIST_SAVE(sg, sgsave) do { \ + (sgsave).sg_nseg = (sg)->sg_nseg; \ + if ((sgsave).sg_nseg > 0) \ + (sgsave).ss_len = (sg)->sg_segs[(sgsave).sg_nseg - 1].ss_len; \ + else \ + (sgsave).ss_len = 0; \ +} while (0) + +#define SGLIST_RESTORE(sg, sgsave) do { \ + (sg)->sg_nseg = (sgsave).sg_nseg; \ + if ((sgsave).sg_nseg > 0) \ + (sg)->sg_segs[(sgsave).sg_nseg - 1].ss_len = (sgsave).ss_len; \ +} while (0) + +static inline int _sglist_append_range(struct uk_sglist *sg, + struct uk_sglist_seg **ssp, __phys_addr paddr, + size_t len); +static inline int _sglist_append_buf(struct uk_sglist *sg, void *buf, + size_t len, size_t *donep); +/** + * Append a single (paddr, len) to a sglist. sg is the list and ss is + * the current segment in the list. If we run out of segments then + * EFBIG will be returned. + */ +static inline int _sglist_append_range(struct uk_sglist *sg, + struct uk_sglist_seg **ssp, __phys_addr paddr, + size_t len) +{ + struct uk_sglist_seg *ss; + + ss = *ssp; + if (ss->ss_paddr + ss->ss_len == paddr) + ss->ss_len += len; + else { + if (sg->sg_nseg == sg->sg_maxseg) + return -EFBIG; + ss++; + ss->ss_paddr = paddr; + ss->ss_len = len; + sg->sg_nseg++; + *ssp = ss; + } + return 0; +} + +/** + * Worker routine to append a virtual address range (either kernel or + * user) to a scatter/gather list. + */ +static inline int _sglist_append_buf(struct uk_sglist *sg, void *buf, + size_t len, size_t *donep) +{ + struct uk_sglist_seg *ss; + __vm_offset vaddr, offset; + __phys_addr paddr; + size_t seglen; + int error; + + if (donep) + *donep = 0; + if (len == 0) + return 0; + + /* Do the first page. It may have an offset. */ + vaddr = (__vm_offset)buf; + offset = page_off(vaddr); + paddr = ukplat_virt_to_phys((void *)vaddr); + seglen = MIN(len, __PAGE_SIZE - offset); + if (sg->sg_nseg == 0) { + ss = sg->sg_segs; + ss->ss_paddr = paddr; + ss->ss_len = seglen; + sg->sg_nseg = 1; + } else { + ss = &sg->sg_segs[sg->sg_nseg - 1]; + error = _sglist_append_range(sg, &ss, paddr, seglen); + if (error) + return error; + } + vaddr += seglen; + len -= seglen; + if (donep) + *donep += seglen; + + while (len > 0) { + seglen = MIN(len, __PAGE_SIZE); + paddr = ukplat_virt_to_phys((void *)vaddr); + error = _sglist_append_range(sg, &ss, paddr, seglen); + if (error) + return error; + vaddr += seglen; + len -= seglen; + if (donep) + *donep += seglen; + } + + return 0; +} + +int uk_sglist_count(void *buf, size_t len) +{ + __vm_offset vaddr, vendaddr; + __phys_addr lastaddr, paddr; + int nsegs; + + if (len == 0) + return 0; + + vaddr = trunc_page((__vm_offset)buf); + vendaddr = (__vm_offset)buf + len; + nsegs = 1; + lastaddr = ukplat_virt_to_phys((void *)vaddr); + vaddr += __PAGE_SIZE; + while (vaddr < vendaddr) { + paddr = ukplat_virt_to_phys((void *)vaddr); + if (lastaddr + __PAGE_SIZE != paddr) + nsegs++; + lastaddr = paddr; + vaddr += __PAGE_SIZE; + } + return nsegs; +} + +int uk_sglist_append(struct uk_sglist *sg, void *buf, size_t len) +{ + struct sgsave save; + int error; + + UK_ASSERT(sg); + + if (sg->sg_maxseg == 0) + return -EINVAL; + + SGLIST_SAVE(sg, save); + error = _sglist_append_buf(sg, buf, len, NULL); + if (error) + SGLIST_RESTORE(sg, save); + + return error; +} + +int uk_sglist_append_sglist(struct uk_sglist *sg, + const struct uk_sglist *source, + size_t offset, size_t length) +{ + struct sgsave save; + struct uk_sglist_seg *ss; + size_t seglen; + int error, i; + + UK_ASSERT(sg); + + if (sg->sg_maxseg == 0 || length == 0) + return -EINVAL; + SGLIST_SAVE(sg, save); + error = -EINVAL; + ss = &sg->sg_segs[sg->sg_nseg - 1]; + for (i = 0; i < source->sg_nseg; i++) { + if (offset >= source->sg_segs[i].ss_len) { + offset -= source->sg_segs[i].ss_len; + continue; + } + seglen = source->sg_segs[i].ss_len - offset; + if (seglen > length) + seglen = length; + error = _sglist_append_range(sg, &ss, + source->sg_segs[i].ss_paddr + offset, seglen); + if (error) + break; + offset = 0; + length -= seglen; + if (length == 0) + break; + } + if (length != 0) { + uk_pr_err("Failed to add buffer to the sg list\n"); + error = -EINVAL; + } + if (error) + SGLIST_RESTORE(sg, save); + return error; +} + +size_t uk_sglist_length(struct uk_sglist *sg) +{ + size_t space; + int i; + + UK_ASSERT(sg); + + space = 0; + for (i = 0; i < sg->sg_nseg; i++) + space += sg->sg_segs[i].ss_len; + return space; +} + +int uk_sglist_join(struct uk_sglist *first, struct uk_sglist *second) +{ + struct uk_sglist_seg *flast, *sfirst; + int append; + + /* If 'second' is empty, there is nothing to do. */ + if (second->sg_nseg == 0) + return 0; + + /* + * If the first entry in 'second' can be appended to the last entry + * in 'first' then set append to '1'. + */ + append = 0; + flast = &first->sg_segs[first->sg_nseg - 1]; + sfirst = &second->sg_segs[0]; + if (first->sg_nseg != 0 && + flast->ss_paddr + flast->ss_len == sfirst->ss_paddr) + append = 1; + + /* Make sure 'first' has enough room. */ + if (first->sg_nseg + second->sg_nseg - append > first->sg_maxseg) + return -EFBIG; + + /* Merge last in 'first' and first in 'second' if needed. */ + if (append) + flast->ss_len += sfirst->ss_len; + + /* Append new segments from 'second' to 'first'. */ + memmove(second->sg_segs + append, first->sg_segs + first->sg_nseg, + (second->sg_nseg - append) * sizeof(struct uk_sglist_seg)); + first->sg_nseg += second->sg_nseg - append; + uk_sglist_reset(second); + return 0; +} + +#ifdef CONFIG_UKALLOC +struct uk_sglist *uk_sglist_alloc(struct uk_alloc *a, int nsegs) +{ + struct uk_sglist *sg; + + UK_ASSERT(a); + + /* Allocate the scatter/gather list */ + sg = uk_malloc(a, sizeof(struct uk_sglist) + + (nsegs * sizeof(struct uk_sglist_seg))); + if (!sg) { + uk_pr_err("Error in allocating sg list\n"); + return NULL; + } + + /* Initialize the list */ + uk_sglist_init(sg, nsegs, (struct uk_sglist_seg *)(sg + 1)); + return sg; +} + +void uk_sglist_free(struct uk_sglist *sg, struct uk_alloc *a) +{ + UK_ASSERT(sg && a); + + if (uk_refcount_release(&sg->sg_refs)) + uk_free(a, sg); +} + +struct uk_sglist *uk_sglist_build(struct uk_alloc *a, void *buf, + size_t len) +{ + struct uk_sglist *sg; + int nsegs; + + UK_ASSERT(a); + + if (len == 0) + return NULL; + + nsegs = uk_sglist_count(buf, len); + UK_ASSERT(nsegs > 0); + + sg = uk_sglist_alloc(a, nsegs); + if (sg == NULL) { + uk_pr_err("Error in allocating the sg list\n"); + return NULL; + } + if (uk_sglist_append(sg, buf, len) != 0) { + uk_pr_err("Error in adding segments to sg list\n"); + uk_sglist_free(sg, a); + return NULL; + } + return sg; +} + +struct uk_sglist *uk_sglist_clone(struct uk_sglist *sg, + struct uk_alloc *a) +{ + struct uk_sglist *new; + + UK_ASSERT(a); + + if (!sg) + return NULL; + + new = uk_sglist_alloc(a, sg->sg_maxseg); + if (!new) { + uk_pr_err("Allocation for the new list failed\n"); + return NULL; + } + + new->sg_nseg = sg->sg_nseg; + memmove(new->sg_segs, sg->sg_segs, + sizeof(struct uk_sglist_seg) * sg->sg_nseg); + return new; +} + +int uk_sglist_split(struct uk_sglist *original, struct uk_sglist **head, + struct uk_alloc *a, size_t length) +{ + struct uk_sglist *sg; + size_t space, split; + int count, i; + + if (uk_refcount_read(&original->sg_refs) > 1) + return -EDOOFUS; + + /* Figure out how big of a sglist '*head' has to hold. */ + count = 0; + space = 0; + split = 0; + for (i = 0; i < original->sg_nseg; i++) { + space += original->sg_segs[i].ss_len; + count++; + if (space >= length) { + /* + * If 'length' falls in the middle of a + * scatter/gather list entry, then 'split' + * holds how much of that entry will remain in + * 'original'. + */ + split = space - length; + break; + } + } + + /* Nothing to do, so leave head empty. */ + if (count == 0) + return 0; + + if (*head == NULL) { + sg = uk_sglist_alloc(a, count); + if (sg == NULL) + return -ENOMEM; + *head = sg; + } else { + sg = *head; + if (sg->sg_maxseg < count) + return -EFBIG; + if (sg->sg_nseg != 0) + return -EINVAL; + } + + /* Copy 'count' entries to 'sg' from 'original'. */ + memmove(sg->sg_segs, original->sg_segs, + count * sizeof(struct uk_sglist_seg)); + sg->sg_nseg = count; + + /* + * If we had to split a list entry, fixup the last entry in + * 'sg' and the new first entry in 'original'. We also + * decrement 'count' by 1 since we will only be removing + * 'count - 1' segments from 'original' now. + */ + if (split != 0) { + count--; + sg->sg_segs[count].ss_len -= split; + original->sg_segs[count].ss_paddr = + sg->sg_segs[count].ss_paddr + split; + original->sg_segs[count].ss_len = split; + } + + /* Trim 'count' entries from the front of 'original'. */ + original->sg_nseg -= count; + memmove(original->sg_segs, original->sg_segs + count, + count * sizeof(struct uk_sglist_seg)); + return 0; +} + +int uk_sglist_slice(struct uk_sglist *original, struct uk_sglist **slice, + struct uk_alloc *a, size_t offset, size_t length) +{ + struct uk_sglist *sg; + size_t space, end, foffs, loffs; + int count, i, fseg; + + /* Nothing to do. */ + if (length == 0) + return 0; + + /* Figure out how many segments '*slice' needs to have. */ + end = offset + length; + space = 0; + count = 0; + fseg = 0; + foffs = loffs = 0; + for (i = 0; i < original->sg_nseg; i++) { + space += original->sg_segs[i].ss_len; + if (space > offset) { + /* + * When we hit the first segment, store its index + * in 'fseg' and the offset into the first segment + * of 'offset' in 'foffs'. + */ + if (count == 0) { + fseg = i; + foffs = offset - (space - + original->sg_segs[i].ss_len); + uk_pr_debug("sglist_slice: foffs = %08lx", + foffs); + } + count++; + + /* + * When we hit the last segment, break out of + * the loop. Store the amount of extra space + * at the end of this segment in 'loffs'. + */ + if (space >= end) { + loffs = space - end; + uk_pr_debug("sglist_slice: loffs = %08lx", + loffs); + break; + } + } + } + + /* If we never hit 'end', then 'length' ran off the end, so fail. */ + if (space < end) + return -EINVAL; + + if (*slice == NULL) { + sg = uk_sglist_alloc(a, count); + if (sg == NULL) + return -ENOMEM; + *slice = sg; + } else { + sg = *slice; + if (sg->sg_maxseg < count) + return -EFBIG; + if (sg->sg_nseg != 0) + return -EINVAL; + } + + /* + * Copy over 'count' segments from 'original' starting at + * 'fseg' to 'sg'. + */ + memmove(sg->sg_segs, original->sg_segs + fseg, + count * sizeof(struct uk_sglist_seg)); + sg->sg_nseg = count; + + /* Fixup first and last segments if needed. */ + if (foffs != 0) { + sg->sg_segs[0].ss_paddr += foffs; + sg->sg_segs[0].ss_len -= foffs; + uk_pr_debug("sglist_slice seg[0]: %16lx:%16lx", + (long)sg->sg_segs[0].ss_paddr, sg->sg_segs[0].ss_len); + } + if (loffs != 0) { + sg->sg_segs[count - 1].ss_len -= loffs; + uk_pr_debug("sglist_slice seg[%d]: len %08x", + count - 1, (uint32_t)sg->sg_segs[count - 1].ss_len); + } + return 0; +} +#endif /* CONFIG_UKALLOC */ -- 2.7.4 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |