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