Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/cddl/usr.sbin/zfsd/zfsd.cc
107607 views
1
/*-
2
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Spectra Logic Corporation
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions, and the following disclaimer,
10
* without modification.
11
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
12
* substantially similar to the "NO WARRANTY" disclaimer below
13
* ("Disclaimer") and any redistribution must be conditioned upon
14
* including a substantially similar Disclaimer requirement for further
15
* binary redistribution.
16
*
17
* NO WARRANTY
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
* POSSIBILITY OF SUCH DAMAGES.
29
*
30
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
31
*/
32
33
/**
34
* \file zfsd.cc
35
*
36
* The ZFS daemon consumes kernel devdctl(4) event data via devd(8)'s
37
* unix domain socket in order to react to system changes that impact
38
* the function of ZFS storage pools. The goal of this daemon is to
39
* provide similar functionality to the Solaris ZFS Diagnostic Engine
40
* (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and
41
* the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler).
42
*/
43
44
#include <sys/cdefs.h>
45
#include <sys/byteorder.h>
46
#include <sys/param.h>
47
#include <sys/fs/zfs.h>
48
49
#include <err.h>
50
#include <fcntl.h>
51
#include <libgeom.h>
52
#include <libutil.h>
53
#include <poll.h>
54
#include <syslog.h>
55
56
#include <libzfs.h>
57
58
#include <list>
59
#include <map>
60
#include <string>
61
62
#include <devdctl/guid.h>
63
#include <devdctl/event.h>
64
#include <devdctl/event_factory.h>
65
#include <devdctl/exception.h>
66
#include <devdctl/consumer.h>
67
68
#include "callout.h"
69
#include "vdev_iterator.h"
70
#include "zfsd_event.h"
71
#include "case_file.h"
72
#include "vdev.h"
73
#include "vdev_iterator.h"
74
#include "zfsd.h"
75
#include "zfsd_exception.h"
76
#include "zpool_list.h"
77
/*================================== Macros ==================================*/
78
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
79
80
/*============================ Namespace Control =============================*/
81
using DevdCtl::Event;
82
using DevdCtl::EventFactory;
83
using DevdCtl::EventList;
84
85
/*================================ Global Data ===============================*/
86
int g_debug = 0;
87
libzfs_handle_t *g_zfsHandle;
88
89
/*--------------------------------- ZfsDaemon --------------------------------*/
90
//- ZfsDaemon Static Private Data ----------------------------------------------
91
ZfsDaemon *ZfsDaemon::s_theZfsDaemon;
92
bool ZfsDaemon::s_logCaseFiles;
93
bool ZfsDaemon::s_terminateEventLoop;
94
char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid";
95
pidfh *ZfsDaemon::s_pidFH;
96
int ZfsDaemon::s_signalPipeFD[2];
97
bool ZfsDaemon::s_systemRescanRequested(false);
98
EventFactory::Record ZfsDaemon::s_registryEntries[] =
99
{
100
{ Event::NOTIFY, "GEOM", &GeomEvent::Builder },
101
{ Event::NOTIFY, "ZFS", &ZfsEvent::Builder }
102
};
103
104
//- ZfsDaemon Static Public Methods --------------------------------------------
105
ZfsDaemon &
106
ZfsDaemon::Get()
107
{
108
return (*s_theZfsDaemon);
109
}
110
111
void
112
ZfsDaemon::WakeEventLoop()
113
{
114
write(s_signalPipeFD[1], "+", 1);
115
}
116
117
void
118
ZfsDaemon::RequestSystemRescan()
119
{
120
s_systemRescanRequested = true;
121
ZfsDaemon::WakeEventLoop();
122
}
123
124
void
125
ZfsDaemon::Run()
126
{
127
ZfsDaemon daemon;
128
129
while (s_terminateEventLoop == false) {
130
131
try {
132
daemon.DisconnectFromDevd();
133
134
if (daemon.ConnectToDevd() == false) {
135
sleep(30);
136
continue;
137
}
138
139
daemon.DetectMissedEvents();
140
141
daemon.EventLoop();
142
143
} catch (const DevdCtl::Exception &exp) {
144
exp.Log();
145
}
146
}
147
148
daemon.DisconnectFromDevd();
149
}
150
151
//- ZfsDaemon Private Methods --------------------------------------------------
152
ZfsDaemon::ZfsDaemon()
153
: Consumer(/*defBuilder*/NULL, s_registryEntries,
154
NUM_ELEMENTS(s_registryEntries))
155
{
156
if (s_theZfsDaemon != NULL)
157
errx(1, "Multiple ZfsDaemon instances created. Exiting");
158
159
s_theZfsDaemon = this;
160
161
if (pipe(s_signalPipeFD) != 0)
162
errx(1, "Unable to allocate signal pipe. Exiting");
163
164
if (fcntl(s_signalPipeFD[0], F_SETFL, O_NONBLOCK) == -1)
165
errx(1, "Unable to set pipe as non-blocking. Exiting");
166
167
if (fcntl(s_signalPipeFD[1], F_SETFL, O_NONBLOCK) == -1)
168
errx(1, "Unable to set pipe as non-blocking. Exiting");
169
170
signal(SIGHUP, ZfsDaemon::RescanSignalHandler);
171
signal(SIGINFO, ZfsDaemon::InfoSignalHandler);
172
signal(SIGINT, ZfsDaemon::QuitSignalHandler);
173
signal(SIGTERM, ZfsDaemon::QuitSignalHandler);
174
signal(SIGUSR1, ZfsDaemon::RescanSignalHandler);
175
176
g_zfsHandle = libzfs_init();
177
if (g_zfsHandle == NULL)
178
errx(1, "Unable to initialize ZFS library. Exiting");
179
180
Callout::Init();
181
InitializeSyslog();
182
OpenPIDFile();
183
184
if (g_debug == 0)
185
daemon(0, 0);
186
187
UpdatePIDFile();
188
}
189
190
ZfsDaemon::~ZfsDaemon()
191
{
192
PurgeCaseFiles();
193
ClosePIDFile();
194
}
195
196
void
197
ZfsDaemon::PurgeCaseFiles()
198
{
199
CaseFile::PurgeAll();
200
}
201
202
bool
203
ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg)
204
{
205
if (vdev.State() != VDEV_STATE_HEALTHY)
206
CaseFile::Create(vdev);
207
208
return (/*break early*/false);
209
}
210
211
void
212
ZfsDaemon::BuildCaseFiles()
213
{
214
ZpoolList zpl;
215
ZpoolList::iterator pool;
216
217
/* Add CaseFiles for vdevs with issues. */
218
for (pool = zpl.begin(); pool != zpl.end(); pool++)
219
VdevIterator(*pool).Each(VdevAddCaseFile, NULL);
220
221
/* De-serialize any saved cases. */
222
CaseFile::DeSerialize();
223
224
/* Simulate config_sync events to force CaseFile reevaluation */
225
for (pool = zpl.begin(); pool != zpl.end(); pool++) {
226
char evString[160];
227
Event *event;
228
nvlist_t *config;
229
uint64_t poolGUID;
230
const char *poolname;
231
232
poolname = zpool_get_name(*pool);
233
config = zpool_get_config(*pool, NULL);
234
if (config == NULL) {
235
syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not "
236
"find pool config for pool %s", poolname);
237
continue;
238
}
239
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
240
&poolGUID) != 0) {
241
syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not "
242
"find pool guid for pool %s", poolname);
243
continue;
244
}
245
246
247
snprintf(evString, 160, "!system=ZFS subsystem=ZFS "
248
"type=sysevent.fs.zfs.config_sync sub_type=synthesized "
249
"pool_name=%s pool_guid=%" PRIu64 "\n", poolname, poolGUID);
250
event = Event::CreateEvent(GetFactory(), string(evString));
251
if (event != NULL) {
252
event->Process();
253
delete event;
254
}
255
}
256
}
257
258
void
259
ZfsDaemon::RescanSystem()
260
{
261
struct gmesh mesh;
262
struct gclass *mp;
263
struct ggeom *gp;
264
struct gprovider *pp;
265
int result;
266
267
/*
268
* The devdctl system doesn't replay events for new consumers
269
* of the interface. Emit manufactured DEVFS arrival events
270
* for any devices that already before we started or during
271
* periods where we've lost our connection to devd.
272
*/
273
result = geom_gettree(&mesh);
274
if (result != 0) {
275
syslog(LOG_ERR, "ZfsDaemon::RescanSystem: "
276
"geom_gettree failed with error %d\n", result);
277
return;
278
}
279
280
const string evStart("!system=DEVFS subsystem=CDEV type=CREATE "
281
"sub_type=synthesized cdev=");
282
LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
283
LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
284
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
285
Event *event;
286
287
string evString(evStart + pp->lg_name + "\n");
288
event = Event::CreateEvent(GetFactory(),
289
evString);
290
if (event != NULL) {
291
if (event->Process())
292
SaveEvent(*event);
293
delete event;
294
}
295
}
296
}
297
}
298
geom_deletetree(&mesh);
299
}
300
301
void
302
ZfsDaemon::DetectMissedEvents()
303
{
304
do {
305
PurgeCaseFiles();
306
307
/*
308
* Discard any events waiting for us. We don't know
309
* if they still apply to the current state of the
310
* system.
311
*/
312
FlushEvents();
313
314
BuildCaseFiles();
315
316
/*
317
* If the system state has changed during our
318
* interrogation, start over.
319
*/
320
} while (s_terminateEventLoop == false && EventsPending());
321
322
RescanSystem();
323
}
324
325
void
326
ZfsDaemon::EventLoop()
327
{
328
while (s_terminateEventLoop == false) {
329
struct pollfd fds[2];
330
int result;
331
332
if (s_logCaseFiles == true) {
333
EventList::iterator event(m_unconsumedEvents.begin());
334
s_logCaseFiles = false;
335
CaseFile::LogAll();
336
while (event != m_unconsumedEvents.end())
337
(*event++)->Log(LOG_INFO);
338
}
339
340
Callout::ExpireCallouts();
341
342
/* Wait for data. */
343
fds[0].fd = m_devdSockFD;
344
fds[0].events = POLLIN;
345
fds[0].revents = 0;
346
fds[1].fd = s_signalPipeFD[0];
347
fds[1].events = POLLIN;
348
fds[1].revents = 0;
349
result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM);
350
if (result == -1) {
351
if (errno == EINTR)
352
continue;
353
else
354
err(1, "Polling for devd events failed");
355
} else if (result == 0) {
356
errx(1, "Unexpected result of 0 from poll. Exiting");
357
}
358
359
if ((fds[0].revents & POLLIN) != 0)
360
ProcessEvents();
361
362
if ((fds[1].revents & POLLIN) != 0) {
363
static char discardBuf[128];
364
365
/*
366
* This pipe exists just to close the signal
367
* race. Its contents are of no interest to
368
* us, but we must ensure that future signals
369
* have space in the pipe to write.
370
*/
371
while (read(s_signalPipeFD[0], discardBuf,
372
sizeof(discardBuf)) > 0)
373
;
374
}
375
376
if (s_systemRescanRequested == true) {
377
s_systemRescanRequested = false;
378
syslog(LOG_INFO, "System Rescan request processed.");
379
RescanSystem();
380
}
381
382
if ((fds[0].revents & POLLERR) != 0) {
383
syslog(LOG_INFO, "POLLERROR detected on devd socket.");
384
break;
385
}
386
387
if ((fds[0].revents & POLLHUP) != 0) {
388
syslog(LOG_INFO, "POLLHUP detected on devd socket.");
389
break;
390
}
391
}
392
}
393
//- ZfsDaemon staic Private Methods --------------------------------------------
394
void
395
ZfsDaemon::InfoSignalHandler(int)
396
{
397
s_logCaseFiles = true;
398
ZfsDaemon::WakeEventLoop();
399
}
400
401
void
402
ZfsDaemon::RescanSignalHandler(int)
403
{
404
RequestSystemRescan();
405
}
406
407
void
408
ZfsDaemon::QuitSignalHandler(int)
409
{
410
s_terminateEventLoop = true;
411
ZfsDaemon::WakeEventLoop();
412
}
413
414
void
415
ZfsDaemon::OpenPIDFile()
416
{
417
pid_t otherPID;
418
419
s_pidFH = pidfile_open(s_pidFilePath, 0600, &otherPID);
420
if (s_pidFH == NULL) {
421
if (errno == EEXIST)
422
errx(1, "already running as PID %d. Exiting", otherPID);
423
warn("cannot open PID file");
424
}
425
}
426
427
void
428
ZfsDaemon::UpdatePIDFile()
429
{
430
if (s_pidFH != NULL)
431
pidfile_write(s_pidFH);
432
}
433
434
void
435
ZfsDaemon::ClosePIDFile()
436
{
437
if (s_pidFH != NULL)
438
pidfile_remove(s_pidFH);
439
}
440
441
void
442
ZfsDaemon::InitializeSyslog()
443
{
444
openlog("zfsd", LOG_NDELAY, LOG_DAEMON);
445
}
446
447
448