]> git.proxmox.com Git - mirror_zfs.git/blame - zfs/lib/libdmu-ctl/dctl_server.c
Remove stray stub kernel files which should be brought in my linux-kernel-module...
[mirror_zfs.git] / zfs / lib / libdmu-ctl / dctl_server.c
CommitLineData
34dc7c2f
BB
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <stdio.h>
28#include <stddef.h>
29#include <stdlib.h>
30#include <string.h>
31#include <signal.h>
32#include <limits.h>
33#include <errno.h>
34#include <poll.h>
35#include <pthread.h>
36#include <unistd.h>
37#include <sys/debug.h>
38#include <sys/socket.h>
39#include <sys/stat.h>
40#include <sys/types.h>
41#include <sys/un.h>
42#include <sys/list.h>
43#include <sys/cred.h>
44
45#include <sys/dmu_ctl.h>
46#include <sys/dmu_ctl_impl.h>
47
48static dctl_sock_info_t ctl_sock = {
49 .dsi_mtx = PTHREAD_MUTEX_INITIALIZER,
50 .dsi_fd = -1
51};
52
53static int dctl_create_socket_common();
54
55/*
56 * Routines from zfs_ioctl.c
57 */
58extern int zfs_ioctl_init();
59extern int zfs_ioctl_fini();
60extern int zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr,
61 int *rvalp);
62
63/*
64 * We can't simply put the client file descriptor in wthr_info_t because we
65 * have no way of accessing it from the DMU code without extensive
66 * modifications.
67 *
68 * Therefore each worker thread will have it's own global thread-specific
69 * client_fd variable.
70 */
71static __thread int client_fd = -1;
72
73int dctls_copyin(const void *src, void *dest, size_t size)
74{
75 dctl_cmd_t cmd;
76
77 VERIFY(client_fd >= 0);
78
79 cmd.dcmd_msg = DCTL_COPYIN;
80 cmd.u.dcmd_copy.ptr = (uintptr_t) src;
81 cmd.u.dcmd_copy.size = size;
82
83 if (dctl_send_msg(client_fd, &cmd) != 0)
84 return EFAULT;
85
86 if (dctl_read_data(client_fd, dest, size) != 0)
87 return EFAULT;
88
89 return 0;
90}
91
92int dctls_copyinstr(const char *from, char *to, size_t max, size_t *len)
93{
94 dctl_cmd_t msg;
95 size_t copied;
96
97 VERIFY(client_fd >= 0);
98
99 if (max == 0)
100 return ENAMETOOLONG;
101 if (max < 0)
102 return EFAULT;
103
104 msg.dcmd_msg = DCTL_COPYINSTR;
105 msg.u.dcmd_copy.ptr = (uintptr_t) from;
106 msg.u.dcmd_copy.size = max;
107
108 if (dctl_send_msg(client_fd, &msg) != 0)
109 return EFAULT;
110
111 if (dctl_read_msg(client_fd, &msg) != 0)
112 return EFAULT;
113
114 if (msg.dcmd_msg != DCTL_GEN_REPLY)
115 return EFAULT;
116
117 copied = msg.u.dcmd_reply.size;
118
119 if (copied >= max)
120 return EFAULT;
121
122 if (copied > 0)
123 if (dctl_read_data(client_fd, to, copied) != 0)
124 return EFAULT;
125
126 to[copied] = '\0';
127
128 if (len != NULL)
129 *len = copied + 1;
130
131 return msg.u.dcmd_reply.rc;
132}
133
134int dctls_copyout(const void *src, void *dest, size_t size)
135{
136 dctl_cmd_t cmd;
137
138 VERIFY(client_fd >= 0);
139
140 cmd.dcmd_msg = DCTL_COPYOUT;
141 cmd.u.dcmd_copy.ptr = (uintptr_t) dest;
142 cmd.u.dcmd_copy.size = size;
143
144 if (dctl_send_msg(client_fd, &cmd) != 0)
145 return EFAULT;
146
147 if (dctl_send_data(client_fd, src, size) != 0)
148 return EFAULT;
149
150 return 0;
151}
152
153int dctls_fd_read(int fd, void *buf, ssize_t len, ssize_t *residp)
154{
155 dctl_cmd_t msg;
156 uint64_t dsize;
157 int error;
158
159 VERIFY(client_fd >= 0);
160
161 msg.dcmd_msg = DCTL_FD_READ;
162 msg.u.dcmd_fd_io.fd = fd;
163 msg.u.dcmd_fd_io.size = len;
164
165 if ((error = dctl_send_msg(client_fd, &msg)) != 0)
166 return error;
167
168 if ((error = dctl_read_msg(client_fd, &msg)) != 0)
169 return error;
170
171 if (msg.dcmd_msg != DCTL_GEN_REPLY)
172 return EIO;
173
174 if (msg.u.dcmd_reply.rc != 0)
175 return msg.u.dcmd_reply.rc;
176
177 dsize = msg.u.dcmd_reply.size;
178
179 if (dsize > 0)
180 error = dctl_read_data(client_fd, buf, dsize);
181
182 *residp = len - dsize;
183
184 return error;
185}
186
187int dctls_fd_write(int fd, const void *src, ssize_t len)
188{
189 dctl_cmd_t msg;
190 int error;
191
192 VERIFY(client_fd >= 0);
193
194 msg.dcmd_msg = DCTL_FD_WRITE;
195 msg.u.dcmd_fd_io.fd = fd;
196 msg.u.dcmd_fd_io.size = len;
197
198 error = dctl_send_msg(client_fd, &msg);
199
200 if (!error)
201 error = dctl_send_data(client_fd, src, len);
202
203 if (!error)
204 error = dctl_read_msg(client_fd, &msg);
205
206 if (error)
207 return error;
208
209 if (msg.dcmd_msg != DCTL_GEN_REPLY)
210 return EIO;
211
212 if (msg.u.dcmd_reply.rc != 0)
213 return msg.u.dcmd_reply.rc;
214
215 /*
216 * We have to do this because the original upstream code
217 * does not check if residp == len.
218 */
219 if (msg.u.dcmd_reply.size != len)
220 return EIO;
221
222 return 0;
223}
224
225/* Handle a new connection */
226static void dctl_handle_conn(int sock_fd)
227{
228 dctl_cmd_t cmd;
229 dev_t dev = { 0 };
230 int rc;
231
232 client_fd = sock_fd;
233
234 while (dctl_read_msg(sock_fd, &cmd) == 0) {
235 if (cmd.dcmd_msg != DCTL_IOCTL) {
236 fprintf(stderr, "%s(): unexpected message type.\n",
237 __func__);
238 break;
239 }
240
241 rc = zfsdev_ioctl(dev, cmd.u.dcmd_ioctl.cmd,
242 (intptr_t) cmd.u.dcmd_ioctl.arg, 0, NULL, NULL);
243
244 cmd.dcmd_msg = DCTL_IOCTL_REPLY;
245 cmd.u.dcmd_reply.rc = rc;
246
247 if (dctl_send_msg(sock_fd, &cmd) != 0)
248 break;
249 }
250 close(sock_fd);
251
252 client_fd = -1;
253}
254
255/* Main worker thread loop */
256static void *dctl_thread(void *arg)
257{
258 wthr_info_t *thr = arg;
259 struct pollfd fds[1];
260
261 fds[0].events = POLLIN;
262
263 pthread_mutex_lock(&ctl_sock.dsi_mtx);
264
265 while (!thr->wthr_exit) {
266 /* Clean-up dead threads */
267 dctl_thr_join();
268
269 /* The file descriptor might change in the thread lifetime */
270 fds[0].fd = ctl_sock.dsi_fd;
271
272 /* Poll socket with 1-second timeout */
273 int rc = poll(fds, 1, 1000);
274 if (rc == 0 || (rc == -1 && errno == EINTR))
275 continue;
276
277 /* Recheck the exit flag */
278 if (thr->wthr_exit)
279 break;
280
281 if (rc == -1) {
282 /* Unknown error, let's try to recreate the socket */
283 close(ctl_sock.dsi_fd);
284 ctl_sock.dsi_fd = -1;
285
286 if (dctl_create_socket_common() != 0)
287 break;
288
289 continue;
290 }
291 ASSERT(rc == 1);
292
293 short rev = fds[0].revents;
294 if (rev == 0)
295 continue;
296 ASSERT(rev == POLLIN);
297
298 /*
299 * At this point there should be a connection ready to be
300 * accepted.
301 */
302 int client_fd = accept(ctl_sock.dsi_fd, NULL, NULL);
303 /* Many possible errors here, we'll just retry */
304 if (client_fd == -1)
305 continue;
306
307 /*
308 * Now lets handle the request. This can take a very
309 * long time (hours even), so we'll let other threads
310 * handle new connections.
311 */
312 pthread_mutex_unlock(&ctl_sock.dsi_mtx);
313
314 dctl_thr_rebalance(thr, B_FALSE);
315 dctl_handle_conn(client_fd);
316 dctl_thr_rebalance(thr, B_TRUE);
317
318 pthread_mutex_lock(&ctl_sock.dsi_mtx);
319 }
320 pthread_mutex_unlock(&ctl_sock.dsi_mtx);
321
322 dctl_thr_die(thr);
323
324 return NULL;
325}
326
327static int dctl_create_socket_common()
328{
329 dctl_sock_info_t *s = &ctl_sock;
330 size_t size;
331 int error;
332
333 ASSERT(s->dsi_fd == -1);
334
335 /*
336 * Unlink old socket, in case it exists.
337 * We don't care about errors here.
338 */
339 unlink(s->dsi_path);
340
341 /* Create the socket */
342 s->dsi_fd = socket(PF_UNIX, SOCK_STREAM, 0);
343 if (s->dsi_fd == -1) {
344 error = errno;
345 perror("socket");
346 return error;
347 }
348
349 s->dsi_addr.sun_family = AF_UNIX;
350
351 size = sizeof(s->dsi_addr.sun_path) - 1;
352 strncpy(s->dsi_addr.sun_path, s->dsi_path, size);
353
354 s->dsi_addr.sun_path[size] = '\0';
355
356 if (bind(s->dsi_fd, (struct sockaddr *) &s->dsi_addr,
357 sizeof(s->dsi_addr)) != 0) {
358 error = errno;
359 perror("bind");
360 return error;
361 }
362
363 if (listen(s->dsi_fd, LISTEN_BACKLOG) != 0) {
364 error = errno;
365 perror("listen");
366 unlink(s->dsi_path);
367 return error;
368 }
369
370 return 0;
371}
372
373static int dctl_create_socket(const char *cfg_dir)
374{
375 int error;
376 dctl_sock_info_t *s = &ctl_sock;
377
378 ASSERT(s->dsi_path == NULL);
379 ASSERT(s->dsi_fd == -1);
380
381 int pathsize = strlen(cfg_dir) + strlen(SOCKNAME) + 2;
382 if (pathsize > sizeof(s->dsi_addr.sun_path))
383 return ENAMETOOLONG;
384
385 s->dsi_path = malloc(pathsize);
386 if (s->dsi_path == NULL)
387 return ENOMEM;
388
389 strcpy(s->dsi_path, cfg_dir);
390 strcat(s->dsi_path, "/" SOCKNAME);
391
392 /*
393 * For convenience, create the directory in case it doesn't exist.
394 * We don't care about errors here.
395 */
396 mkdir(cfg_dir, 0770);
397
398 error = dctl_create_socket_common();
399
400 if (error) {
401 free(s->dsi_path);
402
403 if (s->dsi_fd != -1) {
404 close(s->dsi_fd);
405 s->dsi_fd = -1;
406 }
407 }
408
409 return error;
410}
411
412static void dctl_destroy_socket()
413{
414 dctl_sock_info_t *s = &ctl_sock;
415
416 ASSERT(s->dsi_path != NULL);
417 ASSERT(s->dsi_fd != -1);
418
419 close(s->dsi_fd);
420 s->dsi_fd = -1;
421
422 unlink(s->dsi_path);
423 free(s->dsi_path);
424}
425
426/*
427 * Initialize the DMU userspace control interface.
428 * This should be called after kernel_init().
429 *
430 * Note that only very rarely we have more than a couple of simultaneous
431 * lzfs/lzpool connections. Since the thread pool grows automatically when all
432 * threads are busy, a good value for min_thr and max_free_thr is 2.
433 */
434int dctl_server_init(const char *cfg_dir, int min_thr, int max_free_thr)
435{
436 int error;
437
438 ASSERT(min_thr > 0);
439 ASSERT(max_free_thr >= min_thr);
440
441 error = zfs_ioctl_init();
442 if (error)
443 return error;
444
445 error = dctl_create_socket(cfg_dir);
446 if (error) {
447 (void) zfs_ioctl_fini();
448 return error;
449 }
450
451 error = dctl_thr_pool_create(min_thr, max_free_thr, dctl_thread);
452 if (error) {
453 (void) zfs_ioctl_fini();
454 dctl_destroy_socket();
455 return error;
456 }
457
458 return 0;
459}
460
461/*
462 * Terminate control interface.
463 * This should be called after closing all objsets, but before calling
464 * kernel_fini().
465 * May return EBUSY if the SPA is busy.
466 *
467 * Thread pool destruction can take a while due to poll()
468 * timeout or due to a thread being busy (e.g. a backup is being taken).
469 */
470int dctl_server_fini()
471{
472 dctl_thr_pool_stop();
473 dctl_destroy_socket();
474
475 return zfs_ioctl_fini();
476}