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