1300906Sasomers/*- 2300906Sasomers * Copyright (c) 2011, 2012, 2013, 2014, 2016 Spectra Logic Corporation 3300906Sasomers * All rights reserved. 4300906Sasomers * 5300906Sasomers * Redistribution and use in source and binary forms, with or without 6300906Sasomers * modification, are permitted provided that the following conditions 7300906Sasomers * are met: 8300906Sasomers * 1. Redistributions of source code must retain the above copyright 9300906Sasomers * notice, this list of conditions, and the following disclaimer, 10300906Sasomers * without modification. 11300906Sasomers * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12300906Sasomers * substantially similar to the "NO WARRANTY" disclaimer below 13300906Sasomers * ("Disclaimer") and any redistribution must be conditioned upon 14300906Sasomers * including a substantially similar Disclaimer requirement for further 15300906Sasomers * binary redistribution. 16300906Sasomers * 17300906Sasomers * NO WARRANTY 18300906Sasomers * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19300906Sasomers * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20300906Sasomers * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21300906Sasomers * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22300906Sasomers * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23300906Sasomers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24300906Sasomers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25300906Sasomers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26300906Sasomers * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27300906Sasomers * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28300906Sasomers * POSSIBILITY OF SUCH DAMAGES. 29300906Sasomers * 30300906Sasomers * Authors: Justin T. Gibbs (Spectra Logic Corporation) 31300906Sasomers */ 32300906Sasomers 33300906Sasomers/** 34300906Sasomers * \file zfsd_event.cc 35300906Sasomers */ 36300906Sasomers#include <sys/cdefs.h> 37300906Sasomers#include <sys/time.h> 38300906Sasomers#include <sys/fs/zfs.h> 39326298Sasomers#include <sys/vdev_impl.h> 40300906Sasomers 41300906Sasomers#include <syslog.h> 42300906Sasomers 43300906Sasomers#include <libzfs.h> 44300906Sasomers/* 45300906Sasomers * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with 46300906Sasomers * C++ flush methods 47300906Sasomers */ 48300906Sasomers#undef flush 49300906Sasomers 50300906Sasomers#include <list> 51300906Sasomers#include <map> 52300906Sasomers#include <sstream> 53300906Sasomers#include <string> 54300906Sasomers 55300906Sasomers#include <devdctl/guid.h> 56300906Sasomers#include <devdctl/event.h> 57300906Sasomers#include <devdctl/event_factory.h> 58300906Sasomers#include <devdctl/exception.h> 59300906Sasomers#include <devdctl/consumer.h> 60300906Sasomers 61300906Sasomers#include "callout.h" 62300906Sasomers#include "vdev_iterator.h" 63300906Sasomers#include "zfsd_event.h" 64300906Sasomers#include "case_file.h" 65300906Sasomers#include "vdev.h" 66300906Sasomers#include "zfsd.h" 67300906Sasomers#include "zfsd_exception.h" 68300906Sasomers#include "zpool_list.h" 69300906Sasomers 70300906Sasomers__FBSDID("$FreeBSD: stable/11/cddl/usr.sbin/zfsd/zfsd_event.cc 330733 2018-03-10 03:34:27Z asomers $"); 71300906Sasomers/*============================ Namespace Control =============================*/ 72300906Sasomersusing DevdCtl::Event; 73300906Sasomersusing DevdCtl::Guid; 74300906Sasomersusing DevdCtl::NVPairMap; 75300906Sasomersusing std::stringstream; 76300906Sasomers 77300906Sasomers/*=========================== Class Implementations ==========================*/ 78300906Sasomers 79330733Sasomers/*-------------------------------- GeomEvent --------------------------------*/ 80300906Sasomers 81330733Sasomers//- GeomEvent Static Public Methods ------------------------------------------- 82300906SasomersEvent * 83330733SasomersGeomEvent::Builder(Event::Type type, 84330733Sasomers NVPairMap &nvPairs, 85330733Sasomers const string &eventString) 86300906Sasomers{ 87330733Sasomers return (new GeomEvent(type, nvPairs, eventString)); 88300906Sasomers} 89300906Sasomers 90330733Sasomers//- GeomEvent Virtual Public Methods ------------------------------------------ 91330733SasomersEvent * 92330733SasomersGeomEvent::DeepCopy() const 93330733Sasomers{ 94330733Sasomers return (new GeomEvent(*this)); 95330733Sasomers} 96330733Sasomers 97330733Sasomersbool 98330733SasomersGeomEvent::Process() const 99330733Sasomers{ 100330733Sasomers /* 101330733Sasomers * We only use GEOM events to repair damaged pools. So return early if 102330733Sasomers * there are no damaged pools 103330733Sasomers */ 104330733Sasomers if (CaseFile::Empty()) 105330733Sasomers return (false); 106330733Sasomers 107330733Sasomers /* 108330733Sasomers * We are only concerned with arrivals and physical path changes, 109330733Sasomers * because those can be used to satisfy online and autoreplace 110330733Sasomers * operations 111330733Sasomers */ 112330733Sasomers if (Value("type") != "GEOM::physpath" && Value("type") != "CREATE") 113330733Sasomers return (false); 114330733Sasomers 115330733Sasomers /* Log the event since it is of interest. */ 116330733Sasomers Log(LOG_INFO); 117330733Sasomers 118330733Sasomers string devPath; 119330733Sasomers if (!DevPath(devPath)) 120330733Sasomers return (false); 121330733Sasomers 122330733Sasomers int devFd(open(devPath.c_str(), O_RDONLY)); 123330733Sasomers if (devFd == -1) 124330733Sasomers return (false); 125330733Sasomers 126330733Sasomers bool inUse; 127330733Sasomers bool degraded; 128330733Sasomers nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); 129330733Sasomers 130330733Sasomers string physPath; 131330733Sasomers bool havePhysPath(PhysicalPath(physPath)); 132330733Sasomers 133330733Sasomers string devName; 134330733Sasomers DevName(devName); 135330733Sasomers close(devFd); 136330733Sasomers 137330733Sasomers if (inUse && devLabel != NULL) { 138330733Sasomers OnlineByLabel(devPath, physPath, devLabel); 139330733Sasomers } else if (degraded) { 140330733Sasomers syslog(LOG_INFO, "%s is marked degraded. Ignoring " 141330733Sasomers "as a replace by physical path candidate.\n", 142330733Sasomers devName.c_str()); 143330733Sasomers } else if (havePhysPath) { 144330733Sasomers /* 145330733Sasomers * TODO: attempt to resolve events using every casefile 146330733Sasomers * that matches this physpath 147330733Sasomers */ 148330733Sasomers CaseFile *caseFile(CaseFile::Find(physPath)); 149330733Sasomers if (caseFile != NULL) { 150330733Sasomers syslog(LOG_INFO, 151330733Sasomers "Found CaseFile(%s:%s:%s) - ReEvaluating\n", 152330733Sasomers caseFile->PoolGUIDString().c_str(), 153330733Sasomers caseFile->VdevGUIDString().c_str(), 154330733Sasomers zpool_state_to_name(caseFile->VdevState(), 155330733Sasomers VDEV_AUX_NONE)); 156330733Sasomers caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); 157330733Sasomers } 158330733Sasomers } 159330733Sasomers return (false); 160330733Sasomers} 161330733Sasomers 162330733Sasomers//- GeomEvent Protected Methods ----------------------------------------------- 163330733SasomersGeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs, 164330733Sasomers const string &eventString) 165330733Sasomers : DevdCtl::GeomEvent(type, nvpairs, eventString) 166330733Sasomers{ 167330733Sasomers} 168330733Sasomers 169330733SasomersGeomEvent::GeomEvent(const GeomEvent &src) 170330733Sasomers : DevdCtl::GeomEvent::GeomEvent(src) 171330733Sasomers{ 172330733Sasomers} 173330733Sasomers 174300906Sasomersnvlist_t * 175330733SasomersGeomEvent::ReadLabel(int devFd, bool &inUse, bool °raded) 176300906Sasomers{ 177300906Sasomers pool_state_t poolState; 178300906Sasomers char *poolName; 179300906Sasomers boolean_t b_inuse; 180326298Sasomers int nlabels; 181300906Sasomers 182300906Sasomers inUse = false; 183300906Sasomers degraded = false; 184300906Sasomers poolName = NULL; 185300906Sasomers if (zpool_in_use(g_zfsHandle, devFd, &poolState, 186300906Sasomers &poolName, &b_inuse) == 0) { 187326298Sasomers nvlist_t *devLabel = NULL; 188300906Sasomers 189300906Sasomers inUse = b_inuse == B_TRUE; 190300906Sasomers if (poolName != NULL) 191300906Sasomers free(poolName); 192300906Sasomers 193326298Sasomers nlabels = zpool_read_all_labels(devFd, &devLabel); 194326298Sasomers /* 195326298Sasomers * If we find a disk with fewer than the maximum number of 196326298Sasomers * labels, it might be the whole disk of a partitioned disk 197326298Sasomers * where ZFS resides on a partition. In that case, we should do 198326298Sasomers * nothing and wait for the partition to appear. Or, the disk 199326298Sasomers * might be damaged. In that case, zfsd should do nothing and 200326298Sasomers * wait for the sysadmin to decide. 201326298Sasomers */ 202326298Sasomers if (nlabels != VDEV_LABELS || devLabel == NULL) { 203326298Sasomers nvlist_free(devLabel); 204300906Sasomers return (NULL); 205326298Sasomers } 206300906Sasomers 207300906Sasomers try { 208300906Sasomers Vdev vdev(devLabel); 209300906Sasomers degraded = vdev.State() != VDEV_STATE_HEALTHY; 210300906Sasomers return (devLabel); 211300906Sasomers } catch (ZfsdException &exp) { 212300906Sasomers string devName = fdevname(devFd); 213300906Sasomers string devPath = _PATH_DEV + devName; 214330733Sasomers string context("GeomEvent::ReadLabel: " 215300906Sasomers + devPath + ": "); 216300906Sasomers 217300906Sasomers exp.GetString().insert(0, context); 218300906Sasomers exp.Log(); 219326298Sasomers nvlist_free(devLabel); 220300906Sasomers } 221300906Sasomers } 222300906Sasomers return (NULL); 223300906Sasomers} 224300906Sasomers 225300906Sasomersbool 226330733SasomersGeomEvent::OnlineByLabel(const string &devPath, const string& physPath, 227300906Sasomers nvlist_t *devConfig) 228300906Sasomers{ 229300906Sasomers try { 230300906Sasomers /* 231300906Sasomers * A device with ZFS label information has been 232300906Sasomers * inserted. If it matches a device for which we 233300906Sasomers * have a case, see if we can solve that case. 234300906Sasomers */ 235300906Sasomers syslog(LOG_INFO, "Interrogating VDEV label for %s\n", 236300906Sasomers devPath.c_str()); 237300906Sasomers Vdev vdev(devConfig); 238300906Sasomers CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), 239300906Sasomers vdev.GUID())); 240300906Sasomers if (caseFile != NULL) 241300906Sasomers return (caseFile->ReEvaluate(devPath, physPath, &vdev)); 242300906Sasomers 243300906Sasomers } catch (ZfsdException &exp) { 244330733Sasomers string context("GeomEvent::OnlineByLabel: " + devPath + ": "); 245300906Sasomers 246300906Sasomers exp.GetString().insert(0, context); 247300906Sasomers exp.Log(); 248300906Sasomers } 249300906Sasomers return (false); 250300906Sasomers} 251300906Sasomers 252300906Sasomers 253300906Sasomers/*--------------------------------- ZfsEvent ---------------------------------*/ 254300906Sasomers//- ZfsEvent Static Public Methods --------------------------------------------- 255300906SasomersDevdCtl::Event * 256300906SasomersZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, 257300906Sasomers const string &eventString) 258300906Sasomers{ 259300906Sasomers return (new ZfsEvent(type, nvpairs, eventString)); 260300906Sasomers} 261300906Sasomers 262300906Sasomers//- ZfsEvent Virtual Public Methods -------------------------------------------- 263300906SasomersEvent * 264300906SasomersZfsEvent::DeepCopy() const 265300906Sasomers{ 266300906Sasomers return (new ZfsEvent(*this)); 267300906Sasomers} 268300906Sasomers 269300906Sasomersbool 270300906SasomersZfsEvent::Process() const 271300906Sasomers{ 272300906Sasomers string logstr(""); 273300906Sasomers 274300906Sasomers if (!Contains("class") && !Contains("type")) { 275300906Sasomers syslog(LOG_ERR, 276300906Sasomers "ZfsEvent::Process: Missing class or type data."); 277300906Sasomers return (false); 278300906Sasomers } 279300906Sasomers 280300906Sasomers /* On config syncs, replay any queued events first. */ 281300906Sasomers if (Value("type").find("misc.fs.zfs.config_sync") == 0) { 282300906Sasomers /* 283300906Sasomers * Even if saved events are unconsumed the second time 284300906Sasomers * around, drop them. Any events that still can't be 285300906Sasomers * consumed are probably referring to vdevs or pools that 286300906Sasomers * no longer exist. 287300906Sasomers */ 288300906Sasomers ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true); 289300906Sasomers CaseFile::ReEvaluateByGuid(PoolGUID(), *this); 290300906Sasomers } 291300906Sasomers 292300906Sasomers if (Value("type").find("misc.fs.zfs.") == 0) { 293300906Sasomers /* Configuration changes, resilver events, etc. */ 294300906Sasomers ProcessPoolEvent(); 295300906Sasomers return (false); 296300906Sasomers } 297300906Sasomers 298300906Sasomers if (!Contains("pool_guid") || !Contains("vdev_guid")) { 299300906Sasomers /* Only currently interested in Vdev related events. */ 300300906Sasomers return (false); 301300906Sasomers } 302300906Sasomers 303300906Sasomers CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); 304300906Sasomers if (caseFile != NULL) { 305300906Sasomers Log(LOG_INFO); 306300906Sasomers syslog(LOG_INFO, "Evaluating existing case file\n"); 307300906Sasomers caseFile->ReEvaluate(*this); 308300906Sasomers return (false); 309300906Sasomers } 310300906Sasomers 311300906Sasomers /* Skip events that can't be handled. */ 312300906Sasomers Guid poolGUID(PoolGUID()); 313300906Sasomers /* If there are no replicas for a pool, then it's not manageable. */ 314300906Sasomers if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { 315300906Sasomers stringstream msg; 316300906Sasomers msg << "No replicas available for pool " << poolGUID; 317300906Sasomers msg << ", ignoring"; 318300906Sasomers Log(LOG_INFO); 319300906Sasomers syslog(LOG_INFO, "%s", msg.str().c_str()); 320300906Sasomers return (false); 321300906Sasomers } 322300906Sasomers 323300906Sasomers /* 324300906Sasomers * Create a case file for this vdev, and have it 325300906Sasomers * evaluate the event. 326300906Sasomers */ 327300906Sasomers ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); 328300906Sasomers if (zpl.empty()) { 329300906Sasomers stringstream msg; 330300906Sasomers int priority = LOG_INFO; 331300906Sasomers msg << "ZfsEvent::Process: Event for unknown pool "; 332300906Sasomers msg << poolGUID << " "; 333300906Sasomers msg << "queued"; 334300906Sasomers Log(LOG_INFO); 335300906Sasomers syslog(priority, "%s", msg.str().c_str()); 336300906Sasomers return (true); 337300906Sasomers } 338300906Sasomers 339300906Sasomers nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); 340300906Sasomers if (vdevConfig == NULL) { 341300906Sasomers stringstream msg; 342300906Sasomers int priority = LOG_INFO; 343300906Sasomers msg << "ZfsEvent::Process: Event for unknown vdev "; 344300906Sasomers msg << VdevGUID() << " "; 345300906Sasomers msg << "queued"; 346300906Sasomers Log(LOG_INFO); 347300906Sasomers syslog(priority, "%s", msg.str().c_str()); 348300906Sasomers return (true); 349300906Sasomers } 350300906Sasomers 351300906Sasomers Vdev vdev(zpl.front(), vdevConfig); 352300906Sasomers caseFile = &CaseFile::Create(vdev); 353300906Sasomers if (caseFile->ReEvaluate(*this) == false) { 354300906Sasomers stringstream msg; 355300906Sasomers int priority = LOG_INFO; 356300906Sasomers msg << "ZfsEvent::Process: Unconsumed event for vdev("; 357300906Sasomers msg << zpool_get_name(zpl.front()) << ","; 358300906Sasomers msg << vdev.GUID() << ") "; 359300906Sasomers msg << "queued"; 360300906Sasomers Log(LOG_INFO); 361300906Sasomers syslog(priority, "%s", msg.str().c_str()); 362300906Sasomers return (true); 363300906Sasomers } 364300906Sasomers return (false); 365300906Sasomers} 366300906Sasomers 367300906Sasomers//- ZfsEvent Protected Methods ------------------------------------------------- 368300906SasomersZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, 369300906Sasomers const string &eventString) 370300906Sasomers : DevdCtl::ZfsEvent(type, nvpairs, eventString) 371300906Sasomers{ 372300906Sasomers} 373300906Sasomers 374300906SasomersZfsEvent::ZfsEvent(const ZfsEvent &src) 375300906Sasomers : DevdCtl::ZfsEvent(src) 376300906Sasomers{ 377300906Sasomers} 378300906Sasomers 379300906Sasomers/* 380300906Sasomers * Sometimes the kernel won't detach a spare when it is no longer needed. This 381300906Sasomers * can happen for example if a drive is removed, then either the pool is 382300906Sasomers * exported or the machine is powered off, then the drive is reinserted, then 383300906Sasomers * the machine is powered on or the pool is imported. ZFSD must detach these 384300906Sasomers * spares itself. 385300906Sasomers */ 386300906Sasomersvoid 387300906SasomersZfsEvent::CleanupSpares() const 388300906Sasomers{ 389300906Sasomers Guid poolGUID(PoolGUID()); 390300906Sasomers ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); 391300906Sasomers if (!zpl.empty()) { 392300906Sasomers zpool_handle_t* hdl; 393300906Sasomers 394300906Sasomers hdl = zpl.front(); 395300906Sasomers VdevIterator(hdl).Each(TryDetach, (void*)hdl); 396300906Sasomers } 397300906Sasomers} 398300906Sasomers 399300906Sasomersvoid 400300906SasomersZfsEvent::ProcessPoolEvent() const 401300906Sasomers{ 402300906Sasomers bool degradedDevice(false); 403300906Sasomers 404300906Sasomers /* The pool is destroyed. Discard any open cases */ 405300906Sasomers if (Value("type") == "misc.fs.zfs.pool_destroy") { 406300906Sasomers Log(LOG_INFO); 407300906Sasomers CaseFile::ReEvaluateByGuid(PoolGUID(), *this); 408300906Sasomers return; 409300906Sasomers } 410300906Sasomers 411300906Sasomers CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); 412300906Sasomers if (caseFile != NULL) { 413300906Sasomers if (caseFile->VdevState() != VDEV_STATE_UNKNOWN 414300906Sasomers && caseFile->VdevState() < VDEV_STATE_HEALTHY) 415300906Sasomers degradedDevice = true; 416300906Sasomers 417300906Sasomers Log(LOG_INFO); 418300906Sasomers caseFile->ReEvaluate(*this); 419300906Sasomers } 420300906Sasomers else if (Value("type") == "misc.fs.zfs.resilver_finish") 421300906Sasomers { 422300906Sasomers /* 423300906Sasomers * It's possible to get a resilver_finish event with no 424300906Sasomers * corresponding casefile. For example, if a damaged pool were 425300906Sasomers * exported, repaired, then reimported. 426300906Sasomers */ 427300906Sasomers Log(LOG_INFO); 428300906Sasomers CleanupSpares(); 429300906Sasomers } 430300906Sasomers 431300906Sasomers if (Value("type") == "misc.fs.zfs.vdev_remove" 432300906Sasomers && degradedDevice == false) { 433300906Sasomers 434300906Sasomers /* See if any other cases can make use of this device. */ 435300906Sasomers Log(LOG_INFO); 436300906Sasomers ZfsDaemon::RequestSystemRescan(); 437300906Sasomers } 438300906Sasomers} 439300906Sasomers 440300906Sasomersbool 441300906SasomersZfsEvent::TryDetach(Vdev &vdev, void *cbArg) 442300906Sasomers{ 443300906Sasomers /* 444300906Sasomers * Outline: 445300906Sasomers * if this device is a spare, and its parent includes one healthy, 446300906Sasomers * non-spare child, then detach this device. 447300906Sasomers */ 448300906Sasomers zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg)); 449300906Sasomers 450300906Sasomers if (vdev.IsSpare()) { 451300906Sasomers std::list<Vdev> siblings; 452300906Sasomers std::list<Vdev>::iterator siblings_it; 453300906Sasomers boolean_t cleanup = B_FALSE; 454300906Sasomers 455300906Sasomers Vdev parent = vdev.Parent(); 456300906Sasomers siblings = parent.Children(); 457300906Sasomers 458300906Sasomers /* Determine whether the parent should be cleaned up */ 459300906Sasomers for (siblings_it = siblings.begin(); 460300906Sasomers siblings_it != siblings.end(); 461300906Sasomers siblings_it++) { 462300906Sasomers Vdev sibling = *siblings_it; 463300906Sasomers 464300906Sasomers if (!sibling.IsSpare() && 465300906Sasomers sibling.State() == VDEV_STATE_HEALTHY) { 466300906Sasomers cleanup = B_TRUE; 467300906Sasomers break; 468300906Sasomers } 469300906Sasomers } 470300906Sasomers 471300906Sasomers if (cleanup) { 472300906Sasomers syslog(LOG_INFO, "Detaching spare vdev %s from pool %s", 473300906Sasomers vdev.Path().c_str(), zpool_get_name(hdl)); 474300906Sasomers zpool_vdev_detach(hdl, vdev.Path().c_str()); 475300906Sasomers } 476300906Sasomers 477300906Sasomers } 478300906Sasomers 479300906Sasomers /* Always return false, because there may be other spares to detach */ 480300906Sasomers return (false); 481300906Sasomers} 482