diff --git a/etc/spdk/vhost.conf.in b/etc/spdk/vhost.conf.in index b4d6b2236..93ccdefba 100644 --- a/etc/spdk/vhost.conf.in +++ b/etc/spdk/vhost.conf.in @@ -135,6 +135,8 @@ #Name vhost.2 # Use first partition from the second Malloc device #Dev Malloc2p0 + # Put controller in read-only mode + #ReadOnly no # Start the poller for this vhost controller on one of the cores in # this cpumask. By default, it not specified, will use any core in the # SPDK process. diff --git a/include/spdk/vhost.h b/include/spdk/vhost.h index e9359c6e4..ab82844a5 100644 --- a/include/spdk/vhost.h +++ b/include/spdk/vhost.h @@ -74,8 +74,10 @@ struct spdk_scsi_dev *spdk_vhost_scsi_dev_get_dev(struct spdk_vhost_dev *ctrl, int spdk_vhost_scsi_dev_add_dev(const char *name, unsigned scsi_dev_num, const char *lun_name); int spdk_vhost_scsi_dev_remove_dev(struct spdk_vhost_dev *vdev, unsigned scsi_dev_num); -int spdk_vhost_blk_construct(const char *name, uint64_t cpumask, const char *dev_name); +int spdk_vhost_blk_construct(const char *name, uint64_t cpumask, const char *dev_name, + bool readonly); int spdk_vhost_blk_destroy(struct spdk_vhost_dev *dev); struct spdk_bdev *spdk_vhost_blk_get_dev(struct spdk_vhost_dev *ctrlr); +bool spdk_vhost_blk_get_readonly(struct spdk_vhost_dev *vdev); #endif /* SPDK_VHOST_H */ diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c index c361dc99f..b6e6a05bc 100644 --- a/lib/vhost/vhost.c +++ b/lib/vhost/vhost.c @@ -324,6 +324,7 @@ spdk_vhost_dev_construct(struct spdk_vhost_dev *vdev, const char *name, uint64_t } vdev->name = strdup(name); + vdev->path = strdup(path); vdev->vid = -1; vdev->lcore = -1; vdev->cpumask = cpumask; @@ -379,6 +380,7 @@ spdk_vhost_dev_remove(struct spdk_vhost_dev *vdev) SPDK_NOTICELOG("Controller %s: removed\n", vdev->name); free(vdev->name); + free(vdev->path); g_spdk_vhost_devices[ctrlr_num] = NULL; return 0; } diff --git a/lib/vhost/vhost_blk.c b/lib/vhost/vhost_blk.c index 16180d495..6a0b88eed 100644 --- a/lib/vhost/vhost_blk.c +++ b/lib/vhost/vhost_blk.c @@ -66,6 +66,7 @@ struct spdk_vhost_blk_dev { struct spdk_io_channel *bdev_io_channel; struct spdk_poller *requestq_poller; struct spdk_ring *tasks_pool; + bool readonly; }; static void @@ -256,10 +257,13 @@ process_blk_request(struct spdk_vhost_blk_task *task, struct spdk_vhost_blk_dev rc = spdk_bdev_readv(bvdev->bdev, bvdev->bdev_io_channel, &task->iovs[1], task->iovcnt, offset, task->length, blk_request_complete_cb, task); - } else { + } else if (!bvdev->readonly) { rc = spdk_bdev_writev(bvdev->bdev, bvdev->bdev_io_channel, &task->iovs[1], task->iovcnt, offset, task->length, blk_request_complete_cb, task); + } else { + SPDK_TRACELOG(SPDK_TRACE_VHOST_BLK, "Device is in read-only mode!\n"); + rc = -1; } if (rc) { @@ -422,6 +426,15 @@ spdk_vhost_blk_get_dev(struct spdk_vhost_dev *vdev) return bvdev->bdev; } +bool +spdk_vhost_blk_get_readonly(struct spdk_vhost_dev *vdev) +{ + struct spdk_vhost_blk_dev *bvdev = to_blk_dev(vdev); + + assert(bvdev != NULL); + return bvdev->readonly; +} + static void bdev_remove_cb(void *remove_ctx) { @@ -514,6 +527,7 @@ spdk_vhost_blk_controller_construct(void) char *cpumask_str; char *name; uint64_t cpumask; + bool readonly; for (sp = spdk_conf_first_section(NULL); sp != NULL; sp = spdk_conf_next_section(sp)) { if (!spdk_conf_section_match_prefix(sp, "VhostBlk")) { @@ -533,6 +547,7 @@ spdk_vhost_blk_controller_construct(void) } cpumask_str = spdk_conf_section_get_val(sp, "Cpumask"); + readonly = spdk_conf_section_get_boolval(sp, "ReadOnly", false); if (cpumask_str == NULL) { cpumask = spdk_app_get_core_mask(); } else if (spdk_vhost_parse_core_mask(cpumask_str, &cpumask)) { @@ -545,7 +560,7 @@ spdk_vhost_blk_controller_construct(void) continue; } - if (spdk_vhost_blk_construct(name, cpumask, bdev_name) < 0) { + if (spdk_vhost_blk_construct(name, cpumask, bdev_name, readonly) < 0) { return -1; } } @@ -554,7 +569,7 @@ spdk_vhost_blk_controller_construct(void) } int -spdk_vhost_blk_construct(const char *name, uint64_t cpumask, const char *dev_name) +spdk_vhost_blk_construct(const char *name, uint64_t cpumask, const char *dev_name, bool readonly) { struct spdk_vhost_blk_dev *bvdev; struct spdk_bdev *bdev; @@ -580,7 +595,7 @@ spdk_vhost_blk_construct(const char *name, uint64_t cpumask, const char *dev_nam } bvdev->bdev = bdev; - + bvdev->readonly = readonly; ret = spdk_vhost_dev_construct(&bvdev->vdev, name, cpumask, SPDK_VHOST_DEV_T_BLK, &vhost_blk_device_backend); if (ret != 0) { @@ -588,6 +603,17 @@ spdk_vhost_blk_construct(const char *name, uint64_t cpumask, const char *dev_nam goto err; } + if (readonly && rte_vhost_driver_enable_features(bvdev->vdev.path, (1ULL << VIRTIO_BLK_F_RO))) { + SPDK_ERRLOG("Controller %s: failed to set as a readonly\n", name); + spdk_bdev_close(bvdev->bdev_desc); + + if (spdk_vhost_dev_remove(&bvdev->vdev) != 0) { + SPDK_ERRLOG("Controller %s: failed to remove controller\n", name); + } + + goto err; + } + SPDK_NOTICELOG("Controller %s: using bdev '%s'\n", name, dev_name); diff --git a/lib/vhost/vhost_internal.h b/lib/vhost/vhost_internal.h index cf3d271a9..670e211ad 100644 --- a/lib/vhost/vhost_internal.h +++ b/lib/vhost/vhost_internal.h @@ -67,6 +67,7 @@ enum spdk_vhost_dev_type { struct spdk_vhost_dev { struct rte_vhost_memory *mem; char *name; + char *path; int vid; int task_cnt; diff --git a/lib/vhost/vhost_rpc.c b/lib/vhost/vhost_rpc.c index e7e96ff83..cf8ccc779 100644 --- a/lib/vhost/vhost_rpc.c +++ b/lib/vhost/vhost_rpc.c @@ -373,12 +373,14 @@ struct rpc_vhost_blk_ctrlr { char *ctrlr; char *dev_name; char *cpumask; + bool readonly; }; static const struct spdk_json_object_decoder rpc_construct_vhost_blk_ctrlr[] = { {"ctrlr", offsetof(struct rpc_vhost_blk_ctrlr, ctrlr), spdk_json_decode_string }, {"dev_name", offsetof(struct rpc_vhost_blk_ctrlr, dev_name), spdk_json_decode_string }, {"cpumask", offsetof(struct rpc_vhost_blk_ctrlr, cpumask), spdk_json_decode_string, true}, + {"readonly", offsetof(struct rpc_vhost_blk_ctrlr, readonly), spdk_json_decode_bool, true}, }; static void @@ -413,7 +415,7 @@ spdk_rpc_construct_vhost_blk_controller(struct spdk_jsonrpc_server_conn *conn, goto invalid; } - rc = spdk_vhost_blk_construct(req.ctrlr, cpumask, req.dev_name); + rc = spdk_vhost_blk_construct(req.ctrlr, cpumask, req.dev_name, req.readonly); if (rc < 0) { goto invalid; } @@ -526,6 +528,9 @@ spdk_rpc_get_vhost_blk_controllers(struct spdk_jsonrpc_server_conn *conn, spdk_json_write_name(w, "cpu_mask"); spdk_json_write_string_fmt(w, "%#" PRIx64, spdk_vhost_dev_get_cpumask(vdev)); + spdk_json_write_name(w, "readonly"); + spdk_json_write_bool(w, spdk_vhost_blk_get_readonly(vdev)); + bdev = spdk_vhost_blk_get_dev(vdev); spdk_json_write_name(w, "bdev"); if (bdev) diff --git a/scripts/rpc.py b/scripts/rpc.py index 4962c824c..3cd7640bb 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -536,12 +536,15 @@ def construct_vhost_blk_controller(args): } if args.cpumask: params['cpumask'] = args.cpumask + if args.readonly: + params['readonly'] = args.readonly jsonrpc_call('construct_vhost_blk_controller', params) p = subparsers.add_parser('construct_vhost_blk_controller', help='Add a new vhost block controller') p.add_argument('ctrlr', help='controller name') p.add_argument('dev_name', help='device name') p.add_argument('--cpumask', help='cpu mask for this controller') +p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only') p.set_defaults(func=construct_vhost_blk_controller) def remove_vhost_blk_controller(args):