qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

vhost-scsi.c (10316B)


      1 /*
      2  * vhost_scsi host device
      3  *
      4  * Copyright IBM, Corp. 2011
      5  *
      6  * Authors:
      7  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
      8  *
      9  * Changes for QEMU mainline + tcm_vhost kernel upstream:
     10  *  Nicholas Bellinger <nab@risingtidesystems.com>
     11  *
     12  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
     13  * See the COPYING.LIB file in the top-level directory.
     14  *
     15  */
     16 
     17 #include "qemu/osdep.h"
     18 #include <linux/vhost.h>
     19 #include <sys/ioctl.h>
     20 #include "qapi/error.h"
     21 #include "qemu/error-report.h"
     22 #include "qemu/module.h"
     23 #include "monitor/monitor.h"
     24 #include "migration/blocker.h"
     25 #include "hw/virtio/vhost-scsi.h"
     26 #include "hw/virtio/vhost.h"
     27 #include "hw/virtio/virtio-scsi.h"
     28 #include "hw/virtio/virtio-bus.h"
     29 #include "hw/virtio/virtio-access.h"
     30 #include "hw/fw-path-provider.h"
     31 #include "hw/qdev-properties.h"
     32 #include "qemu/cutils.h"
     33 #include "sysemu/sysemu.h"
     34 
     35 /* Features supported by host kernel. */
     36 static const int kernel_feature_bits[] = {
     37     VIRTIO_F_NOTIFY_ON_EMPTY,
     38     VIRTIO_RING_F_INDIRECT_DESC,
     39     VIRTIO_RING_F_EVENT_IDX,
     40     VIRTIO_SCSI_F_HOTPLUG,
     41     VIRTIO_F_RING_RESET,
     42     VHOST_INVALID_FEATURE_BIT
     43 };
     44 
     45 static int vhost_scsi_set_endpoint(VHostSCSI *s)
     46 {
     47     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
     48     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
     49     const VhostOps *vhost_ops = vsc->dev.vhost_ops;
     50     struct vhost_scsi_target backend;
     51     int ret;
     52 
     53     memset(&backend, 0, sizeof(backend));
     54     pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
     55     ret = vhost_ops->vhost_scsi_set_endpoint(&vsc->dev, &backend);
     56     if (ret < 0) {
     57         return -errno;
     58     }
     59     return 0;
     60 }
     61 
     62 static void vhost_scsi_clear_endpoint(VHostSCSI *s)
     63 {
     64     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
     65     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
     66     struct vhost_scsi_target backend;
     67     const VhostOps *vhost_ops = vsc->dev.vhost_ops;
     68 
     69     memset(&backend, 0, sizeof(backend));
     70     pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
     71     vhost_ops->vhost_scsi_clear_endpoint(&vsc->dev, &backend);
     72 }
     73 
     74 static int vhost_scsi_start(VHostSCSI *s)
     75 {
     76     int ret, abi_version;
     77     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
     78     const VhostOps *vhost_ops = vsc->dev.vhost_ops;
     79 
     80     ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version);
     81     if (ret < 0) {
     82         return -errno;
     83     }
     84     if (abi_version > VHOST_SCSI_ABI_VERSION) {
     85         error_report("vhost-scsi: The running tcm_vhost kernel abi_version:"
     86                      " %d is greater than vhost_scsi userspace supports: %d,"
     87                      " please upgrade your version of QEMU", abi_version,
     88                      VHOST_SCSI_ABI_VERSION);
     89         return -ENOSYS;
     90     }
     91 
     92     ret = vhost_scsi_common_start(vsc);
     93     if (ret < 0) {
     94         return ret;
     95     }
     96 
     97     ret = vhost_scsi_set_endpoint(s);
     98     if (ret < 0) {
     99         error_report("Error setting vhost-scsi endpoint");
    100         vhost_scsi_common_stop(vsc);
    101     }
    102 
    103     return ret;
    104 }
    105 
    106 static void vhost_scsi_stop(VHostSCSI *s)
    107 {
    108     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
    109 
    110     vhost_scsi_clear_endpoint(s);
    111     vhost_scsi_common_stop(vsc);
    112 }
    113 
    114 static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
    115 {
    116     VHostSCSI *s = VHOST_SCSI(vdev);
    117     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
    118     bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK);
    119 
    120     if (!vdev->vm_running) {
    121         start = false;
    122     }
    123 
    124     if (vhost_dev_is_started(&vsc->dev) == start) {
    125         return;
    126     }
    127 
    128     if (start) {
    129         int ret;
    130 
    131         ret = vhost_scsi_start(s);
    132         if (ret < 0) {
    133             error_report("unable to start vhost-scsi: %s", strerror(-ret));
    134             exit(1);
    135         }
    136     } else {
    137         vhost_scsi_stop(s);
    138     }
    139 }
    140 
    141 static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
    142 {
    143 }
    144 
    145 static int vhost_scsi_pre_save(void *opaque)
    146 {
    147     VHostSCSICommon *vsc = opaque;
    148 
    149     /* At this point, backend must be stopped, otherwise
    150      * it might keep writing to memory. */
    151     assert(!vhost_dev_is_started(&vsc->dev));
    152 
    153     return 0;
    154 }
    155 
    156 static const VMStateDescription vmstate_virtio_vhost_scsi = {
    157     .name = "virtio-vhost_scsi",
    158     .minimum_version_id = 1,
    159     .version_id = 1,
    160     .fields = (VMStateField[]) {
    161         VMSTATE_VIRTIO_DEVICE,
    162         VMSTATE_END_OF_LIST()
    163     },
    164     .pre_save = vhost_scsi_pre_save,
    165 };
    166 
    167 static void vhost_scsi_realize(DeviceState *dev, Error **errp)
    168 {
    169     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
    170     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
    171     Error *err = NULL;
    172     int vhostfd = -1;
    173     int ret;
    174     struct vhost_virtqueue *vqs = NULL;
    175 
    176     if (!vs->conf.wwpn) {
    177         error_setg(errp, "vhost-scsi: missing wwpn");
    178         return;
    179     }
    180 
    181     if (vs->conf.vhostfd) {
    182         vhostfd = monitor_fd_param(monitor_cur(), vs->conf.vhostfd, errp);
    183         if (vhostfd == -1) {
    184             error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
    185             return;
    186         }
    187     } else {
    188         vhostfd = open("/dev/vhost-scsi", O_RDWR);
    189         if (vhostfd < 0) {
    190             error_setg(errp, "vhost-scsi: open vhost char device failed: %s",
    191                        strerror(errno));
    192             return;
    193         }
    194     }
    195 
    196     virtio_scsi_common_realize(dev,
    197                                vhost_dummy_handle_output,
    198                                vhost_dummy_handle_output,
    199                                vhost_dummy_handle_output,
    200                                &err);
    201     if (err != NULL) {
    202         error_propagate(errp, err);
    203         goto close_fd;
    204     }
    205 
    206     if (!vsc->migratable) {
    207         error_setg(&vsc->migration_blocker,
    208                 "vhost-scsi does not support migration in all cases. "
    209                 "When external environment supports it (Orchestrator migrates "
    210                 "target SCSI device state or use shared storage over network), "
    211                 "set 'migratable' property to true to enable migration.");
    212         if (migrate_add_blocker(vsc->migration_blocker, errp) < 0) {
    213             goto free_virtio;
    214         }
    215     }
    216 
    217     vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
    218     vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs);
    219     vsc->dev.vqs = vqs;
    220     vsc->dev.vq_index = 0;
    221     vsc->dev.backend_features = 0;
    222 
    223     ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd,
    224                          VHOST_BACKEND_TYPE_KERNEL, 0, errp);
    225     if (ret < 0) {
    226         /*
    227          * vhost_dev_init calls vhost_dev_cleanup on error, which closes
    228          * vhostfd, don't double close it.
    229          */
    230         vhostfd = -1;
    231         goto free_vqs;
    232     }
    233 
    234     /* At present, channel and lun both are 0 for bootable vhost-scsi disk */
    235     vsc->channel = 0;
    236     vsc->lun = 0;
    237     /* Note: we can also get the minimum tpgt from kernel */
    238     vsc->target = vs->conf.boot_tpgt;
    239 
    240     return;
    241 
    242  free_vqs:
    243     g_free(vqs);
    244     if (!vsc->migratable) {
    245         migrate_del_blocker(vsc->migration_blocker);
    246     }
    247  free_virtio:
    248     error_free(vsc->migration_blocker);
    249     virtio_scsi_common_unrealize(dev);
    250  close_fd:
    251     if (vhostfd >= 0) {
    252         close(vhostfd);
    253     }
    254     return;
    255 }
    256 
    257 static void vhost_scsi_unrealize(DeviceState *dev)
    258 {
    259     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    260     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
    261     struct vhost_virtqueue *vqs = vsc->dev.vqs;
    262 
    263     if (!vsc->migratable) {
    264         migrate_del_blocker(vsc->migration_blocker);
    265         error_free(vsc->migration_blocker);
    266     }
    267 
    268     /* This will stop vhost backend. */
    269     vhost_scsi_set_status(vdev, 0);
    270 
    271     vhost_dev_cleanup(&vsc->dev);
    272     g_free(vqs);
    273 
    274     virtio_scsi_common_unrealize(dev);
    275 }
    276 
    277 static struct vhost_dev *vhost_scsi_get_vhost(VirtIODevice *vdev)
    278 {
    279     VHostSCSI *s = VHOST_SCSI(vdev);
    280     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
    281     return &vsc->dev;
    282 }
    283 
    284 static Property vhost_scsi_properties[] = {
    285     DEFINE_PROP_STRING("vhostfd", VirtIOSCSICommon, conf.vhostfd),
    286     DEFINE_PROP_STRING("wwpn", VirtIOSCSICommon, conf.wwpn),
    287     DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
    288     DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues,
    289                        VIRTIO_SCSI_AUTO_NUM_QUEUES),
    290     DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSICommon, conf.virtqueue_size,
    291                        128),
    292     DEFINE_PROP_BOOL("seg_max_adjust", VirtIOSCSICommon, conf.seg_max_adjust,
    293                       true),
    294     DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
    295                        0xFFFF),
    296     DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
    297     DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features,
    298                                                  VIRTIO_SCSI_F_T10_PI,
    299                                                  false),
    300     DEFINE_PROP_BOOL("migratable", VHostSCSICommon, migratable, false),
    301     DEFINE_PROP_END_OF_LIST(),
    302 };
    303 
    304 static void vhost_scsi_class_init(ObjectClass *klass, void *data)
    305 {
    306     DeviceClass *dc = DEVICE_CLASS(klass);
    307     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
    308     FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
    309 
    310     device_class_set_props(dc, vhost_scsi_properties);
    311     dc->vmsd = &vmstate_virtio_vhost_scsi;
    312     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
    313     vdc->realize = vhost_scsi_realize;
    314     vdc->unrealize = vhost_scsi_unrealize;
    315     vdc->get_features = vhost_scsi_common_get_features;
    316     vdc->set_config = vhost_scsi_common_set_config;
    317     vdc->set_status = vhost_scsi_set_status;
    318     vdc->get_vhost = vhost_scsi_get_vhost;
    319     fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
    320 }
    321 
    322 static void vhost_scsi_instance_init(Object *obj)
    323 {
    324     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
    325 
    326     vsc->feature_bits = kernel_feature_bits;
    327 
    328     device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
    329                                   DEVICE(vsc));
    330 }
    331 
    332 static const TypeInfo vhost_scsi_info = {
    333     .name = TYPE_VHOST_SCSI,
    334     .parent = TYPE_VHOST_SCSI_COMMON,
    335     .instance_size = sizeof(VHostSCSI),
    336     .class_init = vhost_scsi_class_init,
    337     .instance_init = vhost_scsi_instance_init,
    338     .interfaces = (InterfaceInfo[]) {
    339         { TYPE_FW_PATH_PROVIDER },
    340         { }
    341     },
    342 };
    343 
    344 static void virtio_register_types(void)
    345 {
    346     type_register_static(&vhost_scsi_info);
    347 }
    348 
    349 type_init(virtio_register_types)