summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/subr_sglist.c43
-rw-r--r--sys/sys/sglist.h2
2 files changed, 45 insertions, 0 deletions
diff --git a/sys/kern/subr_sglist.c b/sys/kern/subr_sglist.c
index 0d371a48ded3..dbc2a5e72fee 100644
--- a/sys/kern/subr_sglist.c
+++ b/sys/kern/subr_sglist.c
@@ -413,6 +413,49 @@ sglist_append_user(struct sglist *sg, void *buf, size_t len, struct thread *td)
}
/*
+ * Append a subset of an existing scatter/gather list 'source' to a
+ * the scatter/gather list 'sg'. If there are insufficient segments,
+ * then this fails with EFBIG.
+ */
+int
+sglist_append_sglist(struct sglist *sg, struct sglist *source, size_t offset,
+ size_t length)
+{
+ struct sgsave save;
+ struct sglist_seg *ss;
+ size_t seglen;
+ int error, i;
+
+ 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)
+ error = EINVAL;
+ if (error)
+ SGLIST_RESTORE(sg, save);
+ return (error);
+}
+
+/*
* Append the segments that describe a single uio 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 1c0985803ebd..7d22e28ecc84 100644
--- a/sys/sys/sglist.h
+++ b/sys/sys/sglist.h
@@ -88,6 +88,8 @@ int sglist_append_bio(struct sglist *sg, struct bio *bp);
int sglist_append_mbuf(struct sglist *sg, struct mbuf *m0);
int sglist_append_phys(struct sglist *sg, vm_paddr_t paddr,
size_t len);
+int sglist_append_sglist(struct sglist *sg, struct sglist *source,
+ size_t offset, size_t length);
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);