static void virtio_blk_ioctl_complete(void *opaque, int status) { VirtIOBlockIoctlReq *ioctl_req = opaque; VirtIOBlockReq *req = ioctl_req->req; VirtIOBlock *s = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); struct virtio_scsi_inhdr *scsi; struct sg_io_hdr *hdr; scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; if (status) { status = VIRTIO_BLK_S_UNSUPP; virtio_stl_p(vdev, &scsi->errors, 255); goto out; } hdr = &ioctl_req->hdr; /* * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) * clear the masked_status field [hence status gets cleared too, see * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED * status has occurred. However they do set DRIVER_SENSE in driver_status * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. */ if (hdr->status == 0 && hdr->sb_len_wr > 0) { hdr->status = CHECK_CONDITION; } virtio_stl_p(vdev, &scsi->errors, hdr->status | (hdr->msg_status << 8) | (hdr->host_status << 16) | (hdr->driver_status << 24)); virtio_stl_p(vdev, &scsi->residual, hdr->resid); virtio_stl_p(vdev, &scsi->sense_len, hdr->sb_len_wr); virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len); out: aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); virtio_blk_req_complete(req, status); virtio_blk_free_request(req); aio_context_release(blk_get_aio_context(s->conf.conf.blk)); g_free(ioctl_req); }
/* coalesce internal state, copy to pci i/o region 0 */ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) { VirtIOBlock *s = VIRTIO_BLK(vdev); struct virtio_blk_config blkcfg; uint64_t capacity; int blk_size = s->conf->logical_block_size; bdrv_get_geometry(s->bs, &capacity); memset(&blkcfg, 0, sizeof(blkcfg)); virtio_stq_p(vdev, &blkcfg.capacity, capacity); virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2); virtio_stw_p(vdev, &blkcfg.cylinders, s->conf->cyls); virtio_stl_p(vdev, &blkcfg.blk_size, blk_size); virtio_stw_p(vdev, &blkcfg.min_io_size, s->conf->min_io_size / blk_size); virtio_stw_p(vdev, &blkcfg.opt_io_size, s->conf->opt_io_size / blk_size); blkcfg.heads = s->conf->heads; /* * We must ensure that the block device capacity is a multiple of * the logical block size. If that is not the case, let's use * sector_mask to adopt the geometry to have a correct picture. * For those devices where the capacity is ok for the given geometry * we don't touch the sector value of the geometry, since some devices * (like s390 dasd) need a specific value. Here the capacity is already * cyls*heads*secs*blk_size and the sector value is not block size * divided by 512 - instead it is the amount of blk_size blocks * per track (cylinder). */ if (bdrv_getlength(s->bs) / s->conf->heads / s->conf->secs % blk_size) { blkcfg.sectors = s->conf->secs & ~s->sector_mask; } else { blkcfg.sectors = s->conf->secs; } blkcfg.size_max = 0; blkcfg.physical_block_exp = get_physical_block_exp(s->conf); blkcfg.alignment_offset = 0; blkcfg.wce = bdrv_enable_write_cache(s->bs); memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); }
static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) { int status = VIRTIO_BLK_S_OK; struct virtio_scsi_inhdr *scsi = NULL; VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); VirtQueueElement *elem = &req->elem; VirtIOBlock *blk = req->dev; #ifdef __linux__ int i; VirtIOBlockIoctlReq *ioctl_req; BlockAIOCB *acb; #endif /* * 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 (elem->out_num < 2 || elem->in_num < 3) { status = VIRTIO_BLK_S_IOERR; goto fail; } /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; if (!blk->conf.scsi) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } /* * No support for bidirection commands yet. */ if (elem->out_num > 2 && elem->in_num > 3) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } #ifdef __linux__ ioctl_req = g_new0(VirtIOBlockIoctlReq, 1); ioctl_req->req = req; ioctl_req->hdr.interface_id = 'S'; ioctl_req->hdr.cmd_len = elem->out_sg[1].iov_len; ioctl_req->hdr.cmdp = elem->out_sg[1].iov_base; ioctl_req->hdr.dxfer_len = 0; if (elem->out_num > 2) { /* * If there are more than the minimally required 2 output segments * there is write payload starting from the third iovec. */ ioctl_req->hdr.dxfer_direction = SG_DXFER_TO_DEV; ioctl_req->hdr.iovec_count = elem->out_num - 2; for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { ioctl_req->hdr.dxfer_len += elem->out_sg[i + 2].iov_len; } ioctl_req->hdr.dxferp = elem->out_sg + 2; } else if (elem->in_num > 3) { /* * If we have more than 3 input segments the guest wants to actually * read data. */ ioctl_req->hdr.dxfer_direction = SG_DXFER_FROM_DEV; ioctl_req->hdr.iovec_count = elem->in_num - 3; for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { ioctl_req->hdr.dxfer_len += elem->in_sg[i].iov_len; } ioctl_req->hdr.dxferp = elem->in_sg; } else { /* * Some SCSI commands don't actually transfer any data. */ ioctl_req->hdr.dxfer_direction = SG_DXFER_NONE; } ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, virtio_blk_ioctl_complete, ioctl_req); if (!acb) { g_free(ioctl_req); status = VIRTIO_BLK_S_UNSUPP; goto fail; } return -EINPROGRESS; #else abort(); #endif fail: /* Just put anything nonzero so that the ioctl fails in the guest. */ if (scsi) { virtio_stl_p(vdev, &scsi->errors, 255); } return status; }
int virtio_blk_handle_scsi_req(VirtIOBlock *blk, VirtQueueElement *elem) { int status = VIRTIO_BLK_S_OK; struct virtio_scsi_inhdr *scsi = NULL; VirtIODevice *vdev = VIRTIO_DEVICE(blk); #ifdef __linux__ int i; struct sg_io_hdr hdr; #endif /* * 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 (elem->out_num < 2 || elem->in_num < 3) { status = VIRTIO_BLK_S_IOERR; goto fail; } /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; if (!blk->blk.scsi) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } /* * No support for bidirection commands yet. */ if (elem->out_num > 2 && elem->in_num > 3) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } #ifdef __linux__ memset(&hdr, 0, sizeof(struct sg_io_hdr)); hdr.interface_id = 'S'; hdr.cmd_len = elem->out_sg[1].iov_len; hdr.cmdp = elem->out_sg[1].iov_base; hdr.dxfer_len = 0; if (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 = elem->out_num - 2; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += elem->out_sg[i + 2].iov_len; hdr.dxferp = elem->out_sg + 2; } else if (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 = elem->in_num - 3; for (i = 0; i < hdr.iovec_count; i++) hdr.dxfer_len += elem->in_sg[i].iov_len; hdr.dxferp = elem->in_sg; } else { /* * Some SCSI commands don't actually transfer any data. */ hdr.dxfer_direction = SG_DXFER_NONE; } hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; status = bdrv_ioctl(blk->bs, SG_IO, &hdr); if (status) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } /* * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) * clear the masked_status field [hence status gets cleared too, see * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED * status has occurred. However they do set DRIVER_SENSE in driver_status * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. */ if (hdr.status == 0 && hdr.sb_len_wr > 0) { hdr.status = CHECK_CONDITION; } virtio_stl_p(vdev, &scsi->errors, hdr.status | (hdr.msg_status << 8) | (hdr.host_status << 16) | (hdr.driver_status << 24)); virtio_stl_p(vdev, &scsi->residual, hdr.resid); virtio_stl_p(vdev, &scsi->sense_len, hdr.sb_len_wr); virtio_stl_p(vdev, &scsi->data_len, hdr.dxfer_len); return status; #else abort(); #endif fail: /* Just put anything nonzero so that the ioctl fails in the guest. */ if (scsi) { virtio_stl_p(vdev, &scsi->errors, 255); } return status; }