linux-debug/workqueue

created : Tue, 10 Nov 2020 23:56:55 +0900
modified : Wed, 09 Dec 2020 13:31:24 +0900
linux-debug workqueue

워크큐

워크큐의 특징

워크큐와 다른 인터럽트 후반부 기법과의 비교

워크큐 설계

워크큐의 종류

alloc_workqueue()
#define alloc_workqueue(fmt, flags, max_active, args...) \
  __alloc_workqueue_key((fmt), (flags), (max_active), \
    NULL, NULL, ##args)
7가지 워크큐
int __init workqueue_init_early(void)
{
  int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
  int i, cpu;
  /* skip */
  system_wq = alloc_workqueue("events", 0, 0);
  system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0);
  system_long_wq = alloc_workqueue("events_long", 0, 0);
  system_unbound_wq = alloc_workqueue("evnets_unbound", WQ_UNBOUND,
                        WQ_UNBOUND_MAX_ACTIVE);
  system_freezable_wq = alloc_workqueue("events_power_efficient",
                        WQ_FREEZABLE, 0);
  system_power_efficient_wq = alloc_workqueue("events_power_efficient",
                        WQ_POWER_EFFICIENT, 0);
  system_freezable_power_efficient_wq =
    alloc_workqueue("events_freezable_power_efficient",
                        WQ_FREEZABLE | WQ_POWER_EFFICIENT,
                        0);

  BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq ||
        !system_unbound_wq || !system_freezable_sq ||
        !system_power_efficient_wq ||
        !system_freezable_power_efficient_wq);
}
워크
struct work_struct {
  atomic_long_t data;
  struct list_head entry;
  work_func_t func;
  #ifdef CONFIG_LOCKDEP
  struct lockdep_map lockdep_map;
  #endif
};
워크 큐잉
워크의 실행 주체

워커 스레드
워커 스레드의 생성 시기

딜레이워크

struct delayed_work {
  struct work_struct work;
  struct timer_list timer;

  struct workqueue_struct *wq;
  int cpu;
};
딜레이 워크의 전체 흐름
INIT_DELAYED_WORK()
struct delayed_work d_work;
static int sample()
{
  INIT_DELAYED_WORK(&d_work, callback_fn);
}
#define INIT_DELAYED_WORK(_work, _func)       \
  __INIT_DELAYED_WORK(_work, _func, 0)
#define __INIT_DELAYED_WORK(_work, _func, _tflags)  \
  do {                                              \
    INIT_WORK(&(_work)->work, (_func));             \
    __init_timer(&(_work)->timer,                   \
      delayed_work_timer_fn,                        \
      (_tflags) | TIMER_IRQSAFE);                   \
  } while(0);
#define __init_timer(_timer, _fn, _flags)                       \
  do {                                                          \
    static struct lock_class_key __key;                         \
    init_timer_key((_timer), (_fn), (_flags), #_timer, &__key); \
  } while (0)
void init_timer_key(struct timer_list *timer,
  void (*func)(truct timer_list *), unsigned int flags,
  const char *name, struct lock_class_key *key)
{
  debug_init(timer);
  do_init_timer(timer, func, flags, name, key);
}
schedule_delayed_work()
schedule_delayed_work(&d_work, timeout);
static inline bool schedule_delayed_work(struct delayed_work *dwork,
                               unsigned long delay)
{
  return queue_dleayed_work(system_wq, dwork, delay);
}
static inline bool queue_delayed_work(struct workqueue_struct *wq,
                              struct delayed_work *dwork,
                              unsigned long delayed)
{
  return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}
bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
          struct delayed_work *dwork, unsigned long delay)
{
  struct work-struct *work = &dwork->work;
  bool ret = false;
  unsigned long flags;

  /* read the comment in __queue_work() */
  local_irq_save(flags);

  if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
    __queue_delayed_work(cpu, wq, dwork, delay);
    ret = true;
  }

  local_irq_restore(flags);
  return ret;
}
static void __queue_delayed_work(int cpu, struct workqueue_struct *wq,
                          struct dealyed_work *dwork, unsigned long delay)
{
  struct timer_list *timer = &dwork->timer;
  struct work_struct *work = &dwork->work;

  WARN_ON_ONCE(!wq);
  WARN_ON_ONCE(timer->function != delayed_work_timer_fn ||
              timer->data != (unsigned long)dwork);
  WARN_ON_ONCE(timer_pending(timer));
  WARN_ON_ONCE(!list_empty(&work->entry));

  if (!delay) {
    __queue_work(cpu, wq, &dwork->work);
    return;
  }

  dwork->wq = wq;
  dwork->cpu = cpu;
  timer->expires = jiffies + delay;

  if (unlikely(cpu != WORK_CPU_UNBOUND))
    add_timer_on(timer, cpu);
  else
    add_tiemr(timer);
}
delayed_work_timer_fn()
void delayed_work_timer_fn(unsigned long __data)
{
  struct delayed_work *dwork = (struct dleayed_work *)__data;

  /* should have been called from irqsafe timer with irq already off */
  __queue_work(dwork->cpu, dwork->wq, &dwork->work);
}