/*-1* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions, and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* substantially similar to the "NO WARRANTY" disclaimer below12* ("Disclaimer") and any redistribution must be conditioned upon13* including a substantially similar Disclaimer requirement for further14* binary redistribution.15*16* NO WARRANTY17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS18* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT19* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR20* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT21* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,25* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING26* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGES.28*29* Authors: Justin T. Gibbs (Spectra Logic Corporation)30*/3132/**33* \file callout.cc34*35* \brief Implementation of the Callout class - multi-client36* timer services built on top of the POSIX interval timer.37*/3839#include <sys/byteorder.h>40#include <sys/time.h>4142#include <signal.h>43#include <syslog.h>4445#include <climits>46#include <list>47#include <map>48#include <string>4950#include <devdctl/guid.h>51#include <devdctl/event.h>52#include <devdctl/event_factory.h>53#include <devdctl/consumer.h>54#include <devdctl/exception.h>5556#include "callout.h"57#include "vdev_iterator.h"58#include "zfsd.h"59#include "zfsd_exception.h"6061std::list<Callout *> Callout::s_activeCallouts;62bool Callout::s_alarmFired(false);6364void65Callout::Init()66{67signal(SIGALRM, Callout::AlarmSignalHandler);68}6970bool71Callout::Stop()72{73if (!IsPending())74return (false);7576for (std::list<Callout *>::iterator it(s_activeCallouts.begin());77it != s_activeCallouts.end(); it++) {78if (*it != this)79continue;8081it = s_activeCallouts.erase(it);82if (it != s_activeCallouts.end()) {8384/*85* Maintain correct interval for the86* callouts that follow the just removed87* entry.88*/89timeradd(&(*it)->m_interval, &m_interval,90&(*it)->m_interval);91}92break;93}94m_pending = false;95return (true);96}9798bool99Callout::Reset(const timeval &interval, CalloutFunc_t *func, void *arg)100{101bool cancelled(false);102103if (!timerisset(&interval))104throw ZfsdException("Callout::Reset: interval of 0");105106cancelled = Stop();107108m_interval = interval;109m_func = func;110m_arg = arg;111m_pending = true;112113std::list<Callout *>::iterator it(s_activeCallouts.begin());114for (; it != s_activeCallouts.end(); it++) {115116if (timercmp(&(*it)->m_interval, &m_interval, <=)) {117/*118* Decrease our interval by those that come119* before us.120*/121timersub(&m_interval, &(*it)->m_interval, &m_interval);122} else {123/*124* Account for the time between the newly125* inserted event and those that follow.126*/127timersub(&(*it)->m_interval, &m_interval,128&(*it)->m_interval);129break;130}131}132s_activeCallouts.insert(it, this);133134135if (s_activeCallouts.front() == this) {136itimerval timerval = { {0, 0}, m_interval };137138setitimer(ITIMER_REAL, &timerval, NULL);139}140141return (cancelled);142}143144void145Callout::AlarmSignalHandler(int)146{147s_alarmFired = true;148ZfsDaemon::WakeEventLoop();149}150151void152Callout::ExpireCallouts()153{154if (!s_alarmFired)155return;156157s_alarmFired = false;158if (s_activeCallouts.empty()) {159/* Callout removal/SIGALRM race was lost. */160return;161}162163/*164* Expire the first callout (the one we used to set the165* interval timer) as well as any callouts following that166* expire at the same time (have a zero interval from167* the callout before it).168*/169do {170Callout *cur(s_activeCallouts.front());171s_activeCallouts.pop_front();172cur->m_pending = false;173cur->m_func(cur->m_arg);174} while (!s_activeCallouts.empty()175&& timerisset(&s_activeCallouts.front()->m_interval) == 0);176177if (!s_activeCallouts.empty()) {178Callout *next(s_activeCallouts.front());179itimerval timerval = { { 0, 0 }, next->m_interval };180181setitimer(ITIMER_REAL, &timerval, NULL);182}183}184185timeval186Callout::TimeRemaining() const187{188/*189* Outline: Add the m_interval for each callout in s_activeCallouts190* ahead of this, except for the first callout. Add to that the result191* of getitimer (That's because the first callout stores its original192* interval setting while the timer is ticking).193*/194itimerval timervalToAlarm;195timeval timeToExpiry;196std::list<Callout *>::iterator it;197198if (!IsPending()) {199timeToExpiry.tv_sec = INT_MAX;200timeToExpiry.tv_usec = 999999; /*maximum normalized value*/201return (timeToExpiry);202}203204timerclear(&timeToExpiry);205getitimer(ITIMER_REAL, &timervalToAlarm);206timeval& timeToAlarm = timervalToAlarm.it_value;207timeradd(&timeToExpiry, &timeToAlarm, &timeToExpiry);208209it =s_activeCallouts.begin();210it++; /*skip the first callout in the list*/211for (; it != s_activeCallouts.end(); it++) {212timeradd(&timeToExpiry, &(*it)->m_interval, &timeToExpiry);213if ((*it) == this)214break;215}216return (timeToExpiry);217}218219220