1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 /*
17 * Kernel Remote Replication Protocol (KRRP)
18 */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/kmem.h>
23 #include <sys/ddi.h>
24 #include <sys/sunddi.h>
25 #include <sys/sunldi.h>
26 #include <sys/time.h>
27 #include <sys/strsubr.h>
28 #include <sys/sysmacros.h>
29 #include <sys/sdt.h>
30 #include <sys/modctl.h>
31 #include <sys/class.h>
32 #include <sys/cmn_err.h>
33
34 #include <krrp_error.h>
35 #include <sys/krrp.h>
36
37 #include "krrp_svc.h"
38 #include "krrp_ioctl.h"
39
40 static int krrp_open(dev_t *, int, int, cred_t *);
41 static int krrp_close(dev_t, int, int, cred_t *);
42 static int krrp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
43 static int krrp_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
44 static int krrp_attach(dev_info_t *, ddi_attach_cmd_t);
45 static int krrp_detach(dev_info_t *, ddi_detach_cmd_t);
46
47 static struct cb_ops krrp_cb_ops = {
48 krrp_open, /* open */
49 krrp_close, /* close */
50 nodev, /* strategy */
51 nodev, /* print */
52 nodev, /* dump */
53 nodev, /* read */
54 nodev, /* write */
55 krrp_ioctl, /* ioctl */
56 nodev, /* devmap */
57 nodev, /* mmap */
58 nodev, /* segmap */
59 nochpoll, /* poll */
60 ddi_prop_op, /* prop_op */
61 NULL, /* streamtab */
62 D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */
63 CB_REV, /* version */
64 nodev, /* async read */
65 nodev, /* async write */
66 };
67
68 static struct dev_ops krrp_dev_ops = {
69 DEVO_REV, /* version */
70 0, /* refcnt */
71 krrp_info, /* info */
72 nulldev, /* identify */
73 nulldev, /* probe */
74 krrp_attach, /* attach */
75 krrp_detach, /* detach */
76 nodev, /* reset */
77 &krrp_cb_ops, /* driver operations */
78 NULL /* no bus operations */
79 };
80
81 static struct modldrv modldrv = {
82 &mod_driverops,
83 "kernel remote replication protocol module",
84 &krrp_dev_ops
85 };
86
87 static struct modlinkage modlinkage = {
88 MODREV_1, (void *)&modldrv, NULL
89 };
90
91 static uint16_t version = 1;
92
93 int
94 _init(void)
95 {
96 int rc;
97 krrp_svc_t *krrp_svc;
98
99 krrp_svc = krrp_svc_get_instance();
100
101 rc = ldi_ident_from_mod(&modlinkage, &krrp_svc->li);
102 if (rc != 0)
103 return (rc);
104
105 rc = mod_install(&modlinkage);
106 if (rc) {
107 ldi_ident_release(krrp_svc->li);
108 return (rc);
109 }
110
111 krrp_svc_init();
112
113 return (0);
114 }
115
116 int
117 _fini(void)
118 {
119 int error;
120 krrp_svc_t *krrp_svc;
121
122 krrp_svc = krrp_svc_get_instance();
123
124 error = mod_remove(&modlinkage);
125 if (error != 0)
126 return (error);
127
128 ldi_ident_release(krrp_svc->li);
129
130 krrp_svc_fini();
131
132 return (0);
133 }
134
135 int
136 _info(struct modinfo *modinfop)
137 {
138 return (mod_info(&modlinkage, modinfop));
139 }
140
141 /* ARGSUSED */
142 static int
143 krrp_open(dev_t *devp, int flag, int otyp, cred_t *cr)
144 {
145 return (0);
146 }
147
148 /* ARGSUSED */
149 static int
150 krrp_close(dev_t dev, int flag, int otyp, cred_t *cr)
151 {
152 return (0);
153 }
154
155 /* ARGSUSED */
156 static int
157 krrp_ioctl(dev_t dev, int cmd, intptr_t argp, int flags, cred_t *cr, int *rvalp)
158 {
159 int rc = 0;
160 size_t size = 0;
161 size_t total_size;
162 krrp_ioctl_data_t ioctl_data_hdr, *ioctl_data;
163 nvlist_t *in_nvl = NULL;
164 nvlist_t *out_nvl = NULL;
165 nvlist_t *error_nvl = NULL;
166 krrp_error_t error;
167 boolean_t ref_cnt_held;
168
169 if (krrp_ioctl_validate_cmd(cmd) != 0) {
170 cmn_err(CE_WARN, "Invalid ioctl command (%d) "
171 "or version mismatch", cmd);
172 return (ENOTSUP);
173 }
174
175 ref_cnt_held = krrp_svc_ref_cnt_try_hold() == 0;
176 if (!ref_cnt_held && cmd != KRRP_IOCTL_SVC_ENABLE &&
177 cmd != KRRP_IOCTL_SVC_STATE) {
178 cmn_err(CE_WARN, "KRRP in-kernel service is not enabled");
179 return (ENOTACTIVE);
180 }
181
182 if (ref_cnt_held && cmd == KRRP_IOCTL_SVC_ENABLE) {
183 cmn_err(CE_WARN, "KRRP in-kernel service already enabled");
184 rc = EALREADY;
185 goto out;
186 }
187
188 (void) memset(&ioctl_data_hdr, 0, sizeof (ioctl_data_hdr));
189 rc = ddi_copyin((void *) argp, &ioctl_data_hdr,
190 sizeof (krrp_ioctl_data_t), flags);
191 if (rc != 0) {
192 cmn_err(CE_WARN, "Failed to ddi_copyin() ioctl data hdr");
193 rc = EFAULT;
194 goto out;
195 }
196
197 if (ioctl_data_hdr.buf_size == 0) {
198 cmn_err(CE_WARN, "No data buffer");
199 rc = ENOBUFS;
200 goto out;
201 }
202
203 total_size = sizeof (krrp_ioctl_data_t) + ioctl_data_hdr.buf_size;
204 ioctl_data = kmem_alloc(total_size, KM_SLEEP);
205 rc = ddi_copyin((void *) argp, ioctl_data, total_size, flags);
206 if (rc != 0) {
207 cmn_err(CE_WARN, "Failed to ddi_copyin() ioctl data buffer");
208 rc = EFAULT;
209 goto out;
210 }
211
212 if (ioctl_data->data_size != 0) {
213 rc = nvlist_unpack(ioctl_data->buf,
214 ioctl_data->data_size, &in_nvl, KM_SLEEP);
215 if (rc != 0) {
216 /*
217 * Unpacking of userspace data is a fatal error
218 * (incorrect use of ioctl API),
219 * so we just print error to syslog and return
220 */
221 cmn_err(CE_WARN, "Failed to unpack nvlist [%d]", rc);
222 goto out_kmem_free;
223 }
224 }
225
226 out_nvl = fnvlist_alloc();
227
228 krrp_error_init(&error);
229 rc = krrp_ioctl_process(cmd, in_nvl, out_nvl, &error);
230
231 /*
232 * Ioctl has been successfully executed.
233 * So lets try to pack its result if exists
234 */
235 if (rc == 0) {
236 VERIFY(error.krrp_errno == 0);
237
238 if (nvlist_empty(out_nvl)) {
239 ioctl_data->data_size = 0;
240 } else {
241 size = fnvlist_size(out_nvl);
242 if (size > ioctl_data->buf_size) {
243 rc = ENOSPC;
244 goto out_nvl_free;
245 } else {
246 char *buf;
247
248 buf = ioctl_data->buf;
249 VERIFY3U(nvlist_pack(out_nvl, (char **)&buf,
250 &size, NV_ENCODE_NATIVE, KM_SLEEP), ==, 0);
251 ioctl_data->data_size = (uint64_t)size;
252 ioctl_data->out_flags |= KRRP_IOCTL_FLAG_RESULT;
253 }
254 }
255 }
256
257 /*
258 * An error occurred during execution of ioctl or
259 * during packing of its result
260 * lets try to copyout the error
261 */
262 if (rc != 0 || error.krrp_errno != 0) {
263 VERIFY(error.krrp_errno != 0);
264
265 cmn_err(CE_WARN, "KRRP Error: [cmd: %d] [%s] [%d]",
266 cmd, krrp_error_errno_to_str(error.krrp_errno),
267 error.unix_errno);
268
269 krrp_error_to_nvl(&error, &error_nvl);
270
271 size = fnvlist_size(error_nvl);
272 if (size > ioctl_data->buf_size) {
273 cmn_err(CE_WARN, "The size of provided buffer "
274 "is to small to store the following error:\n"
275 "[ke: [%d], ue: [%d]]",
276 error.krrp_errno, error.unix_errno);
277 rc = ENOSPC;
278 } else {
279 char *buf;
280
281 buf = ioctl_data->buf;
282 VERIFY3U(nvlist_pack(error_nvl, (char **)&buf,
283 &size, NV_ENCODE_NATIVE, KM_SLEEP), ==, 0);
284 rc = 0;
285 ioctl_data->data_size = (uint64_t)size;
286 ioctl_data->out_flags |= KRRP_IOCTL_FLAG_ERROR;
287 }
288
289 fnvlist_free(error_nvl);
290 }
291
292 /*
293 * rc == 0 if
294 * - successfully executed ioctl
295 * - an error occurred and we successfully packed it
296 *
297 * So here we try to copyout the data
298 */
299 if (rc == 0) {
300 rc = ddi_copyout((void *) ioctl_data, (void *) argp,
301 total_size, flags);
302 if (rc != 0) {
303 cmn_err(CE_WARN, "Failed to ddi_copyout() "
304 "ioctl data buffer");
305 rc = EFAULT;
306 }
307 }
308
309 out_nvl_free:
310 fnvlist_free(out_nvl);
311 fnvlist_free(in_nvl);
312
313 out_kmem_free:
314 kmem_free(ioctl_data, total_size);
315
316 out:
317 if (ref_cnt_held)
318 krrp_svc_ref_cnt_rele();
319
320 return (rc);
321 }
322
323 /* ARGSUSED */
324 static int
325 krrp_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
326 {
327 krrp_svc_t *krrp_svc;
328
329 krrp_svc = krrp_svc_get_instance();
330
331 switch (infocmd) {
332 case DDI_INFO_DEVT2DEVINFO:
333 *result = krrp_svc->dip;
334 return (DDI_SUCCESS);
335 case DDI_INFO_DEVT2INSTANCE:
336 *result = (void *) 0;
337 return (DDI_SUCCESS);
338 default:
339 return (DDI_FAILURE);
340 }
341 }
342
343 static int
344 krrp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
345 {
346 if (cmd != DDI_ATTACH)
347 return (DDI_FAILURE);
348
349 if (ddi_get_instance(dip) != 0)
350 return (DDI_FAILURE);
351
352 if (ddi_create_minor_node(dip, KRRP_DRIVER, S_IFCHR, 0,
353 DDI_PSEUDO, 0) != DDI_SUCCESS) {
354 return (DDI_FAILURE);
355 }
356
357 krrp_svc_attach(dip);
358 ddi_report_dev(dip);
359
360 return (DDI_SUCCESS);
361 }
362
363 static int
364 krrp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
365 {
366 if (cmd != DDI_DETACH)
367 return (DDI_FAILURE);
368
369 if (krrp_svc_detach() != 0)
370 return (EBUSY);
371
372 ddi_remove_minor_node(dip, NULL);
373 ddi_prop_remove_all(dip);
374
375 return (DDI_SUCCESS);
376 }