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:
parent
5a01e6efad
commit
ca492fdb2d
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user