mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 02:15:45 +01:00 
			
		
		
		
	xen: pvblock: Implement front-back protocol and do IO
Implement Xen para-virtual frontend to backend communication and actually read/write disk data. This is based on mini-os implementation of the para-virtual block frontend driver. Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> Signed-off-by: Anastasiia Lukianenko <anastasiia_lukianenko@epam.com>
This commit is contained in:
		
				
					committed by
					
						 Tom Rini
						Tom Rini
					
				
			
			
				
	
			
			
			
						parent
						
							17c96f8851
						
					
				
				
					commit
					3a739cc6c9
				
			| @@ -107,7 +107,7 @@ void unbind_evtchn(evtchn_port_t port) | ||||
| 	int rc; | ||||
|  | ||||
| 	if (ev_actions[port].handler == default_handler) | ||||
| 		printf("WARN: No handler for port %d when unbinding\n", port); | ||||
| 		debug("Default handler for port %d when unbinding\n", port); | ||||
| 	mask_evtchn(port); | ||||
| 	clear_evtchn(port); | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
| #include <asm/io.h> | ||||
| #include <asm/xen/system.h> | ||||
|  | ||||
| #include <linux/bug.h> | ||||
| #include <linux/compat.h> | ||||
|  | ||||
| #include <xen/events.h> | ||||
| @@ -30,6 +31,7 @@ | ||||
|  | ||||
| #define O_RDONLY	00 | ||||
| #define O_RDWR		02 | ||||
| #define WAIT_RING_TO_MS	10 | ||||
|  | ||||
| struct blkfront_info { | ||||
| 	u64 sectors; | ||||
| @@ -65,12 +67,42 @@ struct blkfront_dev { | ||||
| 	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); | ||||
| @@ -81,16 +113,11 @@ static void free_blkfront(struct blkfront_dev *dev) | ||||
|  | ||||
| 	unbind_evtchn(dev->evtchn); | ||||
|  | ||||
| 	free(dev->bounce_buffer); | ||||
| 	free(dev->nodename); | ||||
| 	free(dev); | ||||
| } | ||||
|  | ||||
| static void blkfront_handler(evtchn_port_t port, struct pt_regs *regs, | ||||
| 			     void *data) | ||||
| { | ||||
| 	printf("%s [Port %d] - event received\n", __func__, port); | ||||
| } | ||||
|  | ||||
| static int init_blkfront(unsigned int devid, struct blkfront_dev *dev) | ||||
| { | ||||
| 	xenbus_transaction_t xbt; | ||||
| @@ -111,7 +138,7 @@ static int init_blkfront(unsigned int devid, struct blkfront_dev *dev) | ||||
|  | ||||
| 	snprintf(path, sizeof(path), "%s/backend-id", nodename); | ||||
| 	dev->dom = xenbus_read_integer(path); | ||||
| 	evtchn_alloc_unbound(dev->dom, blkfront_handler, dev, &dev->evtchn); | ||||
| 	evtchn_alloc_unbound(dev->dom, NULL, dev, &dev->evtchn); | ||||
|  | ||||
| 	s = (struct blkif_sring *)memalign(PAGE_SIZE, PAGE_SIZE); | ||||
| 	if (!s) { | ||||
| @@ -232,8 +259,16 @@ done: | ||||
| 	} | ||||
| 	unmask_evtchn(dev->evtchn); | ||||
|  | ||||
| 	debug("%llu sectors of %u bytes\n", | ||||
| 	      dev->info.sectors, dev->info.sector_size); | ||||
| 	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; | ||||
|  | ||||
| @@ -254,6 +289,8 @@ static void shutdown_blkfront(struct blkfront_dev *dev) | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| @@ -308,16 +345,315 @@ close: | ||||
| 		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 0; | ||||
| 	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 0; | ||||
| 	return pvblock_iop(udev, blknr, blkcnt, (void *)buffer, 1); | ||||
| } | ||||
|  | ||||
| static int pvblock_blk_bind(struct udevice *udev) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user