summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2016-05-20 23:28:43 +0000
committerjhb <jhb@FreeBSD.org>2016-05-20 23:28:43 +0000
commitca492fdb2dc89b23ef0fefb029dea1b73243396e (patch)
tree7491ae1dc744661bd8c6d437c0366e366a27891f
parent5a01e6efad18c418c1020e4161d57790f994b24f (diff)
downloadFreeBSD-src-ca492fdb2dc89b23ef0fefb029dea1b73243396e.zip
FreeBSD-src-ca492fdb2dc89b23ef0fefb029dea1b73243396e.tar.gz
Add sglist functions for working with arrays of VM pages.
sglist_count_vmpages() determines the number of segments required for a buffer described by an array of VM pages. sglist_append_vmpages() adds the segments described by such a buffer to an sglist. The latter function is largely pulled from sglist_append_bio(), and sglist_append_bio() now uses sglist_append_vmpages(). Reviewed by: kib Sponsored by: Chelsio Communications
-rw-r--r--share/man/man9/Makefile2
-rw-r--r--share/man/man9/sglist.933
-rw-r--r--sys/kern/subr_sglist.c101
-rw-r--r--sys/sys/sglist.h3
4 files changed, 113 insertions, 26 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index 355a17d..2894c12 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -1510,10 +1510,12 @@ MLINKS+=sglist.9 sglist_alloc.9 \
sglist.9 sglist_append_phys.9 \
sglist.9 sglist_append_uio.9 \
sglist.9 sglist_append_user.9 \
+ sglist.9 sglist_append_vmpages.9 \
sglist.9 sglist_build.9 \
sglist.9 sglist_clone.9 \
sglist.9 sglist_consume_uio.9 \
sglist.9 sglist_count.9 \
+ sglist.9 sglist_count_vmpages.9 \
sglist.9 sglist_free.9 \
sglist.9 sglist_hold.9 \
sglist.9 sglist_init.9 \
diff --git a/share/man/man9/sglist.9 b/share/man/man9/sglist.9
index a9b34de..f45d261 100644
--- a/share/man/man9/sglist.9
+++ b/share/man/man9/sglist.9
@@ -38,10 +38,12 @@
.Nm sglist_append_phys ,
.Nm sglist_append_uio ,
.Nm sglist_append_user ,
+.Nm sglist_append_vmpages ,
.Nm sglist_build ,
.Nm sglist_clone ,
.Nm sglist_consume_uio ,
.Nm sglist_count ,
+.Nm sglist_count_vmpages ,
.Nm sglist_free ,
.Nm sglist_hold ,
.Nm sglist_init ,
@@ -68,6 +70,8 @@
.Fn sglist_append_uio "struct sglist *sg" "struct uio *uio"
.Ft int
.Fn sglist_append_user "struct sglist *sg" "void *buf" "size_t len" "struct thread *td"
+.Ft int
+.Fn sglist_append_vmpages "struct sglist *sg" "vm_page_t *m" "size_t pgoff" "size_t len"
.Ft struct sglist *
.Fn sglist_build "void *buf" "size_t len" "int mflags"
.Ft struct sglist *
@@ -76,6 +80,8 @@
.Fn sglist_consume_uio "struct sglist *sg" "struct uio *uio" "size_t resid"
.Ft int
.Fn sglist_count "void *buf" "size_t len"
+.Ft int
+.Fn sglist_count_vmpages "vm_page_t *m" "size_t pgoff" "size_t len"
.Ft void
.Fn sglist_free "struct sglist *sg"
.Ft struct sglist *
@@ -137,6 +143,18 @@ and is
bytes long.
.Pp
The
+.Nm sglist_count_vmpages
+function returns the number of scatter/gather list elements needed to describe
+the physical address ranges of a buffer backed by an array of virtual memory
+pages
+.Fa m .
+The buffer starts at an offset of
+.Fa pgoff
+bytes relative to the first page and is
+.Fa len
+bytes long.
+.Pp
+The
.Nm sglist_build
function allocates a new scatter/gather list object that describes the physical
address ranges mapped by a single kernel virtual address range.
@@ -262,6 +280,17 @@ the user buffer are wired for the lifetime of
.Fa sg .
.Pp
The
+.Nm sglist_append_vmpages
+function appends the physical address ranges of a buffer backed by an array
+of virtual memory pages
+.Fa m .
+The buffer starts at an offset of
+.Fa pgoff
+bytes relative to the first page and is
+.Fa len
+bytes long.
+.Pp
+The
.Nm sglist_consume_uio
function is a variation of
.Nm sglist_append_uio .
@@ -421,7 +450,9 @@ functions return zero on success or an error on failure.
.Pp
The
.Nm sglist_count
-function returns a count of scatter/gather list elements.
+and
+.Nm sglist_count_vmpages
+functions return a count of scatter/gather list elements.
.Pp
The
.Nm sglist_length
diff --git a/sys/kern/subr_sglist.c b/sys/kern/subr_sglist.c
index df88a26..0d371a4 100644
--- a/sys/kern/subr_sglist.c
+++ b/sys/kern/subr_sglist.c
@@ -192,6 +192,31 @@ sglist_count(void *buf, size_t len)
}
/*
+ * Determine the number of scatter/gather list elements needed to
+ * describe a buffer backed by an array of VM pages.
+ */
+int
+sglist_count_vmpages(vm_page_t *m, size_t pgoff, size_t len)
+{
+ vm_paddr_t lastaddr, paddr;
+ int i, nsegs;
+
+ if (len == 0)
+ return (0);
+
+ len += pgoff;
+ nsegs = 1;
+ lastaddr = VM_PAGE_TO_PHYS(m[0]);
+ for (i = 1; len > PAGE_SIZE; len -= PAGE_SIZE, i++) {
+ paddr = VM_PAGE_TO_PHYS(m[i]);
+ if (lastaddr + PAGE_SIZE != paddr)
+ nsegs++;
+ lastaddr = paddr;
+ }
+ return (nsegs);
+}
+
+/*
* Allocate a scatter/gather list along with 'nsegs' segments. The
* 'mflags' parameters are the same as passed to malloc(9). The caller
* should use sglist_free() to free this list.
@@ -252,33 +277,14 @@ sglist_append(struct sglist *sg, void *buf, size_t len)
int
sglist_append_bio(struct sglist *sg, struct bio *bp)
{
- struct sgsave save;
- vm_paddr_t paddr;
- size_t len, tlen;
- int error, i, ma_offs;
+ int error;
- if ((bp->bio_flags & BIO_UNMAPPED) == 0) {
+ if ((bp->bio_flags & BIO_UNMAPPED) == 0)
error = sglist_append(sg, bp->bio_data, bp->bio_bcount);
- return (error);
- }
-
- if (sg->sg_maxseg == 0)
- return (EINVAL);
-
- SGLIST_SAVE(sg, save);
- tlen = bp->bio_bcount;
- ma_offs = bp->bio_ma_offset;
- for (i = 0; tlen > 0; i++, tlen -= len) {
- len = min(PAGE_SIZE - ma_offs, tlen);
- paddr = VM_PAGE_TO_PHYS(bp->bio_ma[i]) + ma_offs;
- error = sglist_append_phys(sg, paddr, len);
- if (error) {
- SGLIST_RESTORE(sg, save);
- return (error);
- }
- ma_offs = 0;
- }
- return (0);
+ else
+ error = sglist_append_vmpages(sg, bp->bio_ma,
+ bp->bio_ma_offset, bp->bio_bcount);
+ return (error);
}
/*
@@ -341,6 +347,51 @@ sglist_append_mbuf(struct sglist *sg, struct mbuf *m0)
}
/*
+ * Append the segments that describe a buffer spanning an array of VM
+ * pages. The buffer begins at an offset of 'pgoff' in the first
+ * page.
+ */
+int
+sglist_append_vmpages(struct sglist *sg, vm_page_t *m, size_t pgoff,
+ size_t len)
+{
+ struct sgsave save;
+ struct sglist_seg *ss;
+ vm_paddr_t paddr;
+ size_t seglen;
+ int error, i;
+
+ if (sg->sg_maxseg == 0)
+ return (EINVAL);
+ if (len == 0)
+ return (0);
+
+ SGLIST_SAVE(sg, save);
+ i = 0;
+ if (sg->sg_nseg == 0) {
+ seglen = min(PAGE_SIZE - pgoff, len);
+ sg->sg_segs[0].ss_paddr = VM_PAGE_TO_PHYS(m[0]) + pgoff;
+ sg->sg_segs[0].ss_len = seglen;
+ sg->sg_nseg = 1;
+ pgoff = 0;
+ len -= seglen;
+ i++;
+ }
+ ss = &sg->sg_segs[sg->sg_nseg - 1];
+ for (; len > 0; i++, len -= seglen) {
+ seglen = min(PAGE_SIZE - pgoff, len);
+ paddr = VM_PAGE_TO_PHYS(m[i]) + pgoff;
+ error = _sglist_append_range(sg, &ss, paddr, seglen);
+ if (error) {
+ SGLIST_RESTORE(sg, save);
+ return (error);
+ }
+ pgoff = 0;
+ }
+ return (0);
+}
+
+/*
* Append the segments that describe a single user address range to a
* scatter/gather list. If there are insufficient segments, then this
* fails with EFBIG.
diff --git a/sys/sys/sglist.h b/sys/sys/sglist.h
index c712f63..1c09858 100644
--- a/sys/sys/sglist.h
+++ b/sys/sys/sglist.h
@@ -91,10 +91,13 @@ int sglist_append_phys(struct sglist *sg, vm_paddr_t paddr,
int sglist_append_uio(struct sglist *sg, struct uio *uio);
int sglist_append_user(struct sglist *sg, void *buf, size_t len,
struct thread *td);
+int sglist_append_vmpages(struct sglist *sg, vm_page_t *m, size_t pgoff,
+ size_t len);
struct sglist *sglist_build(void *buf, size_t len, int mflags);
struct sglist *sglist_clone(struct sglist *sg, int mflags);
int sglist_consume_uio(struct sglist *sg, struct uio *uio, size_t resid);
int sglist_count(void *buf, size_t len);
+int sglist_count_vmpages(vm_page_t *m, size_t pgoff, size_t len);
void sglist_free(struct sglist *sg);
int sglist_join(struct sglist *first, struct sglist *second);
size_t sglist_length(struct sglist *sg);
OpenPOWER on IntegriCloud