qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

scsi: add persistent reservation manager using qemu-pr-helper

This adds a concrete subclass of pr-manager that talks to qemu-pr-helper.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

+303 -1
+1 -1
scsi/Makefile.objs
··· 1 1 block-obj-y += utils.o 2 2 3 - block-obj-$(CONFIG_LINUX) += pr-manager.o 3 + block-obj-$(CONFIG_LINUX) += pr-manager.o pr-manager-helper.o
+302
scsi/pr-manager-helper.c
··· 1 + /* 2 + * Persistent reservation manager that talks to qemu-pr-helper 3 + * 4 + * Copyright (c) 2017 Red Hat, Inc. 5 + * 6 + * Author: Paolo Bonzini <pbonzini@redhat.com> 7 + * 8 + * This code is licensed under the LGPL v2.1 or later. 9 + * 10 + */ 11 + 12 + #include "qemu/osdep.h" 13 + #include "qapi/error.h" 14 + #include "scsi/constants.h" 15 + #include "scsi/pr-manager.h" 16 + #include "scsi/utils.h" 17 + #include "io/channel.h" 18 + #include "io/channel-socket.h" 19 + #include "pr-helper.h" 20 + 21 + #include <scsi/sg.h> 22 + 23 + #define PR_MAX_RECONNECT_ATTEMPTS 5 24 + 25 + #define TYPE_PR_MANAGER_HELPER "pr-manager-helper" 26 + 27 + #define PR_MANAGER_HELPER(obj) \ 28 + OBJECT_CHECK(PRManagerHelper, (obj), \ 29 + TYPE_PR_MANAGER_HELPER) 30 + 31 + typedef struct PRManagerHelper { 32 + /* <private> */ 33 + PRManager parent; 34 + 35 + char *path; 36 + 37 + QemuMutex lock; 38 + QIOChannel *ioc; 39 + } PRManagerHelper; 40 + 41 + /* Called with lock held. */ 42 + static int pr_manager_helper_read(PRManagerHelper *pr_mgr, 43 + void *buf, int sz, Error **errp) 44 + { 45 + ssize_t r = qio_channel_read_all(pr_mgr->ioc, buf, sz, errp); 46 + 47 + if (r < 0) { 48 + object_unref(OBJECT(pr_mgr->ioc)); 49 + pr_mgr->ioc = NULL; 50 + return -EINVAL; 51 + } 52 + 53 + return 0; 54 + } 55 + 56 + /* Called with lock held. */ 57 + static int pr_manager_helper_write(PRManagerHelper *pr_mgr, 58 + int fd, 59 + const void *buf, int sz, Error **errp) 60 + { 61 + size_t nfds = (fd != -1); 62 + while (sz > 0) { 63 + struct iovec iov; 64 + ssize_t n_written; 65 + 66 + iov.iov_base = (void *)buf; 67 + iov.iov_len = sz; 68 + n_written = qio_channel_writev_full(QIO_CHANNEL(pr_mgr->ioc), &iov, 1, 69 + nfds ? &fd : NULL, nfds, errp); 70 + 71 + if (n_written <= 0) { 72 + assert(n_written != QIO_CHANNEL_ERR_BLOCK); 73 + object_unref(OBJECT(pr_mgr->ioc)); 74 + return n_written < 0 ? -EINVAL : 0; 75 + } 76 + 77 + nfds = 0; 78 + buf += n_written; 79 + sz -= n_written; 80 + } 81 + 82 + return 0; 83 + } 84 + 85 + /* Called with lock held. */ 86 + static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, 87 + Error **errp) 88 + { 89 + char *path = g_strdup(pr_mgr->path); 90 + SocketAddress saddr = { 91 + .type = SOCKET_ADDRESS_TYPE_UNIX, 92 + .u.q_unix.path = path 93 + }; 94 + QIOChannelSocket *sioc = qio_channel_socket_new(); 95 + Error *local_err = NULL; 96 + 97 + uint32_t flags; 98 + int r; 99 + 100 + assert(!pr_mgr->ioc); 101 + qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); 102 + qio_channel_socket_connect_sync(sioc, 103 + &saddr, 104 + &local_err); 105 + g_free(path); 106 + if (local_err) { 107 + object_unref(OBJECT(sioc)); 108 + error_propagate(errp, local_err); 109 + return -ENOTCONN; 110 + } 111 + 112 + qio_channel_set_delay(QIO_CHANNEL(sioc), false); 113 + pr_mgr->ioc = QIO_CHANNEL(sioc); 114 + 115 + /* A simple feature negotation protocol, even though there is 116 + * no optional feature right now. 117 + */ 118 + r = pr_manager_helper_read(pr_mgr, &flags, sizeof(flags), errp); 119 + if (r < 0) { 120 + goto out_close; 121 + } 122 + 123 + flags = 0; 124 + r = pr_manager_helper_write(pr_mgr, -1, &flags, sizeof(flags), errp); 125 + if (r < 0) { 126 + goto out_close; 127 + } 128 + 129 + return 0; 130 + 131 + out_close: 132 + object_unref(OBJECT(pr_mgr->ioc)); 133 + pr_mgr->ioc = NULL; 134 + return r; 135 + } 136 + 137 + static int pr_manager_helper_run(PRManager *p, 138 + int fd, struct sg_io_hdr *io_hdr) 139 + { 140 + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p); 141 + 142 + uint32_t len; 143 + PRHelperResponse resp; 144 + int ret; 145 + int expected_dir; 146 + int attempts; 147 + uint8_t cdb[PR_HELPER_CDB_SIZE] = { 0 }; 148 + 149 + if (!io_hdr->cmd_len || io_hdr->cmd_len > PR_HELPER_CDB_SIZE) { 150 + return -EINVAL; 151 + } 152 + 153 + memcpy(cdb, io_hdr->cmdp, io_hdr->cmd_len); 154 + assert(cdb[0] == PERSISTENT_RESERVE_OUT || cdb[0] == PERSISTENT_RESERVE_IN); 155 + expected_dir = 156 + (cdb[0] == PERSISTENT_RESERVE_OUT ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV); 157 + if (io_hdr->dxfer_direction != expected_dir) { 158 + return -EINVAL; 159 + } 160 + 161 + len = scsi_cdb_xfer(cdb); 162 + if (io_hdr->dxfer_len < len || len > PR_HELPER_DATA_SIZE) { 163 + return -EINVAL; 164 + } 165 + 166 + qemu_mutex_lock(&pr_mgr->lock); 167 + 168 + /* Try to reconnect while sending the CDB. */ 169 + for (attempts = 0; attempts < PR_MAX_RECONNECT_ATTEMPTS; attempts++) { 170 + if (!pr_mgr->ioc) { 171 + ret = pr_manager_helper_initialize(pr_mgr, NULL); 172 + if (ret < 0) { 173 + qemu_mutex_unlock(&pr_mgr->lock); 174 + g_usleep(G_USEC_PER_SEC); 175 + qemu_mutex_lock(&pr_mgr->lock); 176 + continue; 177 + } 178 + } 179 + 180 + ret = pr_manager_helper_write(pr_mgr, fd, cdb, ARRAY_SIZE(cdb), NULL); 181 + if (ret >= 0) { 182 + break; 183 + } 184 + } 185 + if (ret < 0) { 186 + goto out; 187 + } 188 + 189 + /* After sending the CDB, any communications failure causes the 190 + * command to fail. The failure is transient, retrying the command 191 + * will invoke pr_manager_helper_initialize again. 192 + */ 193 + if (expected_dir == SG_DXFER_TO_DEV) { 194 + io_hdr->resid = io_hdr->dxfer_len - len; 195 + ret = pr_manager_helper_write(pr_mgr, -1, io_hdr->dxferp, len, NULL); 196 + if (ret < 0) { 197 + goto out; 198 + } 199 + } 200 + ret = pr_manager_helper_read(pr_mgr, &resp, sizeof(resp), NULL); 201 + if (ret < 0) { 202 + goto out; 203 + } 204 + 205 + resp.result = be32_to_cpu(resp.result); 206 + resp.sz = be32_to_cpu(resp.sz); 207 + if (io_hdr->dxfer_direction == SG_DXFER_FROM_DEV) { 208 + assert(resp.sz <= io_hdr->dxfer_len); 209 + ret = pr_manager_helper_read(pr_mgr, io_hdr->dxferp, resp.sz, NULL); 210 + if (ret < 0) { 211 + goto out; 212 + } 213 + io_hdr->resid = io_hdr->dxfer_len - resp.sz; 214 + } else { 215 + assert(resp.sz == 0); 216 + } 217 + 218 + io_hdr->status = resp.result; 219 + if (resp.result == CHECK_CONDITION) { 220 + io_hdr->driver_status = SG_ERR_DRIVER_SENSE; 221 + io_hdr->sb_len_wr = MIN(io_hdr->mx_sb_len, PR_HELPER_SENSE_SIZE); 222 + memcpy(io_hdr->sbp, resp.sense, io_hdr->sb_len_wr); 223 + } 224 + 225 + out: 226 + if (ret < 0) { 227 + int sense_len = scsi_build_sense(io_hdr->sbp, 228 + SENSE_CODE(LUN_COMM_FAILURE)); 229 + io_hdr->driver_status = SG_ERR_DRIVER_SENSE; 230 + io_hdr->sb_len_wr = MIN(io_hdr->mx_sb_len, sense_len); 231 + io_hdr->status = CHECK_CONDITION; 232 + } 233 + qemu_mutex_unlock(&pr_mgr->lock); 234 + return ret; 235 + } 236 + 237 + static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) 238 + { 239 + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(uc); 240 + 241 + qemu_mutex_lock(&pr_mgr->lock); 242 + pr_manager_helper_initialize(pr_mgr, errp); 243 + qemu_mutex_unlock(&pr_mgr->lock); 244 + } 245 + 246 + static char *get_path(Object *obj, Error **errp) 247 + { 248 + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); 249 + 250 + return g_strdup(pr_mgr->path); 251 + } 252 + 253 + static void set_path(Object *obj, const char *str, Error **errp) 254 + { 255 + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); 256 + 257 + g_free(pr_mgr->path); 258 + pr_mgr->path = g_strdup(str); 259 + } 260 + 261 + static void pr_manager_helper_instance_finalize(Object *obj) 262 + { 263 + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); 264 + 265 + object_unref(OBJECT(pr_mgr->ioc)); 266 + qemu_mutex_destroy(&pr_mgr->lock); 267 + } 268 + 269 + static void pr_manager_helper_instance_init(Object *obj) 270 + { 271 + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); 272 + 273 + qemu_mutex_init(&pr_mgr->lock); 274 + } 275 + 276 + static void pr_manager_helper_class_init(ObjectClass *klass, 277 + void *class_data G_GNUC_UNUSED) 278 + { 279 + PRManagerClass *prmgr_klass = PR_MANAGER_CLASS(klass); 280 + UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); 281 + 282 + object_class_property_add_str(klass, "path", get_path, set_path, 283 + &error_abort); 284 + uc_klass->complete = pr_manager_helper_complete; 285 + prmgr_klass->run = pr_manager_helper_run; 286 + } 287 + 288 + static const TypeInfo pr_manager_helper_info = { 289 + .parent = TYPE_PR_MANAGER, 290 + .name = TYPE_PR_MANAGER_HELPER, 291 + .instance_size = sizeof(PRManagerHelper), 292 + .instance_init = pr_manager_helper_instance_init, 293 + .instance_finalize = pr_manager_helper_instance_finalize, 294 + .class_init = pr_manager_helper_class_init, 295 + }; 296 + 297 + static void pr_manager_helper_register_types(void) 298 + { 299 + type_register_static(&pr_manager_helper_info); 300 + } 301 + 302 + type_init(pr_manager_helper_register_types);