qemu/hw/scsi/vhost-scsi.c
<<
>>
Prefs
   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/queue.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 "qemu/cutils.h"
  32
  33/* Features supported by host kernel. */
  34static const int kernel_feature_bits[] = {
  35    VIRTIO_F_NOTIFY_ON_EMPTY,
  36    VIRTIO_RING_F_INDIRECT_DESC,
  37    VIRTIO_RING_F_EVENT_IDX,
  38    VIRTIO_SCSI_F_HOTPLUG,
  39    VHOST_INVALID_FEATURE_BIT
  40};
  41
  42static int vhost_scsi_set_endpoint(VHostSCSI *s)
  43{
  44    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
  45    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
  46    const VhostOps *vhost_ops = vsc->dev.vhost_ops;
  47    struct vhost_scsi_target backend;
  48    int ret;
  49
  50    memset(&backend, 0, sizeof(backend));
  51    pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
  52    ret = vhost_ops->vhost_scsi_set_endpoint(&vsc->dev, &backend);
  53    if (ret < 0) {
  54        return -errno;
  55    }
  56    return 0;
  57}
  58
  59static void vhost_scsi_clear_endpoint(VHostSCSI *s)
  60{
  61    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
  62    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
  63    struct vhost_scsi_target backend;
  64    const VhostOps *vhost_ops = vsc->dev.vhost_ops;
  65
  66    memset(&backend, 0, sizeof(backend));
  67    pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
  68    vhost_ops->vhost_scsi_clear_endpoint(&vsc->dev, &backend);
  69}
  70
  71static int vhost_scsi_start(VHostSCSI *s)
  72{
  73    int ret, abi_version;
  74    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
  75    const VhostOps *vhost_ops = vsc->dev.vhost_ops;
  76
  77    ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version);
  78    if (ret < 0) {
  79        return -errno;
  80    }
  81    if (abi_version > VHOST_SCSI_ABI_VERSION) {
  82        error_report("vhost-scsi: The running tcm_vhost kernel abi_version:"
  83                     " %d is greater than vhost_scsi userspace supports: %d,"
  84                     " please upgrade your version of QEMU", abi_version,
  85                     VHOST_SCSI_ABI_VERSION);
  86        return -ENOSYS;
  87    }
  88
  89    ret = vhost_scsi_common_start(vsc);
  90    if (ret < 0) {
  91        return ret;
  92    }
  93
  94    ret = vhost_scsi_set_endpoint(s);
  95    if (ret < 0) {
  96        error_report("Error setting vhost-scsi endpoint");
  97        vhost_scsi_common_stop(vsc);
  98    }
  99
 100    return ret;
 101}
 102
 103static void vhost_scsi_stop(VHostSCSI *s)
 104{
 105    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
 106
 107    vhost_scsi_clear_endpoint(s);
 108    vhost_scsi_common_stop(vsc);
 109}
 110
 111static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
 112{
 113    VHostSCSI *s = VHOST_SCSI(vdev);
 114    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
 115    bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK);
 116
 117    if (vsc->dev.started == start) {
 118        return;
 119    }
 120
 121    if (start) {
 122        int ret;
 123
 124        ret = vhost_scsi_start(s);
 125        if (ret < 0) {
 126            error_report("unable to start vhost-scsi: %s", strerror(-ret));
 127            exit(1);
 128        }
 129    } else {
 130        vhost_scsi_stop(s);
 131    }
 132}
 133
 134static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 135{
 136}
 137
 138static void vhost_scsi_realize(DeviceState *dev, Error **errp)
 139{
 140    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
 141    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
 142    Error *err = NULL;
 143    int vhostfd = -1;
 144    int ret;
 145
 146    if (!vs->conf.wwpn) {
 147        error_setg(errp, "vhost-scsi: missing wwpn");
 148        return;
 149    }
 150
 151    if (vs->conf.vhostfd) {
 152        vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp);
 153        if (vhostfd == -1) {
 154            error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
 155            return;
 156        }
 157    } else {
 158        vhostfd = open("/dev/vhost-scsi", O_RDWR);
 159        if (vhostfd < 0) {
 160            error_setg(errp, "vhost-scsi: open vhost char device failed: %s",
 161                       strerror(errno));
 162            return;
 163        }
 164    }
 165
 166    virtio_scsi_common_realize(dev,
 167                               vhost_dummy_handle_output,
 168                               vhost_dummy_handle_output,
 169                               vhost_dummy_handle_output,
 170                               &err);
 171    if (err != NULL) {
 172        error_propagate(errp, err);
 173        goto close_fd;
 174    }
 175
 176    error_setg(&vsc->migration_blocker,
 177               "vhost-scsi does not support migration");
 178    migrate_add_blocker(vsc->migration_blocker, &err);
 179    if (err) {
 180        error_propagate(errp, err);
 181        error_free(vsc->migration_blocker);
 182        goto close_fd;
 183    }
 184
 185    vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
 186    vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs);
 187    vsc->dev.vq_index = 0;
 188    vsc->dev.backend_features = 0;
 189
 190    ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd,
 191                         VHOST_BACKEND_TYPE_KERNEL, 0);
 192    if (ret < 0) {
 193        error_setg(errp, "vhost-scsi: vhost initialization failed: %s",
 194                   strerror(-ret));
 195        goto free_vqs;
 196    }
 197
 198    /* At present, channel and lun both are 0 for bootable vhost-scsi disk */
 199    vsc->channel = 0;
 200    vsc->lun = 0;
 201    /* Note: we can also get the minimum tpgt from kernel */
 202    vsc->target = vs->conf.boot_tpgt;
 203
 204    return;
 205
 206 free_vqs:
 207    migrate_del_blocker(vsc->migration_blocker);
 208    g_free(vsc->dev.vqs);
 209 close_fd:
 210    close(vhostfd);
 211    return;
 212}
 213
 214static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
 215{
 216    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 217    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
 218
 219    migrate_del_blocker(vsc->migration_blocker);
 220    error_free(vsc->migration_blocker);
 221
 222    /* This will stop vhost backend. */
 223    vhost_scsi_set_status(vdev, 0);
 224
 225    vhost_dev_cleanup(&vsc->dev);
 226    g_free(vsc->dev.vqs);
 227
 228    virtio_scsi_common_unrealize(dev, errp);
 229}
 230
 231static Property vhost_scsi_properties[] = {
 232    DEFINE_PROP_STRING("vhostfd", VirtIOSCSICommon, conf.vhostfd),
 233    DEFINE_PROP_STRING("wwpn", VirtIOSCSICommon, conf.wwpn),
 234    DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
 235    DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1),
 236    DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSICommon, conf.virtqueue_size,
 237                       128),
 238    DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
 239                       0xFFFF),
 240    DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
 241    DEFINE_PROP_END_OF_LIST(),
 242};
 243
 244static void vhost_scsi_class_init(ObjectClass *klass, void *data)
 245{
 246    DeviceClass *dc = DEVICE_CLASS(klass);
 247    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 248    FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
 249
 250    dc->props = vhost_scsi_properties;
 251    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 252    vdc->realize = vhost_scsi_realize;
 253    vdc->unrealize = vhost_scsi_unrealize;
 254    vdc->get_features = vhost_scsi_common_get_features;
 255    vdc->set_config = vhost_scsi_common_set_config;
 256    vdc->set_status = vhost_scsi_set_status;
 257    fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
 258}
 259
 260static void vhost_scsi_instance_init(Object *obj)
 261{
 262    VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
 263
 264    vsc->feature_bits = kernel_feature_bits;
 265
 266    device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
 267                                  DEVICE(vsc), NULL);
 268}
 269
 270static const TypeInfo vhost_scsi_info = {
 271    .name = TYPE_VHOST_SCSI,
 272    .parent = TYPE_VHOST_SCSI_COMMON,
 273    .instance_size = sizeof(VHostSCSI),
 274    .class_init = vhost_scsi_class_init,
 275    .instance_init = vhost_scsi_instance_init,
 276    .interfaces = (InterfaceInfo[]) {
 277        { TYPE_FW_PATH_PROVIDER },
 278        { }
 279    },
 280};
 281
 282static void virtio_register_types(void)
 283{
 284    type_register_static(&vhost_scsi_info);
 285}
 286
 287type_init(virtio_register_types)
 288