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