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 | |