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