868 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			868 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | ||
| /*
 | ||
|  * (C) 2007-2008 Samuel Thibault.
 | ||
|  * (C) Copyright 2020 EPAM Systems Inc.
 | ||
|  */
 | ||
| #include <blk.h>
 | ||
| #include <common.h>
 | ||
| #include <dm.h>
 | ||
| #include <dm/device-internal.h>
 | ||
| #include <malloc.h>
 | ||
| #include <part.h>
 | ||
| 
 | ||
| #include <asm/armv8/mmu.h>
 | ||
| #include <asm/io.h>
 | ||
| #include <asm/xen/system.h>
 | ||
| 
 | ||
| #include <linux/bug.h>
 | ||
| #include <linux/compat.h>
 | ||
| 
 | ||
| #include <xen/events.h>
 | ||
| #include <xen/gnttab.h>
 | ||
| #include <xen/hvm.h>
 | ||
| #include <xen/xenbus.h>
 | ||
| 
 | ||
| #include <xen/interface/io/ring.h>
 | ||
| #include <xen/interface/io/blkif.h>
 | ||
| #include <xen/interface/io/protocols.h>
 | ||
| 
 | ||
| #define DRV_NAME	"pvblock"
 | ||
| #define DRV_NAME_BLK	"pvblock_blk"
 | ||
| 
 | ||
| #define O_RDONLY	00
 | ||
| #define O_RDWR		02
 | ||
| #define WAIT_RING_TO_MS	10
 | ||
| 
 | ||
| struct blkfront_info {
 | ||
| 	u64 sectors;
 | ||
| 	unsigned int sector_size;
 | ||
| 	int mode;
 | ||
| 	int info;
 | ||
| 	int barrier;
 | ||
| 	int flush;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * struct blkfront_dev - Struct representing blkfront device
 | ||
|  * @dom: Domain id
 | ||
|  * @ring: Front_ring structure
 | ||
|  * @ring_ref: The grant reference, allowing us to grant access
 | ||
|  *	      to the ring to the other end/domain
 | ||
|  * @evtchn: Event channel used to signal ring events
 | ||
|  * @handle: Events handle
 | ||
|  * @nodename: Device XenStore path in format "device/vbd/" + @devid
 | ||
|  * @backend: Backend XenStore path
 | ||
|  * @info: Private data
 | ||
|  * @devid: Device id
 | ||
|  */
 | ||
| struct blkfront_dev {
 | ||
| 	domid_t dom;
 | ||
| 
 | ||
| 	struct blkif_front_ring ring;
 | ||
| 	grant_ref_t ring_ref;
 | ||
| 	evtchn_port_t evtchn;
 | ||
| 	blkif_vdev_t handle;
 | ||
| 
 | ||
| 	char *nodename;
 | ||
| 	char *backend;
 | ||
| 	struct blkfront_info info;
 | ||
| 	unsigned int devid;
 | ||
| 	u8 *bounce_buffer;
 | ||
| };
 | ||
| 
 | ||
| struct blkfront_platdata {
 | ||
| 	unsigned int devid;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * struct blkfront_aiocb - AIO сontrol block
 | ||
|  * @aio_dev: Blockfront device
 | ||
|  * @aio_buf: Memory buffer, which must be sector-aligned for
 | ||
|  *	     @aio_dev sector
 | ||
|  * @aio_nbytes: Size of AIO, which must be less than @aio_dev
 | ||
|  *		sector-sized amounts
 | ||
|  * @aio_offset: Offset, which must not go beyond @aio_dev
 | ||
|  *		sector-aligned location
 | ||
|  * @data: Data used to receiving response from ring
 | ||
|  * @gref: Array of grant references
 | ||
|  * @n: Number of segments
 | ||
|  * @aio_cb: Represents one I/O request.
 | ||
|  */
 | ||
| struct blkfront_aiocb {
 | ||
| 	struct blkfront_dev *aio_dev;
 | ||
| 	u8 *aio_buf;
 | ||
| 	size_t aio_nbytes;
 | ||
| 	off_t aio_offset;
 | ||
| 	void *data;
 | ||
| 
 | ||
| 	grant_ref_t gref[BLKIF_MAX_SEGMENTS_PER_REQUEST];
 | ||
| 	int n;
 | ||
| 
 | ||
| 	void (*aio_cb)(struct blkfront_aiocb *aiocb, int ret);
 | ||
| };
 | ||
| 
 | ||
| static void blkfront_sync(struct blkfront_dev *dev);
 | ||
| 
 | ||
| static void free_blkfront(struct blkfront_dev *dev)
 | ||
| {
 | ||
| 	mask_evtchn(dev->evtchn);
 | ||
| 	free(dev->backend);
 | ||
| 
 | ||
| 	gnttab_end_access(dev->ring_ref);
 | ||
| 	free(dev->ring.sring);
 | ||
| 
 | ||
| 	unbind_evtchn(dev->evtchn);
 | ||
| 
 | ||
| 	free(dev->bounce_buffer);
 | ||
| 	free(dev->nodename);
 | ||
| 	free(dev);
 | ||
| }
 | ||
| 
 | ||
| static int init_blkfront(unsigned int devid, struct blkfront_dev *dev)
 | ||
| {
 | ||
| 	xenbus_transaction_t xbt;
 | ||
| 	char *err = NULL;
 | ||
| 	char *message = NULL;
 | ||
| 	struct blkif_sring *s;
 | ||
| 	int retry = 0;
 | ||
| 	char *msg = NULL;
 | ||
| 	char *c;
 | ||
| 	char nodename[32];
 | ||
| 	char path[ARRAY_SIZE(nodename) + strlen("/backend-id") + 1];
 | ||
| 
 | ||
| 	sprintf(nodename, "device/vbd/%d", devid);
 | ||
| 
 | ||
| 	memset(dev, 0, sizeof(*dev));
 | ||
| 	dev->nodename = strdup(nodename);
 | ||
| 	dev->devid = devid;
 | ||
| 
 | ||
| 	snprintf(path, sizeof(path), "%s/backend-id", nodename);
 | ||
| 	dev->dom = xenbus_read_integer(path);
 | ||
| 	evtchn_alloc_unbound(dev->dom, NULL, dev, &dev->evtchn);
 | ||
| 
 | ||
| 	s = (struct blkif_sring *)memalign(PAGE_SIZE, PAGE_SIZE);
 | ||
| 	if (!s) {
 | ||
| 		printf("Failed to allocate shared ring\n");
 | ||
| 		goto error;
 | ||
| 	}
 | ||
| 
 | ||
| 	SHARED_RING_INIT(s);
 | ||
| 	FRONT_RING_INIT(&dev->ring, s, PAGE_SIZE);
 | ||
| 
 | ||
| 	dev->ring_ref = gnttab_grant_access(dev->dom, virt_to_pfn(s), 0);
 | ||
| 
 | ||
| again:
 | ||
| 	err = xenbus_transaction_start(&xbt);
 | ||
| 	if (err) {
 | ||
| 		printf("starting transaction\n");
 | ||
| 		free(err);
 | ||
| 	}
 | ||
| 
 | ||
| 	err = xenbus_printf(xbt, nodename, "ring-ref", "%u", dev->ring_ref);
 | ||
| 	if (err) {
 | ||
| 		message = "writing ring-ref";
 | ||
| 		goto abort_transaction;
 | ||
| 	}
 | ||
| 	err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn);
 | ||
| 	if (err) {
 | ||
| 		message = "writing event-channel";
 | ||
| 		goto abort_transaction;
 | ||
| 	}
 | ||
| 	err = xenbus_printf(xbt, nodename, "protocol", "%s",
 | ||
| 			    XEN_IO_PROTO_ABI_NATIVE);
 | ||
| 	if (err) {
 | ||
| 		message = "writing protocol";
 | ||
| 		goto abort_transaction;
 | ||
| 	}
 | ||
| 
 | ||
| 	snprintf(path, sizeof(path), "%s/state", nodename);
 | ||
| 	err = xenbus_switch_state(xbt, path, XenbusStateConnected);
 | ||
| 	if (err) {
 | ||
| 		message = "switching state";
 | ||
| 		goto abort_transaction;
 | ||
| 	}
 | ||
| 
 | ||
| 	err = xenbus_transaction_end(xbt, 0, &retry);
 | ||
| 	free(err);
 | ||
| 	if (retry) {
 | ||
| 		goto again;
 | ||
| 		printf("completing transaction\n");
 | ||
| 	}
 | ||
| 
 | ||
| 	goto done;
 | ||
| 
 | ||
| abort_transaction:
 | ||
| 	free(err);
 | ||
| 	err = xenbus_transaction_end(xbt, 1, &retry);
 | ||
| 	printf("Abort transaction %s\n", message);
 | ||
| 	goto error;
 | ||
| 
 | ||
| done:
 | ||
| 	snprintf(path, sizeof(path), "%s/backend", nodename);
 | ||
| 	msg = xenbus_read(XBT_NIL, path, &dev->backend);
 | ||
| 	if (msg) {
 | ||
| 		printf("Error %s when reading the backend path %s\n",
 | ||
| 		       msg, path);
 | ||
| 		goto error;
 | ||
| 	}
 | ||
| 
 | ||
| 	dev->handle = strtoul(strrchr(nodename, '/') + 1, NULL, 0);
 | ||
| 
 | ||
| 	{
 | ||
| 		XenbusState state;
 | ||
| 		char path[strlen(dev->backend) +
 | ||
| 			strlen("/feature-flush-cache") + 1];
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/mode", dev->backend);
 | ||
| 		msg = xenbus_read(XBT_NIL, path, &c);
 | ||
| 		if (msg) {
 | ||
| 			printf("Error %s when reading the mode\n", msg);
 | ||
| 			goto error;
 | ||
| 		}
 | ||
| 		if (*c == 'w')
 | ||
| 			dev->info.mode = O_RDWR;
 | ||
| 		else
 | ||
| 			dev->info.mode = O_RDONLY;
 | ||
| 		free(c);
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/state", dev->backend);
 | ||
| 
 | ||
| 		msg = NULL;
 | ||
| 		state = xenbus_read_integer(path);
 | ||
| 		while (!msg && state < XenbusStateConnected)
 | ||
| 			msg = xenbus_wait_for_state_change(path, &state);
 | ||
| 		if (msg || state != XenbusStateConnected) {
 | ||
| 			printf("backend not available, state=%d\n", state);
 | ||
| 			goto error;
 | ||
| 		}
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/info", dev->backend);
 | ||
| 		dev->info.info = xenbus_read_integer(path);
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/sectors", dev->backend);
 | ||
| 		/*
 | ||
| 		 * FIXME: read_integer returns an int, so disk size
 | ||
| 		 * limited to 1TB for now
 | ||
| 		 */
 | ||
| 		dev->info.sectors = xenbus_read_integer(path);
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/sector-size", dev->backend);
 | ||
| 		dev->info.sector_size = xenbus_read_integer(path);
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/feature-barrier",
 | ||
| 			 dev->backend);
 | ||
| 		dev->info.barrier = xenbus_read_integer(path);
 | ||
| 
 | ||
| 		snprintf(path, sizeof(path), "%s/feature-flush-cache",
 | ||
| 			 dev->backend);
 | ||
| 		dev->info.flush = xenbus_read_integer(path);
 | ||
| 	}
 | ||
| 	unmask_evtchn(dev->evtchn);
 | ||
| 
 | ||
| 	dev->bounce_buffer = memalign(dev->info.sector_size,
 | ||
| 				      dev->info.sector_size);
 | ||
| 	if (!dev->bounce_buffer) {
 | ||
| 		printf("Failed to allocate bouncing buffer\n");
 | ||
| 		goto error;
 | ||
| 	}
 | ||
| 
 | ||
| 	debug("%llu sectors of %u bytes, bounce buffer at %p\n",
 | ||
| 	      dev->info.sectors, dev->info.sector_size,
 | ||
| 	      dev->bounce_buffer);
 | ||
| 
 | ||
| 	return 0;
 | ||
| 
 | ||
| error:
 | ||
| 	free(msg);
 | ||
| 	free(err);
 | ||
| 	free_blkfront(dev);
 | ||
| 	return -ENODEV;
 | ||
| }
 | ||
| 
 | ||
| static void shutdown_blkfront(struct blkfront_dev *dev)
 | ||
| {
 | ||
| 	char *err = NULL, *err2;
 | ||
| 	XenbusState state;
 | ||
| 
 | ||
| 	char path[strlen(dev->backend) + strlen("/state") + 1];
 | ||
| 	char nodename[strlen(dev->nodename) + strlen("/event-channel") + 1];
 | ||
| 
 | ||
| 	debug("Close " DRV_NAME ", device ID %d\n", dev->devid);
 | ||
| 
 | ||
| 	blkfront_sync(dev);
 | ||
| 
 | ||
| 	snprintf(path, sizeof(path), "%s/state", dev->backend);
 | ||
| 	snprintf(nodename, sizeof(nodename), "%s/state", dev->nodename);
 | ||
| 
 | ||
| 	err = xenbus_switch_state(XBT_NIL, nodename, XenbusStateClosing);
 | ||
| 	if (err) {
 | ||
| 		printf("%s: error changing state to %d: %s\n", __func__,
 | ||
| 		       XenbusStateClosing, err);
 | ||
| 		goto close;
 | ||
| 	}
 | ||
| 
 | ||
| 	state = xenbus_read_integer(path);
 | ||
| 	while (!err && state < XenbusStateClosing)
 | ||
| 		err = xenbus_wait_for_state_change(path, &state);
 | ||
| 	free(err);
 | ||
| 
 | ||
| 	err = xenbus_switch_state(XBT_NIL, nodename, XenbusStateClosed);
 | ||
| 	if (err) {
 | ||
| 		printf("%s: error changing state to %d: %s\n", __func__,
 | ||
| 		       XenbusStateClosed, err);
 | ||
| 		goto close;
 | ||
| 	}
 | ||
| 
 | ||
| 	state = xenbus_read_integer(path);
 | ||
| 	while (state < XenbusStateClosed) {
 | ||
| 		err = xenbus_wait_for_state_change(path, &state);
 | ||
| 		free(err);
 | ||
| 	}
 | ||
| 
 | ||
| 	err = xenbus_switch_state(XBT_NIL, nodename, XenbusStateInitialising);
 | ||
| 	if (err) {
 | ||
| 		printf("%s: error changing state to %d: %s\n", __func__,
 | ||
| 		       XenbusStateInitialising, err);
 | ||
| 		goto close;
 | ||
| 	}
 | ||
| 
 | ||
| 	state = xenbus_read_integer(path);
 | ||
| 	while (!err &&
 | ||
| 	       (state < XenbusStateInitWait || state >= XenbusStateClosed))
 | ||
| 		err = xenbus_wait_for_state_change(path, &state);
 | ||
| 
 | ||
| close:
 | ||
| 	free(err);
 | ||
| 
 | ||
| 	snprintf(nodename, sizeof(nodename), "%s/ring-ref", dev->nodename);
 | ||
| 	err2 = xenbus_rm(XBT_NIL, nodename);
 | ||
| 	free(err2);
 | ||
| 	snprintf(nodename, sizeof(nodename), "%s/event-channel", dev->nodename);
 | ||
| 	err2 = xenbus_rm(XBT_NIL, nodename);
 | ||
| 	free(err2);
 | ||
| 
 | ||
| 	if (!err)
 | ||
| 		free_blkfront(dev);
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * blkfront_aio_poll() - AIO polling function.
 | ||
|  * @dev: Blkfront device
 | ||
|  *
 | ||
|  * Here we receive response from the ring and check its status. This happens
 | ||
|  * until we read all data from the ring. We read the data from consumed pointer
 | ||
|  * to the response pointer. Then increase consumed pointer to make it clear that
 | ||
|  * the data has been read.
 | ||
|  *
 | ||
|  * Return: Number of consumed bytes.
 | ||
|  */
 | ||
| static int blkfront_aio_poll(struct blkfront_dev *dev)
 | ||
| {
 | ||
| 	RING_IDX rp, cons;
 | ||
| 	struct blkif_response *rsp;
 | ||
| 	int more;
 | ||
| 	int nr_consumed;
 | ||
| 
 | ||
| moretodo:
 | ||
| 	rp = dev->ring.sring->rsp_prod;
 | ||
| 	rmb(); /* Ensure we see queued responses up to 'rp'. */
 | ||
| 	cons = dev->ring.rsp_cons;
 | ||
| 
 | ||
| 	nr_consumed = 0;
 | ||
| 	while ((cons != rp)) {
 | ||
| 		struct blkfront_aiocb *aiocbp;
 | ||
| 		int status;
 | ||
| 
 | ||
| 		rsp = RING_GET_RESPONSE(&dev->ring, cons);
 | ||
| 		nr_consumed++;
 | ||
| 
 | ||
| 		aiocbp = (void *)(uintptr_t)rsp->id;
 | ||
| 		status = rsp->status;
 | ||
| 
 | ||
| 		switch (rsp->operation) {
 | ||
| 		case BLKIF_OP_READ:
 | ||
| 		case BLKIF_OP_WRITE:
 | ||
| 		{
 | ||
| 			int j;
 | ||
| 
 | ||
| 			if (status != BLKIF_RSP_OKAY)
 | ||
| 				printf("%s error %d on %s at offset %llu, num bytes %llu\n",
 | ||
| 				       rsp->operation == BLKIF_OP_READ ?
 | ||
| 				       "read" : "write",
 | ||
| 				       status, aiocbp->aio_dev->nodename,
 | ||
| 				       (unsigned long long)aiocbp->aio_offset,
 | ||
| 				       (unsigned long long)aiocbp->aio_nbytes);
 | ||
| 
 | ||
| 			for (j = 0; j < aiocbp->n; j++)
 | ||
| 				gnttab_end_access(aiocbp->gref[j]);
 | ||
| 
 | ||
| 			break;
 | ||
| 		}
 | ||
| 
 | ||
| 		case BLKIF_OP_WRITE_BARRIER:
 | ||
| 			if (status != BLKIF_RSP_OKAY)
 | ||
| 				printf("write barrier error %d\n", status);
 | ||
| 			break;
 | ||
| 		case BLKIF_OP_FLUSH_DISKCACHE:
 | ||
| 			if (status != BLKIF_RSP_OKAY)
 | ||
| 				printf("flush error %d\n", status);
 | ||
| 			break;
 | ||
| 
 | ||
| 		default:
 | ||
| 			printf("unrecognized block operation %d response (status %d)\n",
 | ||
| 			       rsp->operation, status);
 | ||
| 			break;
 | ||
| 		}
 | ||
| 
 | ||
| 		dev->ring.rsp_cons = ++cons;
 | ||
| 		/* Nota: callback frees aiocbp itself */
 | ||
| 		if (aiocbp && aiocbp->aio_cb)
 | ||
| 			aiocbp->aio_cb(aiocbp, status ? -EIO : 0);
 | ||
| 		if (dev->ring.rsp_cons != cons)
 | ||
| 			/* We reentered, we must not continue here */
 | ||
| 			break;
 | ||
| 	}
 | ||
| 
 | ||
| 	RING_FINAL_CHECK_FOR_RESPONSES(&dev->ring, more);
 | ||
| 	if (more)
 | ||
| 		goto moretodo;
 | ||
| 
 | ||
| 	return nr_consumed;
 | ||
| }
 | ||
| 
 | ||
| static void blkfront_wait_slot(struct blkfront_dev *dev)
 | ||
| {
 | ||
| 	/* Wait for a slot */
 | ||
| 	if (RING_FULL(&dev->ring)) {
 | ||
| 		while (true) {
 | ||
| 			blkfront_aio_poll(dev);
 | ||
| 			if (!RING_FULL(&dev->ring))
 | ||
| 				break;
 | ||
| 			wait_event_timeout(NULL, !RING_FULL(&dev->ring),
 | ||
| 					   WAIT_RING_TO_MS);
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * blkfront_aio_poll() - Issue an aio.
 | ||
|  * @aiocbp: AIO control block structure
 | ||
|  * @write: Describes is it read or write operation
 | ||
|  *	   0 - read
 | ||
|  *	   1 - write
 | ||
|  *
 | ||
|  * We check whether the AIO parameters meet the requirements of the device.
 | ||
|  * Then receive request from ring and define its arguments. After this we
 | ||
|  * grant access to the grant references. The last step is notifying about AIO
 | ||
|  * via event channel.
 | ||
|  */
 | ||
| static void blkfront_aio(struct blkfront_aiocb *aiocbp, int write)
 | ||
| {
 | ||
| 	struct blkfront_dev *dev = aiocbp->aio_dev;
 | ||
| 	struct blkif_request *req;
 | ||
| 	RING_IDX i;
 | ||
| 	int notify;
 | ||
| 	int n, j;
 | ||
| 	uintptr_t start, end;
 | ||
| 
 | ||
| 	/* Can't io at non-sector-aligned location */
 | ||
| 	BUG_ON(aiocbp->aio_offset & (dev->info.sector_size - 1));
 | ||
| 	/* Can't io non-sector-sized amounts */
 | ||
| 	BUG_ON(aiocbp->aio_nbytes & (dev->info.sector_size - 1));
 | ||
| 	/* Can't io non-sector-aligned buffer */
 | ||
| 	BUG_ON(((uintptr_t)aiocbp->aio_buf & (dev->info.sector_size - 1)));
 | ||
| 
 | ||
| 	start = (uintptr_t)aiocbp->aio_buf & PAGE_MASK;
 | ||
| 	end = ((uintptr_t)aiocbp->aio_buf + aiocbp->aio_nbytes +
 | ||
| 	       PAGE_SIZE - 1) & PAGE_MASK;
 | ||
| 	n = (end - start) / PAGE_SIZE;
 | ||
| 	aiocbp->n = n;
 | ||
| 
 | ||
| 	BUG_ON(n > BLKIF_MAX_SEGMENTS_PER_REQUEST);
 | ||
| 
 | ||
| 	blkfront_wait_slot(dev);
 | ||
| 	i = dev->ring.req_prod_pvt;
 | ||
| 	req = RING_GET_REQUEST(&dev->ring, i);
 | ||
| 
 | ||
| 	req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ;
 | ||
| 	req->nr_segments = n;
 | ||
| 	req->handle = dev->handle;
 | ||
| 	req->id = (uintptr_t)aiocbp;
 | ||
| 	req->sector_number = aiocbp->aio_offset / dev->info.sector_size;
 | ||
| 
 | ||
| 	for (j = 0; j < n; j++) {
 | ||
| 		req->seg[j].first_sect = 0;
 | ||
| 		req->seg[j].last_sect = PAGE_SIZE / dev->info.sector_size - 1;
 | ||
| 	}
 | ||
| 	req->seg[0].first_sect = ((uintptr_t)aiocbp->aio_buf & ~PAGE_MASK) /
 | ||
| 		dev->info.sector_size;
 | ||
| 	req->seg[n - 1].last_sect = (((uintptr_t)aiocbp->aio_buf +
 | ||
| 		aiocbp->aio_nbytes - 1) & ~PAGE_MASK) / dev->info.sector_size;
 | ||
| 	for (j = 0; j < n; j++) {
 | ||
| 		uintptr_t data = start + j * PAGE_SIZE;
 | ||
| 
 | ||
| 		if (!write) {
 | ||
| 			/* Trigger CoW if needed */
 | ||
| 			*(char *)(data + (req->seg[j].first_sect *
 | ||
| 					  dev->info.sector_size)) = 0;
 | ||
| 			barrier();
 | ||
| 		}
 | ||
| 		req->seg[j].gref = gnttab_grant_access(dev->dom,
 | ||
| 						       virt_to_pfn((void *)data),
 | ||
| 						       write);
 | ||
| 		aiocbp->gref[j] = req->seg[j].gref;
 | ||
| 	}
 | ||
| 
 | ||
| 	dev->ring.req_prod_pvt = i + 1;
 | ||
| 
 | ||
| 	wmb();
 | ||
| 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify);
 | ||
| 
 | ||
| 	if (notify)
 | ||
| 		notify_remote_via_evtchn(dev->evtchn);
 | ||
| }
 | ||
| 
 | ||
| static void blkfront_aio_cb(struct blkfront_aiocb *aiocbp, int ret)
 | ||
| {
 | ||
| 	aiocbp->data = (void *)1;
 | ||
| 	aiocbp->aio_cb = NULL;
 | ||
| }
 | ||
| 
 | ||
| static void blkfront_io(struct blkfront_aiocb *aiocbp, int write)
 | ||
| {
 | ||
| 	aiocbp->aio_cb = blkfront_aio_cb;
 | ||
| 	blkfront_aio(aiocbp, write);
 | ||
| 	aiocbp->data = NULL;
 | ||
| 
 | ||
| 	while (true) {
 | ||
| 		blkfront_aio_poll(aiocbp->aio_dev);
 | ||
| 		if (aiocbp->data)
 | ||
| 			break;
 | ||
| 		cpu_relax();
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| static void blkfront_push_operation(struct blkfront_dev *dev, u8 op,
 | ||
| 				    uint64_t id)
 | ||
| {
 | ||
| 	struct blkif_request *req;
 | ||
| 	int notify, i;
 | ||
| 
 | ||
| 	blkfront_wait_slot(dev);
 | ||
| 	i = dev->ring.req_prod_pvt;
 | ||
| 	req = RING_GET_REQUEST(&dev->ring, i);
 | ||
| 	req->operation = op;
 | ||
| 	req->nr_segments = 0;
 | ||
| 	req->handle = dev->handle;
 | ||
| 	req->id = id;
 | ||
| 	req->sector_number = 0;
 | ||
| 	dev->ring.req_prod_pvt = i + 1;
 | ||
| 	wmb();
 | ||
| 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify);
 | ||
| 	if (notify)
 | ||
| 		notify_remote_via_evtchn(dev->evtchn);
 | ||
| }
 | ||
| 
 | ||
| static void blkfront_sync(struct blkfront_dev *dev)
 | ||
| {
 | ||
| 	if (dev->info.mode == O_RDWR) {
 | ||
| 		if (dev->info.barrier == 1)
 | ||
| 			blkfront_push_operation(dev,
 | ||
| 						BLKIF_OP_WRITE_BARRIER, 0);
 | ||
| 
 | ||
| 		if (dev->info.flush == 1)
 | ||
| 			blkfront_push_operation(dev,
 | ||
| 						BLKIF_OP_FLUSH_DISKCACHE, 0);
 | ||
| 	}
 | ||
| 
 | ||
| 	while (true) {
 | ||
| 		blkfront_aio_poll(dev);
 | ||
| 		if (RING_FREE_REQUESTS(&dev->ring) == RING_SIZE(&dev->ring))
 | ||
| 			break;
 | ||
| 		cpu_relax();
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * pvblock_iop() - Issue an aio.
 | ||
|  * @udev: Pvblock device
 | ||
|  * @blknr: Block number to read from / write to
 | ||
|  * @blkcnt: Amount of blocks to read / write
 | ||
|  * @buffer: Memory buffer with data to be read / write
 | ||
|  * @write: Describes is it read or write operation
 | ||
|  *	   0 - read
 | ||
|  *	   1 - write
 | ||
|  *
 | ||
|  * Depending on the operation - reading or writing, data is read / written from the
 | ||
|  * specified address (@buffer) to the sector (@blknr).
 | ||
|  */
 | ||
| static ulong pvblock_iop(struct udevice *udev, lbaint_t blknr,
 | ||
| 			 lbaint_t blkcnt, void *buffer, int write)
 | ||
| {
 | ||
| 	struct blkfront_dev *blk_dev = dev_get_priv(udev);
 | ||
| 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
 | ||
| 	struct blkfront_aiocb aiocb;
 | ||
| 	lbaint_t blocks_todo;
 | ||
| 	bool unaligned;
 | ||
| 
 | ||
| 	if (blkcnt == 0)
 | ||
| 		return 0;
 | ||
| 
 | ||
| 	if ((blknr + blkcnt) > desc->lba) {
 | ||
| 		printf(DRV_NAME ": block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
 | ||
| 		       blknr + blkcnt, desc->lba);
 | ||
| 		return 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	unaligned = (uintptr_t)buffer & (blk_dev->info.sector_size - 1);
 | ||
| 
 | ||
| 	aiocb.aio_dev = blk_dev;
 | ||
| 	aiocb.aio_offset = blknr * desc->blksz;
 | ||
| 	aiocb.aio_cb = NULL;
 | ||
| 	aiocb.data = NULL;
 | ||
| 	blocks_todo = blkcnt;
 | ||
| 	do {
 | ||
| 		aiocb.aio_buf = unaligned ? blk_dev->bounce_buffer : buffer;
 | ||
| 
 | ||
| 		if (write && unaligned)
 | ||
| 			memcpy(blk_dev->bounce_buffer, buffer, desc->blksz);
 | ||
| 
 | ||
| 		aiocb.aio_nbytes = unaligned ? desc->blksz :
 | ||
| 			min((size_t)(BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE),
 | ||
| 			    (size_t)(blocks_todo * desc->blksz));
 | ||
| 
 | ||
| 		blkfront_io(&aiocb, write);
 | ||
| 
 | ||
| 		if (!write && unaligned)
 | ||
| 			memcpy(buffer, blk_dev->bounce_buffer, desc->blksz);
 | ||
| 
 | ||
| 		aiocb.aio_offset += aiocb.aio_nbytes;
 | ||
| 		buffer += aiocb.aio_nbytes;
 | ||
| 		blocks_todo -= aiocb.aio_nbytes / desc->blksz;
 | ||
| 	} while (blocks_todo > 0);
 | ||
| 
 | ||
| 	return blkcnt;
 | ||
| }
 | ||
| 
 | ||
| ulong pvblock_blk_read(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt,
 | ||
| 		       void *buffer)
 | ||
| {
 | ||
| 	return pvblock_iop(udev, blknr, blkcnt, buffer, 0);
 | ||
| }
 | ||
| 
 | ||
| ulong pvblock_blk_write(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt,
 | ||
| 			const void *buffer)
 | ||
| {
 | ||
| 	return pvblock_iop(udev, blknr, blkcnt, (void *)buffer, 1);
 | ||
| }
 | ||
| 
 | ||
| static int pvblock_blk_bind(struct udevice *udev)
 | ||
| {
 | ||
| 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
 | ||
| 	int devnum;
 | ||
| 
 | ||
| 	desc->if_type = IF_TYPE_PVBLOCK;
 | ||
| 	/*
 | ||
| 	 * Initialize the devnum to -ENODEV. This is to make sure that
 | ||
| 	 * blk_next_free_devnum() works as expected, since the default
 | ||
| 	 * value 0 is a valid devnum.
 | ||
| 	 */
 | ||
| 	desc->devnum = -ENODEV;
 | ||
| 	devnum = blk_next_free_devnum(IF_TYPE_PVBLOCK);
 | ||
| 	if (devnum < 0)
 | ||
| 		return devnum;
 | ||
| 	desc->devnum = devnum;
 | ||
| 	desc->part_type = PART_TYPE_UNKNOWN;
 | ||
| 	desc->bdev = udev;
 | ||
| 
 | ||
| 	strncpy(desc->vendor, "Xen", sizeof(desc->vendor));
 | ||
| 	strncpy(desc->revision, "1", sizeof(desc->revision));
 | ||
| 	strncpy(desc->product, "Virtual disk", sizeof(desc->product));
 | ||
| 
 | ||
| 	return 0;
 | ||
| }
 | ||
| 
 | ||
| static int pvblock_blk_probe(struct udevice *udev)
 | ||
| {
 | ||
| 	struct blkfront_dev *blk_dev = dev_get_priv(udev);
 | ||
| 	struct blkfront_platdata *platdata = dev_get_platdata(udev);
 | ||
| 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
 | ||
| 	int ret, devid;
 | ||
| 
 | ||
| 	devid = platdata->devid;
 | ||
| 	free(platdata);
 | ||
| 
 | ||
| 	ret = init_blkfront(devid, blk_dev);
 | ||
| 	if (ret < 0)
 | ||
| 		return ret;
 | ||
| 
 | ||
| 	desc->blksz = blk_dev->info.sector_size;
 | ||
| 	desc->lba = blk_dev->info.sectors;
 | ||
| 	desc->log2blksz = LOG2(blk_dev->info.sector_size);
 | ||
| 
 | ||
| 	return 0;
 | ||
| }
 | ||
| 
 | ||
| static int pvblock_blk_remove(struct udevice *udev)
 | ||
| {
 | ||
| 	struct blkfront_dev *blk_dev = dev_get_priv(udev);
 | ||
| 
 | ||
| 	shutdown_blkfront(blk_dev);
 | ||
| 	return 0;
 | ||
| }
 | ||
| 
 | ||
| static const struct blk_ops pvblock_blk_ops = {
 | ||
| 	.read	= pvblock_blk_read,
 | ||
| 	.write	= pvblock_blk_write,
 | ||
| };
 | ||
| 
 | ||
| U_BOOT_DRIVER(pvblock_blk) = {
 | ||
| 	.name			= DRV_NAME_BLK,
 | ||
| 	.id			= UCLASS_BLK,
 | ||
| 	.ops			= &pvblock_blk_ops,
 | ||
| 	.bind			= pvblock_blk_bind,
 | ||
| 	.probe			= pvblock_blk_probe,
 | ||
| 	.remove			= pvblock_blk_remove,
 | ||
| 	.priv_auto_alloc_size	= sizeof(struct blkfront_dev),
 | ||
| 	.flags			= DM_FLAG_OS_PREPARE,
 | ||
| };
 | ||
| 
 | ||
| /*******************************************************************************
 | ||
|  * Para-virtual block device class
 | ||
|  *******************************************************************************/
 | ||
| 
 | ||
| typedef int (*enum_vbd_callback)(struct udevice *parent, unsigned int devid);
 | ||
| 
 | ||
| static int on_new_vbd(struct udevice *parent, unsigned int devid)
 | ||
| {
 | ||
| 	struct driver_info info;
 | ||
| 	struct udevice *udev;
 | ||
| 	struct blkfront_platdata *platdata;
 | ||
| 	int ret;
 | ||
| 
 | ||
| 	debug("New " DRV_NAME_BLK ", device ID %d\n", devid);
 | ||
| 
 | ||
| 	platdata = malloc(sizeof(struct blkfront_platdata));
 | ||
| 	if (!platdata) {
 | ||
| 		printf("Failed to allocate platform data\n");
 | ||
| 		return -ENOMEM;
 | ||
| 	}
 | ||
| 
 | ||
| 	platdata->devid = devid;
 | ||
| 
 | ||
| 	info.name = DRV_NAME_BLK;
 | ||
| 	info.platdata = platdata;
 | ||
| 
 | ||
| 	ret = device_bind_by_name(parent, false, &info, &udev);
 | ||
| 	if (ret < 0) {
 | ||
| 		printf("Failed to bind " DRV_NAME_BLK " to device with ID %d, ret: %d\n",
 | ||
| 		       devid, ret);
 | ||
| 		free(platdata);
 | ||
| 	}
 | ||
| 	return ret;
 | ||
| }
 | ||
| 
 | ||
| static int xenbus_enumerate_vbd(struct udevice *udev, enum_vbd_callback clb)
 | ||
| {
 | ||
| 	char **dirs, *msg;
 | ||
| 	int i, ret;
 | ||
| 
 | ||
| 	msg = xenbus_ls(XBT_NIL, "device/vbd", &dirs);
 | ||
| 	if (msg) {
 | ||
| 		printf("Failed to read device/vbd directory: %s\n", msg);
 | ||
| 		free(msg);
 | ||
| 		return -ENODEV;
 | ||
| 	}
 | ||
| 
 | ||
| 	for (i = 0; dirs[i]; i++) {
 | ||
| 		int devid;
 | ||
| 
 | ||
| 		sscanf(dirs[i], "%d", &devid);
 | ||
| 		ret = clb(udev, devid);
 | ||
| 		if (ret < 0)
 | ||
| 			goto fail;
 | ||
| 
 | ||
| 		free(dirs[i]);
 | ||
| 	}
 | ||
| 	ret = 0;
 | ||
| 
 | ||
| fail:
 | ||
| 	for (; dirs[i]; i++)
 | ||
| 		free(dirs[i]);
 | ||
| 	free(dirs);
 | ||
| 	return ret;
 | ||
| }
 | ||
| 
 | ||
| static void print_pvblock_devices(void)
 | ||
| {
 | ||
| 	struct udevice *udev;
 | ||
| 	bool first = true;
 | ||
| 	const char *class_name;
 | ||
| 
 | ||
| 	class_name = uclass_get_name(UCLASS_PVBLOCK);
 | ||
| 	for (blk_first_device(IF_TYPE_PVBLOCK, &udev); udev;
 | ||
| 	     blk_next_device(&udev), first = false) {
 | ||
| 		struct blk_desc *desc = dev_get_uclass_platdata(udev);
 | ||
| 
 | ||
| 		if (!first)
 | ||
| 			puts(", ");
 | ||
| 		printf("%s: %d", class_name, desc->devnum);
 | ||
| 	}
 | ||
| 	printf("\n");
 | ||
| }
 | ||
| 
 | ||
| void pvblock_init(void)
 | ||
| {
 | ||
| 	struct driver_info info;
 | ||
| 	struct udevice *udev;
 | ||
| 	struct uclass *uc;
 | ||
| 	int ret;
 | ||
| 
 | ||
| 	/*
 | ||
| 	 * At this point Xen drivers have already initialized,
 | ||
| 	 * so we can instantiate the class driver and enumerate
 | ||
| 	 * virtual block devices.
 | ||
| 	 */
 | ||
| 	info.name = DRV_NAME;
 | ||
| 	ret = device_bind_by_name(gd->dm_root, false, &info, &udev);
 | ||
| 	if (ret < 0)
 | ||
| 		printf("Failed to bind " DRV_NAME ", ret: %d\n", ret);
 | ||
| 
 | ||
| 	/* Bootstrap virtual block devices class driver */
 | ||
| 	ret = uclass_get(UCLASS_PVBLOCK, &uc);
 | ||
| 	if (ret)
 | ||
| 		return;
 | ||
| 	uclass_foreach_dev_probe(UCLASS_PVBLOCK, udev);
 | ||
| 
 | ||
| 	print_pvblock_devices();
 | ||
| }
 | ||
| 
 | ||
| static int pvblock_probe(struct udevice *udev)
 | ||
| {
 | ||
| 	struct uclass *uc;
 | ||
| 	int ret;
 | ||
| 
 | ||
| 	if (xenbus_enumerate_vbd(udev, on_new_vbd) < 0)
 | ||
| 		return -ENODEV;
 | ||
| 
 | ||
| 	ret = uclass_get(UCLASS_BLK, &uc);
 | ||
| 	if (ret)
 | ||
| 		return ret;
 | ||
| 	uclass_foreach_dev_probe(UCLASS_BLK, udev) {
 | ||
| 		if (_ret)
 | ||
| 			return _ret;
 | ||
| 	};
 | ||
| 	return 0;
 | ||
| }
 | ||
| 
 | ||
| U_BOOT_DRIVER(pvblock_drv) = {
 | ||
| 	.name		= DRV_NAME,
 | ||
| 	.id		= UCLASS_PVBLOCK,
 | ||
| 	.probe		= pvblock_probe,
 | ||
| };
 | ||
| 
 | ||
| UCLASS_DRIVER(pvblock) = {
 | ||
| 	.name		= DRV_NAME,
 | ||
| 	.id		= UCLASS_PVBLOCK,
 | ||
| };
 |