static int scsiback_gnttab_data_map()

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;
}