virtio-blk: add SGI_IO passthru support
[had the qemu list address wrong the first time, reply to this message, not the previous if you were on Cc] Add support for SG_IO passthru (packet commands) to the virtio-blk backend. Conceptually based on an older patch from Hannes Reinecke but largely rewritten to match the code structure and layering in virtio-blk. Note that currently we issue the hose SG_IO synchronously. We could easily switch to async I/O, but that would required either bloating the VirtIOBlockReq by the size of struct sg_io_hdr or an additional memory allocation for each SG_IO request. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
		
							parent
							
								
									451c4abd8c
								
							
						
					
					
						commit
						1063b8b15f
					
				
							
								
								
									
										123
									
								
								hw/virtio-blk.c
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								hw/virtio-blk.c
									
									
									
									
									
								
							| @ -15,6 +15,9 @@ | ||||
| #include <sysemu.h> | ||||
| #include "virtio-blk.h" | ||||
| #include "block_int.h" | ||||
| #ifdef __linux__ | ||||
| # include <scsi/sg.h> | ||||
| #endif | ||||
| 
 | ||||
| typedef struct VirtIOBlock | ||||
| { | ||||
| @ -35,6 +38,7 @@ typedef struct VirtIOBlockReq | ||||
|     VirtQueueElement elem; | ||||
|     struct virtio_blk_inhdr *in; | ||||
|     struct virtio_blk_outhdr *out; | ||||
|     struct virtio_scsi_inhdr *scsi; | ||||
|     QEMUIOVector qiov; | ||||
|     struct VirtIOBlockReq *next; | ||||
| } VirtIOBlockReq; | ||||
| @ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) | ||||
|     return req; | ||||
| } | ||||
| 
 | ||||
| #ifdef __linux__ | ||||
| static void virtio_blk_handle_scsi(VirtIOBlockReq *req) | ||||
| { | ||||
|     struct sg_io_hdr hdr; | ||||
|     int ret, size = 0; | ||||
|     int status; | ||||
|     int i; | ||||
| 
 | ||||
|     /*
 | ||||
|      * We require at least one output segment each for the virtio_blk_outhdr | ||||
|      * and the SCSI command block. | ||||
|      * | ||||
|      * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr | ||||
|      * and the sense buffer pointer in the input segments. | ||||
|      */ | ||||
|     if (req->elem.out_num < 2 || req->elem.in_num < 3) { | ||||
|         virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * No support for bidirection commands yet. | ||||
|      */ | ||||
|     if (req->elem.out_num > 2 && req->elem.in_num > 3) { | ||||
|         virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * The scsi inhdr is placed in the second-to-last input segment, just | ||||
|      * before the regular inhdr. | ||||
|      */ | ||||
|     req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; | ||||
|     size = sizeof(*req->in) + sizeof(*req->scsi); | ||||
| 
 | ||||
|     memset(&hdr, 0, sizeof(struct sg_io_hdr)); | ||||
|     hdr.interface_id = 'S'; | ||||
|     hdr.cmd_len = req->elem.out_sg[1].iov_len; | ||||
|     hdr.cmdp = req->elem.out_sg[1].iov_base; | ||||
|     hdr.dxfer_len = 0; | ||||
| 
 | ||||
|     if (req->elem.out_num > 2) { | ||||
|         /*
 | ||||
|          * If there are more than the minimally required 2 output segments | ||||
|          * there is write payload starting from the third iovec. | ||||
|          */ | ||||
|         hdr.dxfer_direction = SG_DXFER_TO_DEV; | ||||
|         hdr.iovec_count = req->elem.out_num - 2; | ||||
| 
 | ||||
|         for (i = 0; i < hdr.iovec_count; i++) | ||||
|             hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; | ||||
| 
 | ||||
|         hdr.dxferp = req->elem.out_sg + 2; | ||||
| 
 | ||||
|     } else if (req->elem.in_num > 3) { | ||||
|         /*
 | ||||
|          * If we have more than 3 input segments the guest wants to actually | ||||
|          * read data. | ||||
|          */ | ||||
|         hdr.dxfer_direction = SG_DXFER_FROM_DEV; | ||||
|         hdr.iovec_count = req->elem.in_num - 3; | ||||
|         for (i = 0; i < hdr.iovec_count; i++) | ||||
|             hdr.dxfer_len += req->elem.in_sg[i].iov_len; | ||||
| 
 | ||||
|         hdr.dxferp = req->elem.in_sg; | ||||
|         size += hdr.dxfer_len; | ||||
|     } else { | ||||
|         /*
 | ||||
|          * Some SCSI commands don't actually transfer any data. | ||||
|          */ | ||||
|         hdr.dxfer_direction = SG_DXFER_NONE; | ||||
|     } | ||||
| 
 | ||||
|     hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; | ||||
|     hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; | ||||
|     size += hdr.mx_sb_len; | ||||
| 
 | ||||
|     ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); | ||||
|     if (ret) { | ||||
|         status = VIRTIO_BLK_S_UNSUPP; | ||||
|         hdr.status = ret; | ||||
|         hdr.resid = hdr.dxfer_len; | ||||
|     } else if (hdr.status) { | ||||
|         status = VIRTIO_BLK_S_IOERR; | ||||
|     } else { | ||||
|         status = VIRTIO_BLK_S_OK; | ||||
|     } | ||||
| 
 | ||||
|     req->scsi->errors = hdr.status; | ||||
|     req->scsi->residual = hdr.resid; | ||||
|     req->scsi->sense_len = hdr.sb_len_wr; | ||||
|     req->scsi->data_len = hdr.dxfer_len; | ||||
| 
 | ||||
|     virtio_blk_req_complete(req, status); | ||||
| } | ||||
| #else | ||||
| static void virtio_blk_handle_scsi(VirtIOBlockReq *req) | ||||
| { | ||||
|     virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); | ||||
| } | ||||
| #endif /* __linux__ */ | ||||
| 
 | ||||
| static void virtio_blk_handle_write(VirtIOBlockReq *req) | ||||
| { | ||||
|     bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov, | ||||
| @ -136,12 +242,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) | ||||
|         req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; | ||||
| 
 | ||||
|         if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) { | ||||
|             unsigned int len = sizeof(*req->in); | ||||
| 
 | ||||
|             req->in->status = VIRTIO_BLK_S_UNSUPP; | ||||
|             virtqueue_push(vq, &req->elem, len); | ||||
|             virtio_notify(vdev, vq); | ||||
|             qemu_free(req); | ||||
|             virtio_blk_handle_scsi(req); | ||||
|         } else if (req->out->type & VIRTIO_BLK_T_OUT) { | ||||
|             qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], | ||||
|                                      req->elem.out_num - 1); | ||||
| @ -203,7 +304,15 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) | ||||
| 
 | ||||
| static uint32_t virtio_blk_get_features(VirtIODevice *vdev) | ||||
| { | ||||
|     return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY); | ||||
|     uint32_t features = 0; | ||||
| 
 | ||||
|     features |= (1 << VIRTIO_BLK_F_SEG_MAX); | ||||
|     features |= (1 << VIRTIO_BLK_F_GEOMETRY); | ||||
| #ifdef __linux__ | ||||
|     features |= (1 << VIRTIO_BLK_F_SCSI); | ||||
| #endif | ||||
| 
 | ||||
|     return features; | ||||
| } | ||||
| 
 | ||||
| static void virtio_blk_save(QEMUFile *f, void *opaque) | ||||
|  | ||||
| @ -28,6 +28,9 @@ | ||||
| #define VIRTIO_BLK_F_SIZE_MAX   1       /* Indicates maximum segment size */ | ||||
| #define VIRTIO_BLK_F_SEG_MAX    2       /* Indicates maximum # of segments */ | ||||
| #define VIRTIO_BLK_F_GEOMETRY   4       /* Indicates support of legacy geometry */ | ||||
| #define VIRTIO_BLK_F_RO         5       /* Disk is read-only */ | ||||
| #define VIRTIO_BLK_F_BLK_SIZE   6       /* Block size of disk is available*/ | ||||
| #define VIRTIO_BLK_F_SCSI       7       /* Supports scsi command passthru */ | ||||
| 
 | ||||
| struct virtio_blk_config | ||||
| { | ||||
| @ -70,6 +73,15 @@ struct virtio_blk_inhdr | ||||
|     unsigned char status; | ||||
| }; | ||||
| 
 | ||||
| /* SCSI pass-through header */ | ||||
| struct virtio_scsi_inhdr | ||||
| { | ||||
|     uint32_t errors; | ||||
|     uint32_t data_len; | ||||
|     uint32_t sense_len; | ||||
|     uint32_t residual; | ||||
| }; | ||||
| 
 | ||||
| void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Christoph Hellwig
						Christoph Hellwig