1b639fb45Simarom/*
2b639fb45Simarom Itay Marom
3b639fb45Simarom Cisco Systems, Inc.
4b639fb45Simarom*/
5b639fb45Simarom
6b639fb45Simarom/*
7b639fb45SimaromCopyright (c) 2015-2015 Cisco Systems, Inc.
8b639fb45Simarom
9b639fb45SimaromLicensed under the Apache License, Version 2.0 (the "License");
10b639fb45Simaromyou may not use this file except in compliance with the License.
11b639fb45SimaromYou may obtain a copy of the License at
12b639fb45Simarom
13b639fb45Simarom    http://www.apache.org/licenses/LICENSE-2.0
14b639fb45Simarom
15b639fb45SimaromUnless required by applicable law or agreed to in writing, software
16b639fb45Simaromdistributed under the License is distributed on an "AS IS" BASIS,
17b639fb45SimaromWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18b639fb45SimaromSee the License for the specific language governing permissions and
19b639fb45Simaromlimitations under the License.
20b639fb45Simarom*/
21b639fb45Simarom
22b639fb45Simarom#ifndef __TREX_WATCHDOG_H__
23b639fb45Simarom#define __TREX_WATCHDOG_H__
24b639fb45Simarom
25b639fb45Simarom#include <string>
26b639fb45Simarom#include <vector>
27b639fb45Simarom#include <thread>
283c4a29e1Simarom#include <mutex>
299ad36b3dSimarom#include <assert.h>
30b639fb45Simarom
313c4a29e1Simarom#include "mbuf.h"
32b639fb45Simarom#include "os_time.h"
33b639fb45Simarom
343ca8be80Simarom/**
353ca8be80Simarom * every thread creates its own monitor from its own memory
363ca8be80Simarom *
373ca8be80Simarom * @author imarom (19-Jun-16)
383ca8be80Simarom */
393ca8be80Simaromclass TrexMonitor {
40fff19c8cSimarom    friend class TrexWatchDog;
41fff19c8cSimarom
42b639fb45Simarompublic:
43b639fb45Simarom
443ca8be80Simarom    /**
453ca8be80Simarom    * create a monitor
463ca8be80Simarom    *
473ca8be80Simarom    * @author imarom (31-May-16)
483ca8be80Simarom    *
493ca8be80Simarom    * @param name
503ca8be80Simarom    * @param timeout
513ca8be80Simarom    *
523ca8be80Simarom    * @return int
533ca8be80Simarom    */
543ca8be80Simarom    void create(const std::string &name, double timeout_sec);
55ca8b613fSHanoh Haim
563c4a29e1Simarom    /**
57fff19c8cSimarom     * disable the monitor for 'time_sec'
58fff19c8cSimarom     * by default it will disable it for a long period of time
59fff19c8cSimarom     * (forever)
603c4a29e1Simarom     *
613c4a29e1Simarom     */
62fff19c8cSimarom    void disable(dsec_t time_sec = 1e9) {
639ad36b3dSimarom        set_timeout(time_sec);
643ca8be80Simarom    }
653c4a29e1Simarom
66fff19c8cSimarom    /**
67fff19c8cSimarom     * re-enable a monitor after it was disabled
68fff19c8cSimarom     *
69fff19c8cSimarom     */
70fff19c8cSimarom    void enable() {
719ad36b3dSimarom        set_timeout(m_base_timeout_sec);
729ad36b3dSimarom    }
739ad36b3dSimarom
749ad36b3dSimarom    /**
759ad36b3dSimarom     * not thread safe
769ad36b3dSimarom     * call from current thread only
779ad36b3dSimarom     */
789ad36b3dSimarom    void io_begin() {
799ad36b3dSimarom        /**
809ad36b3dSimarom         * holds a ref cnt
819ad36b3dSimarom         * a thread might start many IO operations
829ad36b3dSimarom         */
839ad36b3dSimarom        m_io_ref_cnt++;
849ad36b3dSimarom        set_timeout(IO_TIMEOUT_SEC);
859ad36b3dSimarom    }
869ad36b3dSimarom
879ad36b3dSimarom     /**
889ad36b3dSimarom     * not thread safe
899ad36b3dSimarom     * call from current thread only
909ad36b3dSimarom     */
919ad36b3dSimarom    void io_end() {
929ad36b3dSimarom        assert(m_io_ref_cnt > 0);
939ad36b3dSimarom        m_io_ref_cnt--;
949ad36b3dSimarom        if (m_io_ref_cnt == 0) {
959ad36b3dSimarom            set_timeout(m_base_timeout_sec);
969ad36b3dSimarom        }
97fff19c8cSimarom    }
98fff19c8cSimarom
993c4a29e1Simarom    /**
1003ca8be80Simarom     * tickle the monitor - this should be called from the thread
1013ca8be80Simarom     * to avoid the watchdog from detecting a stuck thread
1023c4a29e1Simarom     *
1033ca8be80Simarom     * @author imarom (19-Jun-16)
1043c4a29e1Simarom     */
1053ca8be80Simarom    void tickle() {
1063ca8be80Simarom        /* to avoid useless writes - first check */
1073ca8be80Simarom        if (!m_tickled) {
1083ca8be80Simarom            m_tickled = true;
1093ca8be80Simarom        }
1103ca8be80Simarom    }
1113c4a29e1Simarom
112fff19c8cSimarom    const std::string &get_name() const {
113fff19c8cSimarom        return m_name;
114fff19c8cSimarom    }
115fff19c8cSimarom
116fff19c8cSimarom    /* return how much time has passed since last tickle */
117fff19c8cSimarom    dsec_t get_interval(dsec_t now) const {
118fff19c8cSimarom        return (now - m_ts);
119fff19c8cSimarom    }
120fff19c8cSimarom
121fff19c8cSimarom
122fff19c8cSimarom    dsec_t get_timeout_sec() const {
123fff19c8cSimarom        return m_timeout_sec;
124fff19c8cSimarom    }
125fff19c8cSimarom
126fff19c8cSimarom
127fff19c8cSimaromprivate:
128fff19c8cSimarom
129b639fb45Simarom    /**
1303ca8be80Simarom     * called by the watchdog to reset the monitor for a new round
131b639fb45Simarom     *
132b639fb45Simarom     */
1333ca8be80Simarom    void reset(dsec_t now) {
1343ca8be80Simarom        m_tickled = false;
1353ca8be80Simarom        m_ts      = now;
1363ca8be80Simarom    }
1373ca8be80Simarom
138fff19c8cSimarom
1393ca8be80Simarom    pthread_t get_tid() const {
1403ca8be80Simarom        return m_tid;
1413ca8be80Simarom    }
1423ca8be80Simarom
1433ca8be80Simarom    volatile bool is_tickled() const {
1443ca8be80Simarom        return m_tickled;
1453ca8be80Simarom    }
1463ca8be80Simarom
1473ca8be80Simarom    bool is_expired(dsec_t now) const {
1483ca8be80Simarom        return ( get_interval(now) > m_timeout_sec );
1493ca8be80Simarom    }
1503ca8be80Simarom
1519ad36b3dSimarom    void set_timeout(double timeout_sec) {
1529ad36b3dSimarom        /* before changing timeout we MUST tickle and memory fence o.w the main thread might crash */
1539ad36b3dSimarom        tickle();
1549ad36b3dSimarom        asm volatile("mfence" ::: "memory");
1559ad36b3dSimarom        m_timeout_sec = timeout_sec;
1569ad36b3dSimarom    }
1579ad36b3dSimarom
1583ca8be80Simarom
1593ca8be80Simarom    /* write fields are first */
160fff19c8cSimarom    volatile bool    m_tickled;
161fff19c8cSimarom    int              m_handle;
162fff19c8cSimarom    dsec_t           m_ts;
163fff19c8cSimarom    double           m_timeout_sec;
1649ad36b3dSimarom    double           m_base_timeout_sec;
165fff19c8cSimarom    pthread_t        m_tid;
166fff19c8cSimarom    std::string      m_name;
1673ca8be80Simarom
1689ad36b3dSimarom    uint32_t         m_io_ref_cnt;
1699ad36b3dSimarom
1709ad36b3dSimarom    static const int IO_TIMEOUT_SEC = 30;
1713ca8be80Simarom
1723ca8be80Simarom} __rte_cache_aligned;
1733ca8be80Simarom
1743ca8be80Simarom
1753ca8be80Simarom/**
1763ca8be80Simarom * a watchdog is a list of registered monitors
1773ca8be80Simarom *
1783ca8be80Simarom * @author imarom (19-Jun-16)
1793ca8be80Simarom */
1803ca8be80Simaromclass TrexWatchDog {
1813ca8be80Simarompublic:
1823c4a29e1Simarom
1838feef53bSimarom    /**
1843ca8be80Simarom     * singleton entry
1853ca8be80Simarom     *
1863ca8be80Simarom     * @author imarom (19-Jun-16)
1878feef53bSimarom     *
1883ca8be80Simarom     * @return TrexWatchDog&
1898feef53bSimarom     */
1903ca8be80Simarom    static TrexWatchDog& getInstance() {
1913ca8be80Simarom        static TrexWatchDog instance;
1928feef53bSimarom
1933ca8be80Simarom        return instance;
1943ca8be80Simarom    }
1958feef53bSimarom
196935de2a8SYaroslav Brustinov    class IOFunction;
197935de2a8SYaroslav Brustinov
1983ca8be80Simarom    void init(bool enable);
199f5350dfbSYaroslav Brustinov
200f5350dfbSYaroslav Brustinov    /**
201f5350dfbSYaroslav Brustinov     * get monitor of current thread if registered
202f5350dfbSYaroslav Brustinov     * (NULL if not registered)
203f5350dfbSYaroslav Brustinov     *
204f5350dfbSYaroslav Brustinov     */
205f5350dfbSYaroslav Brustinov    TrexMonitor * get_current_monitor();
206f5350dfbSYaroslav Brustinov
207b639fb45Simarom    /**
2083ca8be80Simarom     * add a monitor to the watchdog
2093ca8be80Simarom     * from now on this monitor will be watched
2103ca8be80Simarom     *
2113ca8be80Simarom     * @author imarom (19-Jun-16)
212b639fb45Simarom     *
2133ca8be80Simarom     * @param monitor - a pointer to the object
214b639fb45Simarom     *
215b639fb45Simarom     */
2163ca8be80Simarom    void register_monitor(TrexMonitor *monitor);
217b639fb45Simarom
2183c4a29e1Simarom
219b639fb45Simarom    /**
220b639fb45Simarom     * start the watchdog
221b639fb45Simarom     *
222b639fb45Simarom     */
223b639fb45Simarom    void start();
224b639fb45Simarom
2253c4a29e1Simarom
226b639fb45Simarom    /**
227b639fb45Simarom     * stop the watchdog
228b639fb45Simarom     *
229b639fb45Simarom     */
230b639fb45Simarom    void stop();
231b639fb45Simarom
232b639fb45Simarom
2333ca8be80Simaromprivate:
234b639fb45Simarom
2353ca8be80Simarom    TrexWatchDog() {
236fff19c8cSimarom        m_thread        = NULL;
237fff19c8cSimarom        m_enable        = false;
238fff19c8cSimarom        m_active        = false;
239fff19c8cSimarom        m_mon_count     = 0;
2403ca8be80Simarom    }
241b639fb45Simarom
2423c4a29e1Simarom    void register_signal();
2433c4a29e1Simarom    void _main();
2443c4a29e1Simarom
2453ca8be80Simarom    static const int           MAX_MONITORS = 100;
2463ca8be80Simarom    TrexMonitor               *m_monitors[MAX_MONITORS];
2473ca8be80Simarom    volatile int               m_mon_count;
2483ca8be80Simarom    std::mutex                 m_lock;
249ca8b613fSHanoh Haim
2503ca8be80Simarom    bool                       m_enable;
2513ca8be80Simarom    volatile bool              m_active;
2523ca8be80Simarom    std::thread               *m_thread;
2533c4a29e1Simarom
2543ca8be80Simarom    static bool                g_signal_init;
255b639fb45Simarom};
256b639fb45Simarom
257935de2a8SYaroslav Brustinovclass TrexWatchDog::IOFunction {
258935de2a8SYaroslav Brustinovpublic:
259935de2a8SYaroslav Brustinov    static void io_begin() {
260935de2a8SYaroslav Brustinov        TrexMonitor * cur_monitor = TrexWatchDog::getInstance().get_current_monitor();
261935de2a8SYaroslav Brustinov        if (cur_monitor != NULL) {
262935de2a8SYaroslav Brustinov            cur_monitor->io_begin();
263935de2a8SYaroslav Brustinov        }
264935de2a8SYaroslav Brustinov    }
265935de2a8SYaroslav Brustinov
266935de2a8SYaroslav Brustinov    static void io_end() {
267935de2a8SYaroslav Brustinov        TrexMonitor * cur_monitor = TrexWatchDog::getInstance().get_current_monitor();
268935de2a8SYaroslav Brustinov        if (cur_monitor != NULL) {
269935de2a8SYaroslav Brustinov            cur_monitor->io_end();
270935de2a8SYaroslav Brustinov        }
271935de2a8SYaroslav Brustinov    }
272935de2a8SYaroslav Brustinov
273935de2a8SYaroslav Brustinov    IOFunction() {
274935de2a8SYaroslav Brustinov        IOFunction::io_begin();
275935de2a8SYaroslav Brustinov    }
276935de2a8SYaroslav Brustinov
277935de2a8SYaroslav Brustinov    ~IOFunction() {
278935de2a8SYaroslav Brustinov        IOFunction::io_end();
279935de2a8SYaroslav Brustinov    }
280935de2a8SYaroslav Brustinov
281935de2a8SYaroslav Brustinov};
2823c4a29e1Simarom
283b639fb45Simarom#endif /* __TREX_WATCHDOG_H__ */
284