1 /*- 2 * Copyright (c) 2011, 2012, 2013, 2014, 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_event.cc 35 */ 36 #include <sys/cdefs.h> 37 #include <sys/time.h> 38 #include <sys/fs/zfs.h> 39 #include <sys/vdev_impl.h> 40 41 #include <syslog.h> 42 43 #include <libzfs.h> 44 /* 45 * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with 46 * C++ flush methods 47 */ 48 #undef flush 49 50 #include <list> 51 #include <map> 52 #include <sstream> 53 #include <string> 54 55 #include <devdctl/guid.h> 56 #include <devdctl/event.h> 57 #include <devdctl/event_factory.h> 58 #include <devdctl/exception.h> 59 #include <devdctl/consumer.h> 60 61 #include "callout.h" 62 #include "vdev_iterator.h" 63 #include "zfsd_event.h" 64 #include "case_file.h" 65 #include "vdev.h" 66 #include "zfsd.h" 67 #include "zfsd_exception.h" 68 #include "zpool_list.h" 69 70 __FBSDID("$FreeBSD$"); 71 /*============================ Namespace Control =============================*/ 72 using DevdCtl::Event; 73 using DevdCtl::Guid; 74 using DevdCtl::NVPairMap; 75 using std::stringstream; 76 77 /*=========================== Class Implementations ==========================*/ 78 79 /*-------------------------------- DevfsEvent --------------------------------*/ 80 81 //- DevfsEvent Static Public Methods ------------------------------------------- 82 Event * 83 DevfsEvent::Builder(Event::Type type, 84 NVPairMap &nvPairs, 85 const string &eventString) 86 { 87 return (new DevfsEvent(type, nvPairs, eventString)); 88 } 89 90 //- DevfsEvent Static Protected Methods ---------------------------------------- 91 nvlist_t * 92 DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) 93 { 94 pool_state_t poolState; 95 char *poolName; 96 boolean_t b_inuse; 97 int nlabels; 98 99 inUse = false; 100 degraded = false; 101 poolName = NULL; 102 if (zpool_in_use(g_zfsHandle, devFd, &poolState, 103 &poolName, &b_inuse) == 0) { 104 nvlist_t *devLabel = NULL; 105 106 inUse = b_inuse == B_TRUE; 107 if (poolName != NULL) 108 free(poolName); 109 110 nlabels = zpool_read_all_labels(devFd, &devLabel); 111 /* 112 * If we find a disk with fewer than the maximum number of 113 * labels, it might be the whole disk of a partitioned disk 114 * where ZFS resides on a partition. In that case, we should do 115 * nothing and wait for the partition to appear. Or, the disk 116 * might be damaged. In that case, zfsd should do nothing and 117 * wait for the sysadmin to decide. 118 */ 119 if (nlabels != VDEV_LABELS || devLabel == NULL) { 120 nvlist_free(devLabel); 121 return (NULL); 122 } 123 124 try { 125 Vdev vdev(devLabel); 126 degraded = vdev.State() != VDEV_STATE_HEALTHY; 127 return (devLabel); 128 } catch (ZfsdException &exp) { 129 string devName = fdevname(devFd); 130 string devPath = _PATH_DEV + devName; 131 string context("DevfsEvent::ReadLabel: " 132 + devPath + ": "); 133 134 exp.GetString().insert(0, context); 135 exp.Log(); 136 nvlist_free(devLabel); 137 } 138 } 139 return (NULL); 140 } 141 142 bool 143 DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, 144 nvlist_t *devConfig) 145 { 146 try { 147 /* 148 * A device with ZFS label information has been 149 * inserted. If it matches a device for which we 150 * have a case, see if we can solve that case. 151 */ 152 syslog(LOG_INFO, "Interrogating VDEV label for %s\n", 153 devPath.c_str()); 154 Vdev vdev(devConfig); 155 CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), 156 vdev.GUID())); 157 if (caseFile != NULL) 158 return (caseFile->ReEvaluate(devPath, physPath, &vdev)); 159 160 } catch (ZfsdException &exp) { 161 string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); 162 163 exp.GetString().insert(0, context); 164 exp.Log(); 165 } 166 return (false); 167 } 168 169 //- DevfsEvent Virtual Public Methods ------------------------------------------ 170 Event * 171 DevfsEvent::DeepCopy() const 172 { 173 return (new DevfsEvent(*this)); 174 } 175 176 bool 177 DevfsEvent::Process() const 178 { 179 /* 180 * We are only concerned with newly discovered 181 * devices that can be ZFS vdevs. 182 */ 183 if (Value("type") != "CREATE" || !IsDiskDev()) 184 return (false); 185 186 /* Log the event since it is of interest. */ 187 Log(LOG_INFO); 188 189 string devPath; 190 if (!DevPath(devPath)) 191 return (false); 192 193 int devFd(open(devPath.c_str(), O_RDONLY)); 194 if (devFd == -1) 195 return (false); 196 197 bool inUse; 198 bool degraded; 199 nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); 200 201 string physPath; 202 bool havePhysPath(PhysicalPath(physPath)); 203 204 string devName; 205 DevName(devName); 206 close(devFd); 207 208 if (inUse && devLabel != NULL) { 209 OnlineByLabel(devPath, physPath, devLabel); 210 } else if (degraded) { 211 syslog(LOG_INFO, "%s is marked degraded. Ignoring " 212 "as a replace by physical path candidate.\n", 213 devName.c_str()); 214 } else if (havePhysPath && IsWholeDev()) { 215 /* 216 * TODO: attempt to resolve events using every casefile 217 * that matches this physpath 218 */ 219 CaseFile *caseFile(CaseFile::Find(physPath)); 220 if (caseFile != NULL) { 221 syslog(LOG_INFO, 222 "Found CaseFile(%s:%s:%s) - ReEvaluating\n", 223 caseFile->PoolGUIDString().c_str(), 224 caseFile->VdevGUIDString().c_str(), 225 zpool_state_to_name(caseFile->VdevState(), 226 VDEV_AUX_NONE)); 227 caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); 228 } 229 } 230 if (devLabel != NULL) 231 nvlist_free(devLabel); 232 return (false); 233 } 234 235 //- DevfsEvent Protected Methods ----------------------------------------------- 236 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs, 237 const string &eventString) 238 : DevdCtl::DevfsEvent(type, nvpairs, eventString) 239 { 240 } 241 242 DevfsEvent::DevfsEvent(const DevfsEvent &src) 243 : DevdCtl::DevfsEvent::DevfsEvent(src) 244 { 245 } 246 247 /*-------------------------------- GeomEvent --------------------------------*/ 248 249 //- GeomEvent Static Public Methods ------------------------------------------- 250 Event * 251 GeomEvent::Builder(Event::Type type, 252 NVPairMap &nvPairs, 253 const string &eventString) 254 { 255 return (new GeomEvent(type, nvPairs, eventString)); 256 } 257 258 //- GeomEvent Virtual Public Methods ------------------------------------------ 259 Event * 260 GeomEvent::DeepCopy() const 261 { 262 return (new GeomEvent(*this)); 263 } 264 265 bool 266 GeomEvent::Process() const 267 { 268 /* 269 * We are only concerned with physical path changes, because those can 270 * be used to satisfy autoreplace operations 271 */ 272 if (Value("type") != "GEOM::physpath" || !IsDiskDev()) 273 return (false); 274 275 /* Log the event since it is of interest. */ 276 Log(LOG_INFO); 277 278 string devPath; 279 if (!DevPath(devPath)) 280 return (false); 281 282 string physPath; 283 bool havePhysPath(PhysicalPath(physPath)); 284 285 string devName; 286 DevName(devName); 287 288 if (havePhysPath) { 289 /* 290 * TODO: attempt to resolve events using every casefile 291 * that matches this physpath 292 */ 293 CaseFile *caseFile(CaseFile::Find(physPath)); 294 if (caseFile != NULL) { 295 syslog(LOG_INFO, 296 "Found CaseFile(%s:%s:%s) - ReEvaluating\n", 297 caseFile->PoolGUIDString().c_str(), 298 caseFile->VdevGUIDString().c_str(), 299 zpool_state_to_name(caseFile->VdevState(), 300 VDEV_AUX_NONE)); 301 caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); 302 } 303 } 304 return (false); 305 } 306 307 //- GeomEvent Protected Methods ----------------------------------------------- 308 GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs, 309 const string &eventString) 310 : DevdCtl::GeomEvent(type, nvpairs, eventString) 311 { 312 } 313 314 GeomEvent::GeomEvent(const GeomEvent &src) 315 : DevdCtl::GeomEvent::GeomEvent(src) 316 { 317 } 318 319 320 /*--------------------------------- ZfsEvent ---------------------------------*/ 321 //- ZfsEvent Static Public Methods --------------------------------------------- 322 DevdCtl::Event * 323 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, 324 const string &eventString) 325 { 326 return (new ZfsEvent(type, nvpairs, eventString)); 327 } 328 329 //- ZfsEvent Virtual Public Methods -------------------------------------------- 330 Event * 331 ZfsEvent::DeepCopy() const 332 { 333 return (new ZfsEvent(*this)); 334 } 335 336 bool 337 ZfsEvent::Process() const 338 { 339 string logstr(""); 340 341 if (!Contains("class") && !Contains("type")) { 342 syslog(LOG_ERR, 343 "ZfsEvent::Process: Missing class or type data."); 344 return (false); 345 } 346 347 /* On config syncs, replay any queued events first. */ 348 if (Value("type").find("misc.fs.zfs.config_sync") == 0) { 349 /* 350 * Even if saved events are unconsumed the second time 351 * around, drop them. Any events that still can't be 352 * consumed are probably referring to vdevs or pools that 353 * no longer exist. 354 */ 355 ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true); 356 CaseFile::ReEvaluateByGuid(PoolGUID(), *this); 357 } 358 359 if (Value("type").find("misc.fs.zfs.") == 0) { 360 /* Configuration changes, resilver events, etc. */ 361 ProcessPoolEvent(); 362 return (false); 363 } 364 365 if (!Contains("pool_guid") || !Contains("vdev_guid")) { 366 /* Only currently interested in Vdev related events. */ 367 return (false); 368 } 369 370 CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); 371 if (caseFile != NULL) { 372 Log(LOG_INFO); 373 syslog(LOG_INFO, "Evaluating existing case file\n"); 374 caseFile->ReEvaluate(*this); 375 return (false); 376 } 377 378 /* Skip events that can't be handled. */ 379 Guid poolGUID(PoolGUID()); 380 /* If there are no replicas for a pool, then it's not manageable. */ 381 if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { 382 stringstream msg; 383 msg << "No replicas available for pool " << poolGUID; 384 msg << ", ignoring"; 385 Log(LOG_INFO); 386 syslog(LOG_INFO, "%s", msg.str().c_str()); 387 return (false); 388 } 389 390 /* 391 * Create a case file for this vdev, and have it 392 * evaluate the event. 393 */ 394 ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); 395 if (zpl.empty()) { 396 stringstream msg; 397 int priority = LOG_INFO; 398 msg << "ZfsEvent::Process: Event for unknown pool "; 399 msg << poolGUID << " "; 400 msg << "queued"; 401 Log(LOG_INFO); 402 syslog(priority, "%s", msg.str().c_str()); 403 return (true); 404 } 405 406 nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); 407 if (vdevConfig == NULL) { 408 stringstream msg; 409 int priority = LOG_INFO; 410 msg << "ZfsEvent::Process: Event for unknown vdev "; 411 msg << VdevGUID() << " "; 412 msg << "queued"; 413 Log(LOG_INFO); 414 syslog(priority, "%s", msg.str().c_str()); 415 return (true); 416 } 417 418 Vdev vdev(zpl.front(), vdevConfig); 419 caseFile = &CaseFile::Create(vdev); 420 if (caseFile->ReEvaluate(*this) == false) { 421 stringstream msg; 422 int priority = LOG_INFO; 423 msg << "ZfsEvent::Process: Unconsumed event for vdev("; 424 msg << zpool_get_name(zpl.front()) << ","; 425 msg << vdev.GUID() << ") "; 426 msg << "queued"; 427 Log(LOG_INFO); 428 syslog(priority, "%s", msg.str().c_str()); 429 return (true); 430 } 431 return (false); 432 } 433 434 //- ZfsEvent Protected Methods ------------------------------------------------- 435 ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, 436 const string &eventString) 437 : DevdCtl::ZfsEvent(type, nvpairs, eventString) 438 { 439 } 440 441 ZfsEvent::ZfsEvent(const ZfsEvent &src) 442 : DevdCtl::ZfsEvent(src) 443 { 444 } 445 446 /* 447 * Sometimes the kernel won't detach a spare when it is no longer needed. This 448 * can happen for example if a drive is removed, then either the pool is 449 * exported or the machine is powered off, then the drive is reinserted, then 450 * the machine is powered on or the pool is imported. ZFSD must detach these 451 * spares itself. 452 */ 453 void 454 ZfsEvent::CleanupSpares() const 455 { 456 Guid poolGUID(PoolGUID()); 457 ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); 458 if (!zpl.empty()) { 459 zpool_handle_t* hdl; 460 461 hdl = zpl.front(); 462 VdevIterator(hdl).Each(TryDetach, (void*)hdl); 463 } 464 } 465 466 void 467 ZfsEvent::ProcessPoolEvent() const 468 { 469 bool degradedDevice(false); 470 471 /* The pool is destroyed. Discard any open cases */ 472 if (Value("type") == "misc.fs.zfs.pool_destroy") { 473 Log(LOG_INFO); 474 CaseFile::ReEvaluateByGuid(PoolGUID(), *this); 475 return; 476 } 477 478 CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); 479 if (caseFile != NULL) { 480 if (caseFile->VdevState() != VDEV_STATE_UNKNOWN 481 && caseFile->VdevState() < VDEV_STATE_HEALTHY) 482 degradedDevice = true; 483 484 Log(LOG_INFO); 485 caseFile->ReEvaluate(*this); 486 } 487 else if (Value("type") == "misc.fs.zfs.resilver_finish") 488 { 489 /* 490 * It's possible to get a resilver_finish event with no 491 * corresponding casefile. For example, if a damaged pool were 492 * exported, repaired, then reimported. 493 */ 494 Log(LOG_INFO); 495 CleanupSpares(); 496 } 497 498 if (Value("type") == "misc.fs.zfs.vdev_remove" 499 && degradedDevice == false) { 500 501 /* See if any other cases can make use of this device. */ 502 Log(LOG_INFO); 503 ZfsDaemon::RequestSystemRescan(); 504 } 505 } 506 507 bool 508 ZfsEvent::TryDetach(Vdev &vdev, void *cbArg) 509 { 510 /* 511 * Outline: 512 * if this device is a spare, and its parent includes one healthy, 513 * non-spare child, then detach this device. 514 */ 515 zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg)); 516 517 if (vdev.IsSpare()) { 518 std::list<Vdev> siblings; 519 std::list<Vdev>::iterator siblings_it; 520 boolean_t cleanup = B_FALSE; 521 522 Vdev parent = vdev.Parent(); 523 siblings = parent.Children(); 524 525 /* Determine whether the parent should be cleaned up */ 526 for (siblings_it = siblings.begin(); 527 siblings_it != siblings.end(); 528 siblings_it++) { 529 Vdev sibling = *siblings_it; 530 531 if (!sibling.IsSpare() && 532 sibling.State() == VDEV_STATE_HEALTHY) { 533 cleanup = B_TRUE; 534 break; 535 } 536 } 537 538 if (cleanup) { 539 syslog(LOG_INFO, "Detaching spare vdev %s from pool %s", 540 vdev.Path().c_str(), zpool_get_name(hdl)); 541 zpool_vdev_detach(hdl, vdev.Path().c_str()); 542 } 543 544 } 545 546 /* Always return false, because there may be other spares to detach */ 547 return (false); 548 } 549