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