void *rq;
QEMUBH *bh;
BlockConf *conf;
+ char *serial;
unsigned short sector_mask;
- char sn[BLOCK_SERIAL_STRLEN];
DeviceState *qdev;
} VirtIOBlock;
trace_virtio_blk_req_complete(req, status);
- req->in->status = status;
+ stb_p(&req->in->status, status);
virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
virtio_notify(&s->vdev, s->vq);
req->next = s->rq;
s->rq = req;
bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
- vm_stop(0);
+ vm_stop(VMSTOP_DISKFULL);
} else {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
trace_virtio_blk_rw_complete(req, ret);
if (ret) {
- int is_read = !(req->out->type & VIRTIO_BLK_T_OUT);
+ int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT);
if (virtio_blk_handle_rw_error(req, -ret, is_read))
return;
}
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;
+ stl_p(&req->scsi->errors, hdr.status);
+ stl_p(&req->scsi->residual, hdr.resid);
+ stl_p(&req->scsi->sense_len, hdr.sb_len_wr);
+ stl_p(&req->scsi->data_len, hdr.dxfer_len);
virtio_blk_req_complete(req, status);
}
static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
BlockRequest *blkreq;
+ uint64_t sector;
- trace_virtio_blk_handle_write(req, req->out->sector, req->qiov.size / 512);
+ sector = ldq_p(&req->out->sector);
- if (req->out->sector & req->dev->sector_mask) {
+ trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512);
+
+ if (sector & req->dev->sector_mask) {
+ virtio_blk_rw_complete(req, -EIO);
+ return;
+ }
+ if (req->qiov.size % req->dev->conf->logical_block_size) {
virtio_blk_rw_complete(req, -EIO);
return;
}
}
blkreq = &mrb->blkreq[mrb->num_writes];
- blkreq->sector = req->out->sector;
+ blkreq->sector = sector;
blkreq->nb_sectors = req->qiov.size / BDRV_SECTOR_SIZE;
blkreq->qiov = &req->qiov;
blkreq->cb = virtio_blk_rw_complete;
static void virtio_blk_handle_read(VirtIOBlockReq *req)
{
BlockDriverAIOCB *acb;
+ uint64_t sector;
+
+ sector = ldq_p(&req->out->sector);
- if (req->out->sector & req->dev->sector_mask) {
+ if (sector & req->dev->sector_mask) {
+ virtio_blk_rw_complete(req, -EIO);
+ return;
+ }
+ if (req->qiov.size % req->dev->conf->logical_block_size) {
virtio_blk_rw_complete(req, -EIO);
return;
}
- acb = bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov,
+ acb = bdrv_aio_readv(req->dev->bs, sector, &req->qiov,
req->qiov.size / BDRV_SECTOR_SIZE,
virtio_blk_rw_complete, req);
if (!acb) {
static void virtio_blk_handle_request(VirtIOBlockReq *req,
MultiReqBuffer *mrb)
{
+ uint32_t type;
+
if (req->elem.out_num < 1 || req->elem.in_num < 1) {
- fprintf(stderr, "virtio-blk missing headers\n");
+ error_report("virtio-blk missing headers");
exit(1);
}
if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
- fprintf(stderr, "virtio-blk header not in correct element\n");
+ error_report("virtio-blk header not in correct element");
exit(1);
}
req->out = (void *)req->elem.out_sg[0].iov_base;
req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
- if (req->out->type & VIRTIO_BLK_T_FLUSH) {
+ type = ldl_p(&req->out->type);
+
+ if (type & VIRTIO_BLK_T_FLUSH) {
virtio_blk_handle_flush(req, mrb);
- } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
+ } else if (type & VIRTIO_BLK_T_SCSI_CMD) {
virtio_blk_handle_scsi(req);
- } else if (req->out->type & VIRTIO_BLK_T_GET_ID) {
+ } else if (type & VIRTIO_BLK_T_GET_ID) {
VirtIOBlock *s = req->dev;
- memcpy(req->elem.in_sg[0].iov_base, s->sn,
- MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn)));
+ /*
+ * NB: per existing s/n string convention the string is
+ * terminated by '\0' only when shorter than buffer.
+ */
+ strncpy(req->elem.in_sg[0].iov_base,
+ s->serial ? s->serial : "",
+ MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
- } else if (req->out->type & VIRTIO_BLK_T_OUT) {
+ } else if (type & VIRTIO_BLK_T_OUT) {
qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
req->elem.out_num - 1);
virtio_blk_handle_write(req, mrb);
return 0;
}
-VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf)
+static void virtio_blk_change_cb(void *opaque, int reason)
+{
+ VirtIOBlock *s = opaque;
+
+ if (reason & CHANGE_SIZE) {
+ virtio_notify_config(&s->vdev);
+ }
+}
+
+VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf,
+ char **serial)
{
VirtIOBlock *s;
int cylinders, heads, secs;
return NULL;
}
+ if (!*serial) {
+ /* try to fall back to value set with legacy -drive serial=... */
+ dinfo = drive_get_by_blockdev(conf->bs);
+ if (*dinfo->serial) {
+ *serial = strdup(dinfo->serial);
+ }
+ }
+
s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK,
sizeof(struct virtio_blk_config),
sizeof(VirtIOBlock));
s->vdev.reset = virtio_blk_reset;
s->bs = conf->bs;
s->conf = conf;
+ s->serial = *serial;
s->rq = NULL;
s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1;
bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
- /* NB: per existing s/n string convention the string is terminated
- * by '\0' only when less than sizeof (s->sn)
- */
- dinfo = drive_get_by_blockdev(s->bs);
- strncpy(s->sn, dinfo->serial, sizeof (s->sn));
-
s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
virtio_blk_save, virtio_blk_load, s);
bdrv_set_removable(s->bs, 0);
+ bdrv_set_change_cb(s->bs, virtio_blk_change_cb, s);
s->bs->buffer_alignment = conf->logical_block_size;
+ add_boot_device_path(conf->bootindex, dev, "/disk@0,0");
+
return &s->vdev;
}