in xen-scsiback.c [430:539]
static int scsiback_gnttab_data_map(struct vscsiif_request *ring_req,
struct vscsibk_pend *pending_req)
{
u32 flags;
int i, err, n_segs, i_seg = 0;
struct page **pg;
struct scsiif_request_segment *seg;
unsigned long end_seg = 0;
unsigned int nr_segments = (unsigned int)ring_req->nr_segments;
unsigned int nr_sgl = 0;
struct scatterlist *sg;
grant_handle_t *grant;
pending_req->n_sg = 0;
pending_req->n_grants = 0;
pending_req->data_len = 0;
nr_segments &= ~VSCSIIF_SG_GRANT;
if (!nr_segments)
return 0;
if (nr_segments > VSCSIIF_SG_TABLESIZE) {
pr_debug("invalid parameter nr_seg = %d\n",
ring_req->nr_segments);
return -EINVAL;
}
if (ring_req->nr_segments & VSCSIIF_SG_GRANT) {
err = scsiback_gnttab_data_map_list(pending_req, ring_req->seg,
pending_req->pages, pending_req->grant_handles,
nr_segments, GNTMAP_host_map | GNTMAP_readonly);
if (err)
return err;
nr_sgl = nr_segments;
nr_segments = 0;
for (i = 0; i < nr_sgl; i++) {
n_segs = ring_req->seg[i].length /
sizeof(struct scsiif_request_segment);
if ((unsigned)ring_req->seg[i].offset +
(unsigned)ring_req->seg[i].length > PAGE_SIZE ||
n_segs * sizeof(struct scsiif_request_segment) !=
ring_req->seg[i].length)
return -EINVAL;
nr_segments += n_segs;
}
if (nr_segments > SG_ALL) {
pr_debug("invalid nr_seg = %d\n", nr_segments);
return -EINVAL;
}
}
/* free of (sgl) in fast_flush_area() */
pending_req->sgl = kmalloc_array(nr_segments,
sizeof(struct scatterlist), GFP_KERNEL);
if (!pending_req->sgl)
return -ENOMEM;
sg_init_table(pending_req->sgl, nr_segments);
pending_req->n_sg = nr_segments;
flags = GNTMAP_host_map;
if (pending_req->sc_data_direction == DMA_TO_DEVICE)
flags |= GNTMAP_readonly;
pg = pending_req->pages + nr_sgl;
grant = pending_req->grant_handles + nr_sgl;
if (!nr_sgl) {
seg = ring_req->seg;
err = scsiback_gnttab_data_map_list(pending_req, seg,
pg, grant, nr_segments, flags);
if (err)
return err;
} else {
for (i = 0; i < nr_sgl; i++) {
seg = (struct scsiif_request_segment *)(
vaddr(pending_req, i) + ring_req->seg[i].offset);
n_segs = ring_req->seg[i].length /
sizeof(struct scsiif_request_segment);
err = scsiback_gnttab_data_map_list(pending_req, seg,
pg, grant, n_segs, flags);
if (err)
return err;
pg += n_segs;
grant += n_segs;
}
end_seg = vaddr(pending_req, 0) + ring_req->seg[0].offset;
seg = (struct scsiif_request_segment *)end_seg;
end_seg += ring_req->seg[0].length;
pg = pending_req->pages + nr_sgl;
}
for_each_sg(pending_req->sgl, sg, nr_segments, i) {
sg_set_page(sg, pg[i], seg->length, seg->offset);
pending_req->data_len += seg->length;
seg++;
if (nr_sgl && (unsigned long)seg >= end_seg) {
i_seg++;
end_seg = vaddr(pending_req, i_seg) +
ring_req->seg[i_seg].offset;
seg = (struct scsiif_request_segment *)end_seg;
end_seg += ring_req->seg[i_seg].length;
}
if (sg->offset >= PAGE_SIZE ||
sg->length > PAGE_SIZE ||
sg->offset + sg->length > PAGE_SIZE)
return -EINVAL;
}
return 0;
}