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