openflow build environment setup
This commit is contained in:
527
openflow/include/click/routerthread.hh
Normal file
527
openflow/include/click/routerthread.hh
Normal file
@ -0,0 +1,527 @@
|
||||
// -*- c-basic-offset: 4; related-file-name: "../../lib/routerthread.cc" -*-
|
||||
#ifndef CLICK_ROUTERTHREAD_HH
|
||||
#define CLICK_ROUTERTHREAD_HH
|
||||
#include <click/sync.hh>
|
||||
#include <click/vector.hh>
|
||||
#include <click/timerset.hh>
|
||||
#if CLICK_LINUXMODULE
|
||||
# include <click/cxxprotect.h>
|
||||
CLICK_CXX_PROTECT
|
||||
# include <linux/sched.h>
|
||||
CLICK_CXX_UNPROTECT
|
||||
# include <click/cxxunprotect.h>
|
||||
#elif CLICK_BSDMODULE
|
||||
# include <click/cxxprotect.h>
|
||||
CLICK_CXX_PROTECT
|
||||
# include <sys/systm.h>
|
||||
CLICK_CXX_UNPROTECT
|
||||
# include <click/cxxunprotect.h>
|
||||
#elif CLICK_USERLEVEL
|
||||
# include <click/selectset.hh>
|
||||
#endif
|
||||
|
||||
// NB: user must #include <click/task.hh> before <click/routerthread.hh>.
|
||||
// We cannot #include <click/task.hh> ourselves because of circular #include
|
||||
// dependency.
|
||||
CLICK_DECLS
|
||||
|
||||
class RouterThread { public:
|
||||
|
||||
enum { THREAD_QUIESCENT = -1, THREAD_UNKNOWN = -1000 };
|
||||
|
||||
inline int thread_id() const;
|
||||
|
||||
inline Master *master() const;
|
||||
inline TimerSet &timer_set() { return _timers; }
|
||||
inline const TimerSet &timer_set() const { return _timers; }
|
||||
#if CLICK_USERLEVEL
|
||||
inline SelectSet &select_set() { return _selects; }
|
||||
inline const SelectSet &select_set() const { return _selects; }
|
||||
#endif
|
||||
|
||||
// Task list functions
|
||||
inline bool active() const;
|
||||
inline Task *task_begin() const;
|
||||
inline Task *task_next(Task *task) const;
|
||||
inline Task *task_end() const;
|
||||
void scheduled_tasks(Router *router, Vector<Task *> &x);
|
||||
|
||||
inline void lock_tasks();
|
||||
inline void unlock_tasks();
|
||||
|
||||
inline void schedule_block_tasks();
|
||||
inline void block_tasks(bool scheduled);
|
||||
inline void unblock_tasks();
|
||||
|
||||
inline bool stop_flag() const;
|
||||
|
||||
inline void mark_driver_entry();
|
||||
void driver();
|
||||
|
||||
void kill_router(Router *router);
|
||||
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
// min_cpu_share() and max_cpu_share() are expressed on a scale with
|
||||
// Task::MAX_UTILIZATION == 100%.
|
||||
unsigned min_cpu_share() const { return _min_click_share; }
|
||||
unsigned max_cpu_share() const { return _max_click_share; }
|
||||
unsigned cur_cpu_share() const { return _cur_click_share; }
|
||||
void set_cpu_share(unsigned min_share, unsigned max_share);
|
||||
#endif
|
||||
|
||||
#if CLICK_LINUXMODULE || CLICK_BSDMODULE
|
||||
bool greedy() const { return _greedy; }
|
||||
void set_greedy(bool g) { _greedy = g; }
|
||||
#endif
|
||||
|
||||
inline void wake();
|
||||
|
||||
#if CLICK_USERLEVEL
|
||||
inline void run_signals();
|
||||
#endif
|
||||
|
||||
enum { S_PAUSED, S_BLOCKED, S_TIMERWAIT,
|
||||
S_LOCKSELECT, S_LOCKTASKS,
|
||||
S_RUNTASK, S_RUNTIMER, S_RUNSIGNAL, S_RUNPENDING, S_RUNSELECT,
|
||||
NSTATES };
|
||||
inline void set_thread_state(int state);
|
||||
inline void set_thread_state_for_blocking(int delay_type);
|
||||
#if CLICK_DEBUG_SCHEDULING
|
||||
int thread_state() const { return _thread_state; }
|
||||
static String thread_state_name(int state);
|
||||
uint32_t driver_epoch() const { return _driver_epoch; }
|
||||
uint32_t driver_task_epoch() const { return _driver_task_epoch; }
|
||||
Timestamp task_epoch_time(uint32_t epoch) const;
|
||||
# if CLICK_LINUXMODULE
|
||||
struct task_struct *sleeper() const { return _linux_task; }
|
||||
# endif
|
||||
# if CLICK_DEBUG_SCHEDULING > 1
|
||||
inline Timestamp thread_state_time(int state) const;
|
||||
inline uint64_t thread_state_count(int state) const;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
#if HAVE_TASK_HEAP
|
||||
struct task_heap_element {
|
||||
unsigned pass;
|
||||
Task *t;
|
||||
task_heap_element() {
|
||||
}
|
||||
task_heap_element(Task *t_)
|
||||
: pass(t_->_pass), t(t_) {
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// LOCAL STATE GROUP
|
||||
TaskLink _task_link;
|
||||
volatile bool _stop_flag;
|
||||
#if HAVE_TASK_HEAP
|
||||
Vector<task_heap_element> _task_heap;
|
||||
#endif
|
||||
|
||||
TimerSet _timers;
|
||||
#if CLICK_USERLEVEL
|
||||
SelectSet _selects;
|
||||
#endif
|
||||
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
enum { C_CLICK, C_KERNEL, NCLIENTS };
|
||||
struct Client { // top-level stride clients
|
||||
unsigned pass;
|
||||
unsigned stride;
|
||||
int tickets;
|
||||
Client() : pass(0), tickets(0) { }
|
||||
};
|
||||
Client _clients[NCLIENTS];
|
||||
unsigned _global_pass; // global pass
|
||||
unsigned _max_click_share; // maximum allowed Click share of CPU
|
||||
unsigned _min_click_share; // minimum allowed Click share of CPU
|
||||
unsigned _cur_click_share; // current Click share
|
||||
Timestamp _adaptive_restride_timestamp;
|
||||
int _adaptive_restride_iter;
|
||||
#endif
|
||||
|
||||
// EXTERNAL STATE GROUP
|
||||
Spinlock _task_lock CLICK_ALIGNED(CLICK_CACHE_LINE_SIZE);
|
||||
atomic_uint32_t _task_blocker;
|
||||
atomic_uint32_t _task_blocker_waiting;
|
||||
|
||||
Task::Pending _pending_head;
|
||||
Task::Pending *_pending_tail;
|
||||
SpinlockIRQ _pending_lock;
|
||||
|
||||
// SHARED STATE GROUP
|
||||
Master *_master CLICK_ALIGNED(CLICK_CACHE_LINE_SIZE);
|
||||
int _id;
|
||||
bool _driver_entered;
|
||||
#if HAVE_MULTITHREAD && !(CLICK_LINUXMODULE || CLICK_MINIOS)
|
||||
click_processor_t _running_processor;
|
||||
#endif
|
||||
#if CLICK_LINUXMODULE
|
||||
bool _greedy;
|
||||
struct task_struct *_linux_task;
|
||||
#endif
|
||||
#if CLICK_MINIOS
|
||||
struct thread *_minios_thread;
|
||||
#endif
|
||||
public:
|
||||
unsigned _tasks_per_iter;
|
||||
unsigned _iters_per_os;
|
||||
private:
|
||||
|
||||
#if CLICK_NS
|
||||
Timestamp _ns_scheduled;
|
||||
Timestamp _ns_last_active;
|
||||
int _ns_active_iter;
|
||||
enum { ns_iters_per_time = 1000 };
|
||||
#endif
|
||||
|
||||
#if CLICK_BSDMODULE
|
||||
// XXX FreeBSD
|
||||
u_int64_t _old_tsc; /* MARKO - temp. */
|
||||
void *_sleep_ident;
|
||||
int _oticks;
|
||||
bool _greedy;
|
||||
#endif
|
||||
|
||||
#if CLICK_DEBUG_SCHEDULING
|
||||
int _thread_state;
|
||||
uint32_t _driver_epoch;
|
||||
uint32_t _driver_task_epoch;
|
||||
enum { TASK_EPOCH_BUFSIZ = 32 };
|
||||
uint32_t _task_epoch_first;
|
||||
Timestamp _task_epoch_time[TASK_EPOCH_BUFSIZ];
|
||||
# if CLICK_DEBUG_SCHEDULING > 1
|
||||
Timestamp _thread_state_time[NSTATES];
|
||||
uint64_t _thread_state_count[NSTATES];
|
||||
Timestamp _thread_state_timestamp;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// called by Master
|
||||
RouterThread(Master *master, int threadno);
|
||||
~RouterThread();
|
||||
|
||||
// task requests
|
||||
inline void add_pending();
|
||||
#if HAVE_STRIDE_SCHED
|
||||
inline unsigned pass() const {
|
||||
# if HAVE_TASK_HEAP
|
||||
return _task_heap.size() ? _task_heap.unchecked_at(0).pass : 0;
|
||||
# else
|
||||
return _task_link._next->_pass;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// task running functions
|
||||
void driver_lock_tasks();
|
||||
inline void driver_unlock_tasks() {
|
||||
uint32_t val = _task_blocker.compare_swap((uint32_t) -1, 0);
|
||||
(void) val;
|
||||
assert(val == (uint32_t) -1);
|
||||
}
|
||||
|
||||
inline void run_tasks(int ntasks);
|
||||
inline void process_pending();
|
||||
inline void run_os();
|
||||
#if HAVE_ADAPTIVE_SCHEDULER
|
||||
void client_set_tickets(int client, int tickets);
|
||||
inline void client_update_pass(int client, const Timestamp &before);
|
||||
#endif
|
||||
#if HAVE_TASK_HEAP
|
||||
void task_reheapify_from(int pos, Task*);
|
||||
#endif
|
||||
static inline bool running_in_interrupt();
|
||||
inline bool current_thread_is_running() const;
|
||||
inline bool current_thread_is_running_cleanup() const;
|
||||
|
||||
friend class Task;
|
||||
friend class Master;
|
||||
#if CLICK_USERLEVEL
|
||||
friend class SelectSet;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** @brief Returns this thread's ID.
|
||||
*
|
||||
* The result is >= 0 for true threads, and < 0 for threads that never run any
|
||||
* of their associated Tasks.
|
||||
*/
|
||||
inline int
|
||||
RouterThread::thread_id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
/** @brief Returns this thread's associated Master. */
|
||||
inline Master*
|
||||
RouterThread::master() const
|
||||
{
|
||||
return _master;
|
||||
}
|
||||
|
||||
/** @brief Returns whether any tasks are scheduled.
|
||||
*
|
||||
* Returns false iff no tasks are scheduled and no events are pending. Since
|
||||
* not all events actually matter (for example, a Task might have been
|
||||
* scheduled and then subsequently unscheduled), active() may temporarily
|
||||
* return true even when no real events are outstanding.
|
||||
*/
|
||||
inline bool
|
||||
RouterThread::active() const
|
||||
{
|
||||
click_compiler_fence();
|
||||
#if HAVE_TASK_HEAP
|
||||
return _task_heap.size() != 0 || _pending_head.x;
|
||||
#else
|
||||
return _task_link._next != &_task_link || _pending_head.x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @brief Returns the beginning of the scheduled task list.
|
||||
*
|
||||
* Each RouterThread maintains a list of all currently-scheduled tasks.
|
||||
* Elements may traverse this list with the task_begin(), task_next(), and
|
||||
* task_end() functions, using iterator-like code such as:
|
||||
*
|
||||
* @code
|
||||
* thread->lock_tasks();
|
||||
* for (Task *t = thread->task_begin();
|
||||
* t != thread->task_end();
|
||||
* t = thread->task_next(t)) {
|
||||
* // ... do something with t...
|
||||
* }
|
||||
* thread->unlock_tasks();
|
||||
* @endcode
|
||||
*
|
||||
* The thread's task lock must be held during the traversal, as shown above.
|
||||
*
|
||||
* The return value may not be a real task. Test it against task_end() before
|
||||
* use.
|
||||
*
|
||||
* @sa task_next, task_end, lock_tasks, unlock_tasks
|
||||
*/
|
||||
inline Task *
|
||||
RouterThread::task_begin() const
|
||||
{
|
||||
#if HAVE_TASK_HEAP
|
||||
return (_task_heap.size() ? _task_heap.unchecked_at(0).t : 0);
|
||||
#else
|
||||
return static_cast<Task *>(_task_link._next);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @brief Returns the task following @a task in the scheduled task list.
|
||||
* @param task the current task
|
||||
*
|
||||
* The return value may not be a real task. Test it against task_end() before
|
||||
* use. However, the @a task argument must be a real task; do not attempt to
|
||||
* call task_next(task_end()).
|
||||
*
|
||||
* @sa task_begin for usage, task_end
|
||||
*/
|
||||
inline Task *
|
||||
RouterThread::task_next(Task *task) const
|
||||
{
|
||||
#if HAVE_TASK_HEAP
|
||||
int p = task->_schedpos + 1;
|
||||
return (p < _task_heap.size() ? _task_heap.unchecked_at(p).t : 0);
|
||||
#else
|
||||
return static_cast<Task *>(task->_next);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** @brief Returns the end of the scheduled task list.
|
||||
*
|
||||
* The return value is not a real task.
|
||||
*
|
||||
* @sa task_begin for usage, task_next
|
||||
*/
|
||||
inline Task *
|
||||
RouterThread::task_end() const
|
||||
{
|
||||
#if HAVE_TASK_HEAP
|
||||
return 0;
|
||||
#else
|
||||
return static_cast<Task *>(const_cast<TaskLink *>(&_task_link));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool
|
||||
RouterThread::running_in_interrupt()
|
||||
{
|
||||
#if CLICK_LINUXMODULE
|
||||
return in_interrupt();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::mark_driver_entry()
|
||||
{
|
||||
_driver_entered = true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
RouterThread::current_thread_is_running() const
|
||||
{
|
||||
#if CLICK_LINUXMODULE
|
||||
return current == _linux_task && !running_in_interrupt();
|
||||
#elif CLICK_MINIOS
|
||||
return get_current() == _minios_thread;
|
||||
#elif CLICK_USERLEVEL && HAVE_MULTITHREAD && HAVE___THREAD_STORAGE_CLASS
|
||||
return click_current_thread_id == (_id | 0x40000000);
|
||||
#elif CLICK_USERLEVEL && HAVE_MULTITHREAD
|
||||
return click_current_processor() == _running_processor;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool
|
||||
RouterThread::current_thread_is_running_cleanup() const
|
||||
{
|
||||
return current_thread_is_running() || (!_driver_entered && _id >= 0);
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::schedule_block_tasks()
|
||||
{
|
||||
assert(!current_thread_is_running());
|
||||
++_task_blocker_waiting;
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::block_tasks(bool scheduled)
|
||||
{
|
||||
assert(!current_thread_is_running() && !running_in_interrupt());
|
||||
if (!scheduled)
|
||||
++_task_blocker_waiting;
|
||||
while (1) {
|
||||
uint32_t blocker = _task_blocker.value();
|
||||
if ((int32_t) blocker >= 0
|
||||
&& _task_blocker.compare_swap(blocker, blocker + 1) == blocker)
|
||||
break;
|
||||
#if CLICK_LINUXMODULE
|
||||
// 3.Nov.2008: Must allow other threads a chance to run. Otherwise,
|
||||
// soft lock is possible: the thread in block_tasks() waits for
|
||||
// RouterThread::_linux_task to complete a task set, but
|
||||
// RouterThread::_linux_task can't run until the thread in
|
||||
// block_tasks() relinquishes the CPU.
|
||||
//
|
||||
// We might be able to avoid schedule() in some cases, but don't
|
||||
// bother to try.
|
||||
schedule();
|
||||
#endif
|
||||
}
|
||||
--_task_blocker_waiting;
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::unblock_tasks()
|
||||
{
|
||||
assert((int32_t) _task_blocker.value() > 0);
|
||||
--_task_blocker;
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::lock_tasks()
|
||||
{
|
||||
assert(!running_in_interrupt());
|
||||
if (unlikely(!current_thread_is_running())) {
|
||||
block_tasks(false);
|
||||
_task_lock.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::unlock_tasks()
|
||||
{
|
||||
assert(!running_in_interrupt());
|
||||
if (unlikely(!current_thread_is_running())) {
|
||||
_task_lock.release();
|
||||
unblock_tasks();
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::wake()
|
||||
{
|
||||
#if CLICK_LINUXMODULE
|
||||
struct task_struct *task = _linux_task;
|
||||
if (task)
|
||||
wake_up_process(task);
|
||||
#elif CLICK_USERLEVEL
|
||||
// see also Master::add_select()
|
||||
if (!current_thread_is_running())
|
||||
_selects.wake_immediate();
|
||||
#elif CLICK_BSDMODULE && !BSD_NETISRSCHED
|
||||
if (_sleep_ident)
|
||||
wakeup_one(&_sleep_ident);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::add_pending()
|
||||
{
|
||||
wake();
|
||||
}
|
||||
|
||||
inline bool
|
||||
RouterThread::stop_flag() const
|
||||
{
|
||||
return _stop_flag;
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::set_thread_state(int state)
|
||||
{
|
||||
(void) state;
|
||||
#if CLICK_DEBUG_SCHEDULING
|
||||
assert(state >= 0 && state < NSTATES);
|
||||
# if CLICK_DEBUG_SCHEDULING > 1
|
||||
Timestamp now = Timestamp::now();
|
||||
if (_thread_state_timestamp)
|
||||
_thread_state_time[_thread_state] += now - _thread_state_timestamp;
|
||||
if (_thread_state != state)
|
||||
++_thread_state_count[_thread_state];
|
||||
_thread_state_timestamp = now;
|
||||
# endif
|
||||
_thread_state = state;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
RouterThread::set_thread_state_for_blocking(int delay_type)
|
||||
{
|
||||
if (delay_type < 0)
|
||||
set_thread_state(S_BLOCKED);
|
||||
else
|
||||
set_thread_state(delay_type ? S_TIMERWAIT : S_PAUSED);
|
||||
}
|
||||
|
||||
#if CLICK_DEBUG_SCHEDULING > 1
|
||||
inline Timestamp
|
||||
RouterThread::thread_state_time(int state) const
|
||||
{
|
||||
assert(state >= 0 && state < NSTATES);
|
||||
return _thread_state_time[state];
|
||||
}
|
||||
|
||||
inline uint64_t
|
||||
RouterThread::thread_state_count(int state) const
|
||||
{
|
||||
assert(state >= 0 && state < NSTATES);
|
||||
return _thread_state_count[state];
|
||||
}
|
||||
#endif
|
||||
|
||||
CLICK_ENDDECLS
|
||||
#endif
|
||||
Reference in New Issue
Block a user