]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/hastctl/hastctl.c
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / sbin / hastctl / hastctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Pawel Jakub Dawidek under sponsorship from
8  * the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36
37 #include <err.h>
38 #include <libutil.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include <activemap.h>
44
45 #include "hast.h"
46 #include "hast_proto.h"
47 #include "metadata.h"
48 #include "nv.h"
49 #include "pjdlog.h"
50 #include "proto.h"
51 #include "subr.h"
52
53 /* Path to configuration file. */
54 static const char *cfgpath = HAST_CONFIG;
55 /* Hastd configuration. */
56 static struct hastd_config *cfg;
57 /* Control connection. */
58 static struct proto_conn *controlconn;
59
60 enum {
61         CMD_INVALID,
62         CMD_CREATE,
63         CMD_ROLE,
64         CMD_STATUS,
65         CMD_DUMP,
66         CMD_LIST
67 };
68
69 static __dead2 void
70 usage(void)
71 {
72
73         fprintf(stderr,
74             "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n"
75             "\t\t[-m mediasize] name ...\n",
76             getprogname());
77         fprintf(stderr,
78             "       %s role [-d] [-c config] <init | primary | secondary> all | name ...\n",
79             getprogname());
80         fprintf(stderr,
81             "       %s list [-d] [-c config] [all | name ...]\n",
82             getprogname());
83         fprintf(stderr,
84             "       %s status [-d] [-c config] [all | name ...]\n",
85             getprogname());
86         fprintf(stderr,
87             "       %s dump [-d] [-c config] [all | name ...]\n",
88             getprogname());
89         exit(EX_USAGE);
90 }
91
92 static int
93 create_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize,
94     intmax_t keepdirty)
95 {
96         unsigned char *buf;
97         size_t mapsize;
98         int ec;
99
100         ec = 0;
101         pjdlog_prefix_set("[%s] ", res->hr_name);
102
103         if (provinfo(res, true) == -1) {
104                 ec = EX_NOINPUT;
105                 goto end;
106         }
107         if (mediasize == 0)
108                 mediasize = res->hr_local_mediasize;
109         else if (mediasize > res->hr_local_mediasize) {
110                 pjdlog_error("Provided mediasize is larger than provider %s size.",
111                     res->hr_localpath);
112                 ec = EX_DATAERR;
113                 goto end;
114         }
115         if (!powerof2(res->hr_local_sectorsize)) {
116                 pjdlog_error("Sector size of provider %s is not power of 2 (%u).",
117                     res->hr_localpath, res->hr_local_sectorsize);
118                 ec = EX_DATAERR;
119                 goto end;
120         }
121         if (extentsize == 0)
122                 extentsize = HAST_EXTENTSIZE;
123         if (extentsize < res->hr_local_sectorsize) {
124                 pjdlog_error("Extent size (%jd) is less than sector size (%u).",
125                     (intmax_t)extentsize, res->hr_local_sectorsize);
126                 ec = EX_DATAERR;
127                 goto end;
128         }
129         if ((extentsize % res->hr_local_sectorsize) != 0) {
130                 pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).",
131                     (intmax_t)extentsize, res->hr_local_sectorsize);
132                 ec = EX_DATAERR;
133                 goto end;
134         }
135         mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE,
136             extentsize, res->hr_local_sectorsize);
137         if (keepdirty == 0)
138                 keepdirty = HAST_KEEPDIRTY;
139         res->hr_datasize = mediasize - METADATA_SIZE - mapsize;
140         res->hr_extentsize = extentsize;
141         res->hr_keepdirty = keepdirty;
142
143         res->hr_localoff = METADATA_SIZE + mapsize;
144
145         if (metadata_write(res) == -1) {
146                 ec = EX_IOERR;
147                 goto end;
148         }
149         buf = calloc(1, mapsize);
150         if (buf == NULL) {
151                 pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.",
152                     mapsize);
153                 ec = EX_TEMPFAIL;
154                 goto end;
155         }
156         if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) !=
157             (ssize_t)mapsize) {
158                 pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s",
159                     res->hr_localpath);
160                 free(buf);
161                 ec = EX_IOERR;
162                 goto end;
163         }
164         free(buf);
165 end:
166         if (res->hr_localfd >= 0)
167                 close(res->hr_localfd);
168         pjdlog_prefix_set("%s", "");
169         return (ec);
170 }
171
172 static void
173 control_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize,
174     intmax_t keepdirty)
175 {
176         struct hast_resource *res;
177         int ec, ii, ret;
178
179         /* Initialize the given resources. */
180         if (argc < 1)
181                 usage();
182         ec = 0;
183         for (ii = 0; ii < argc; ii++) {
184                 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
185                         if (strcmp(argv[ii], res->hr_name) == 0)
186                                 break;
187                 }
188                 if (res == NULL) {
189                         pjdlog_error("Unknown resource %s.", argv[ii]);
190                         if (ec == 0)
191                                 ec = EX_DATAERR;
192                         continue;
193                 }
194                 ret = create_one(res, mediasize, extentsize, keepdirty);
195                 if (ret != 0 && ec == 0)
196                         ec = ret;
197         }
198         exit(ec);
199 }
200
201 static int
202 dump_one(struct hast_resource *res)
203 {
204         int ret;
205
206         ret = metadata_read(res, false);
207         if (ret != 0)
208                 return (ret);
209
210         printf("resource: %s\n", res->hr_name);
211         printf("    datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize,
212             (intmax_t)res->hr_datasize);
213         printf("    extentsize: %d (%NB)\n", res->hr_extentsize,
214             (intmax_t)res->hr_extentsize);
215         printf("    keepdirty: %d\n", res->hr_keepdirty);
216         printf("    localoff: %ju\n", (uintmax_t)res->hr_localoff);
217         printf("    resuid: %ju\n", (uintmax_t)res->hr_resuid);
218         printf("    localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt);
219         printf("    remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt);
220         printf("    prevrole: %s\n", role2str(res->hr_previous_role));
221
222         return (0);
223 }
224
225 static void
226 control_dump(int argc, char *argv[])
227 {
228         struct hast_resource *res;
229         int ec, ret;
230
231         /* Dump metadata of the given resource(s). */
232
233         ec = 0;
234         if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) {
235                 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
236                         ret = dump_one(res);
237                         if (ret != 0 && ec == 0)
238                                 ec = ret;
239                 }
240         } else {
241                 int ii;
242
243                 for (ii = 0; ii < argc; ii++) {
244                         TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
245                                 if (strcmp(argv[ii], res->hr_name) == 0)
246                                         break;
247                         }
248                         if (res == NULL) {
249                                 pjdlog_error("Unknown resource %s.", argv[ii]);
250                                 if (ec == 0)
251                                         ec = EX_DATAERR;
252                                 continue;
253                         }
254                         ret = dump_one(res);
255                         if (ret != 0 && ec == 0)
256                                 ec = ret;
257                 }
258         }
259         exit(ec);
260 }
261
262 static int
263 control_set_role(struct nv *nv, const char *newrole)
264 {
265         const char *res, *oldrole;
266         unsigned int ii;
267         int error, ret;
268
269         ret = 0;
270
271         for (ii = 0; ; ii++) {
272                 res = nv_get_string(nv, "resource%u", ii);
273                 if (res == NULL)
274                         break;
275                 pjdlog_prefix_set("[%s] ", res);
276                 error = nv_get_int16(nv, "error%u", ii);
277                 if (error != 0) {
278                         if (ret == 0)
279                                 ret = error;
280                         pjdlog_warning("Received error %d from hastd.", error);
281                         continue;
282                 }
283                 oldrole = nv_get_string(nv, "role%u", ii);
284                 if (strcmp(oldrole, newrole) == 0)
285                         pjdlog_debug(2, "Role unchanged (%s).", oldrole);
286                 else {
287                         pjdlog_debug(1, "Role changed from %s to %s.", oldrole,
288                             newrole);
289                 }
290         }
291         pjdlog_prefix_set("%s", "");
292         return (ret);
293 }
294
295 static int
296 control_list(struct nv *nv)
297 {
298         pid_t pid;
299         unsigned int ii;
300         const char *str;
301         int error, ret;
302
303         ret = 0;
304
305         for (ii = 0; ; ii++) {
306                 str = nv_get_string(nv, "resource%u", ii);
307                 if (str == NULL)
308                         break;
309                 printf("%s:\n", str);
310                 error = nv_get_int16(nv, "error%u", ii);
311                 if (error != 0) {
312                         if (ret == 0)
313                                 ret = error;
314                         printf("  error: %d\n", error);
315                         continue;
316                 }
317                 printf("  role: %s\n", nv_get_string(nv, "role%u", ii));
318                 printf("  provname: %s\n",
319                     nv_get_string(nv, "provname%u", ii));
320                 printf("  localpath: %s\n",
321                     nv_get_string(nv, "localpath%u", ii));
322                 printf("  extentsize: %u (%NB)\n",
323                     (unsigned int)nv_get_uint32(nv, "extentsize%u", ii),
324                     (intmax_t)nv_get_uint32(nv, "extentsize%u", ii));
325                 printf("  keepdirty: %u\n",
326                     (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii));
327                 printf("  remoteaddr: %s\n",
328                     nv_get_string(nv, "remoteaddr%u", ii));
329                 str = nv_get_string(nv, "sourceaddr%u", ii);
330                 if (str != NULL)
331                         printf("  sourceaddr: %s\n", str);
332                 printf("  replication: %s\n",
333                     nv_get_string(nv, "replication%u", ii));
334                 str = nv_get_string(nv, "status%u", ii);
335                 if (str != NULL)
336                         printf("  status: %s\n", str);
337                 pid = nv_get_int32(nv, "workerpid%u", ii);
338                 if (pid != 0)
339                         printf("  workerpid: %d\n", pid);
340                 printf("  dirty: %ju (%NB)\n",
341                     (uintmax_t)nv_get_uint64(nv, "dirty%u", ii),
342                     (intmax_t)nv_get_uint64(nv, "dirty%u", ii));
343                 printf("  statistics:\n");
344                 printf("    reads: %ju\n",
345                     (uintmax_t)nv_get_uint64(nv, "stat_read%u", ii));
346                 printf("    writes: %ju\n",
347                     (uintmax_t)nv_get_uint64(nv, "stat_write%u", ii));
348                 printf("    deletes: %ju\n",
349                     (uintmax_t)nv_get_uint64(nv, "stat_delete%u", ii));
350                 printf("    flushes: %ju\n",
351                     (uintmax_t)nv_get_uint64(nv, "stat_flush%u", ii));
352                 printf("    activemap updates: %ju\n",
353                     (uintmax_t)nv_get_uint64(nv, "stat_activemap_update%u", ii));
354                 printf("    local errors: "
355                     "read: %ju, write: %ju, delete: %ju, flush: %ju\n",
356                     (uintmax_t)nv_get_uint64(nv, "stat_read_error%u", ii),
357                     (uintmax_t)nv_get_uint64(nv, "stat_write_error%u", ii),
358                     (uintmax_t)nv_get_uint64(nv, "stat_delete_error%u", ii),
359                     (uintmax_t)nv_get_uint64(nv, "stat_flush_error%u", ii));
360                 printf("    queues: "
361                     "local: %ju, send: %ju, recv: %ju, done: %ju, idle: %ju\n",
362                     (uintmax_t)nv_get_uint64(nv, "local_queue_size%u", ii),
363                     (uintmax_t)nv_get_uint64(nv, "send_queue_size%u", ii),
364                     (uintmax_t)nv_get_uint64(nv, "recv_queue_size%u", ii),
365                     (uintmax_t)nv_get_uint64(nv, "done_queue_size%u", ii),
366                     (uintmax_t)nv_get_uint64(nv, "idle_queue_size%u", ii));
367         }
368         return (ret);
369 }
370
371 static int
372 control_status(struct nv *nv)
373 {
374         unsigned int ii;
375         const char *str;
376         int error, hprinted, ret;
377
378         hprinted = 0;
379         ret = 0;
380
381         for (ii = 0; ; ii++) {
382                 str = nv_get_string(nv, "resource%u", ii);
383                 if (str == NULL)
384                         break;
385                 if (!hprinted) {
386                         printf("Name\tStatus\t Role\t\tComponents\n");
387                         hprinted = 1;
388                 }
389                 printf("%s\t", str);
390                 error = nv_get_int16(nv, "error%u", ii);
391                 if (error != 0) {
392                         if (ret == 0)
393                                 ret = error;
394                         printf("ERR%d\n", error);
395                         continue;
396                 }
397                 str = nv_get_string(nv, "status%u", ii);
398                 printf("%-9s", (str != NULL) ? str : "-");
399                 printf("%-15s", nv_get_string(nv, "role%u", ii));
400                 printf("%s\t",
401                     nv_get_string(nv, "localpath%u", ii));
402                 printf("%s\n",
403                     nv_get_string(nv, "remoteaddr%u", ii));
404         }
405         return (ret);
406 }
407
408 int
409 main(int argc, char *argv[])
410 {
411         struct nv *nv;
412         int64_t mediasize, extentsize, keepdirty;
413         int cmd, debug, error, ii;
414         const char *optstr;
415
416         debug = 0;
417         mediasize = extentsize = keepdirty = 0;
418
419         if (argc == 1)
420                 usage();
421
422         if (strcmp(argv[1], "create") == 0) {
423                 cmd = CMD_CREATE;
424                 optstr = "c:de:k:m:h";
425         } else if (strcmp(argv[1], "role") == 0) {
426                 cmd = CMD_ROLE;
427                 optstr = "c:dh";
428         } else if (strcmp(argv[1], "list") == 0) {
429                 cmd = CMD_LIST;
430                 optstr = "c:dh";
431         } else if (strcmp(argv[1], "status") == 0) {
432                 cmd = CMD_STATUS;
433                 optstr = "c:dh";
434         } else if (strcmp(argv[1], "dump") == 0) {
435                 cmd = CMD_DUMP;
436                 optstr = "c:dh";
437         } else
438                 usage();
439
440         argc--;
441         argv++;
442
443         for (;;) {
444                 int ch;
445
446                 ch = getopt(argc, argv, optstr);
447                 if (ch == -1)
448                         break;
449                 switch (ch) {
450                 case 'c':
451                         cfgpath = optarg;
452                         break;
453                 case 'd':
454                         debug++;
455                         break;
456                 case 'e':
457                         if (expand_number(optarg, &extentsize) == -1)
458                                 errx(EX_USAGE, "Invalid extentsize");
459                         break;
460                 case 'k':
461                         if (expand_number(optarg, &keepdirty) == -1)
462                                 errx(EX_USAGE, "Invalid keepdirty");
463                         break;
464                 case 'm':
465                         if (expand_number(optarg, &mediasize) == -1)
466                                 errx(EX_USAGE, "Invalid mediasize");
467                         break;
468                 case 'h':
469                 default:
470                         usage();
471                 }
472         }
473         argc -= optind;
474         argv += optind;
475
476         switch (cmd) {
477         case CMD_CREATE:
478         case CMD_ROLE:
479                 if (argc == 0)
480                         usage();
481                 break;
482         }
483
484         pjdlog_init(PJDLOG_MODE_STD);
485         pjdlog_debug_set(debug);
486
487         cfg = yy_config_parse(cfgpath, true);
488         PJDLOG_ASSERT(cfg != NULL);
489
490         switch (cmd) {
491         case CMD_CREATE:
492                 control_create(argc, argv, mediasize, extentsize, keepdirty);
493                 /* NOTREACHED */
494                 PJDLOG_ABORT("What are we doing here?!");
495                 break;
496         case CMD_DUMP:
497                 /* Dump metadata from local component of the given resource. */
498                 control_dump(argc, argv);
499                 /* NOTREACHED */
500                 PJDLOG_ABORT("What are we doing here?!");
501                 break;
502         case CMD_ROLE:
503                 /* Change role for the given resources. */
504                 if (argc < 2)
505                         usage();
506                 nv = nv_alloc();
507                 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd");
508                 if (strcmp(argv[0], "init") == 0)
509                         nv_add_uint8(nv, HAST_ROLE_INIT, "role");
510                 else if (strcmp(argv[0], "primary") == 0)
511                         nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role");
512                 else if (strcmp(argv[0], "secondary") == 0)
513                         nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role");
514                 else
515                         usage();
516                 for (ii = 0; ii < argc - 1; ii++)
517                         nv_add_string(nv, argv[ii + 1], "resource%d", ii);
518                 break;
519         case CMD_LIST:
520         case CMD_STATUS:
521                 /* Obtain status of the given resources. */
522                 nv = nv_alloc();
523                 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd");
524                 if (argc == 0)
525                         nv_add_string(nv, "all", "resource%d", 0);
526                 else {
527                         for (ii = 0; ii < argc; ii++)
528                                 nv_add_string(nv, argv[ii], "resource%d", ii);
529                 }
530                 break;
531         default:
532                 PJDLOG_ABORT("Impossible command!");
533         }
534
535         /* Setup control connection... */
536         if (proto_client(NULL, cfg->hc_controladdr, &controlconn) == -1) {
537                 pjdlog_exit(EX_OSERR,
538                     "Unable to setup control connection to %s",
539                     cfg->hc_controladdr);
540         }
541         /* ...and connect to hastd. */
542         if (proto_connect(controlconn, HAST_TIMEOUT) == -1) {
543                 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s",
544                     cfg->hc_controladdr);
545         }
546
547         if (drop_privs(NULL) != 0)
548                 exit(EX_CONFIG);
549
550         /* Send the command to the server... */
551         if (hast_proto_send(NULL, controlconn, nv, NULL, 0) == -1) {
552                 pjdlog_exit(EX_UNAVAILABLE,
553                     "Unable to send command to hastd via %s",
554                     cfg->hc_controladdr);
555         }
556         nv_free(nv);
557         /* ...and receive reply. */
558         if (hast_proto_recv_hdr(controlconn, &nv) == -1) {
559                 pjdlog_exit(EX_UNAVAILABLE,
560                     "cannot receive reply from hastd via %s",
561                     cfg->hc_controladdr);
562         }
563
564         error = nv_get_int16(nv, "error");
565         if (error != 0) {
566                 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.",
567                     error);
568         }
569         nv_set_error(nv, 0);
570
571         switch (cmd) {
572         case CMD_ROLE:
573                 error = control_set_role(nv, argv[0]);
574                 break;
575         case CMD_LIST:
576                 error = control_list(nv);
577                 break;
578         case CMD_STATUS:
579                 error = control_status(nv);
580                 break;
581         default:
582                 PJDLOG_ABORT("Impossible command!");
583         }
584
585         exit(error);
586 }