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
This commit is contained in:
jhb 2016-05-20 23:28:43 +00:00
parent 5a01e6efad
commit ca492fdb2d
4 changed files with 113 additions and 26 deletions

View File

@ -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 \

View File

@ -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

View File

@ -191,6 +191,31 @@ sglist_count(void *buf, size_t len)
return (nsegs);
}
/*
* 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
@ -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);
}
/*
@ -340,6 +346,51 @@ sglist_append_mbuf(struct sglist *sg, struct mbuf *m0)
return (0);
}
/*
* 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

View File

@ -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);