23.11.28 최초 작성
24.01.04 후반부 기법 내용 보충
24.01.23 워크큐 실행 흐름 추가
디바이스 드라이버는 인터럽트 발생 시 신속히 처리해줘야 한다. 하지만 현실적으로 그럴 수 없는 경우도 있기에 인터럽트를 처리할 코드를 다음과 같이 2단계로 분리해 설계한다.
Top Half
Bottom Half
Acknowledge to device
: 다음 인터럽트를 받을 수 있도록 세팅Top Half
) : 레지스터 read/write 처리wake_up_interruptible()
호출 : 대기 중인 커널 스레드 깨움태스크릿
: Soft IRQ
서비스를 동적으로 쓸 수 있는 인터페이스 및 자료구조워크큐
: 인터럽트 핸들러가 실행될 때 워크를 워크큐에 큐잉하고 프로세스 콘텍스트에서 실행되는 워커 스레드에서 인터럽트 후반부 처리Bottom Half
처리를 위한 인터럽트 처리 전용 irq 커널 스레드
인터럽트 후반부 처리를 위한 인터럽트 처리 전용 프로세스
동적으로 priority 설정 가능하며 real-time으로 동작함
부팅 과정에서 request_threaded_irq()
함수를 호출해 IRQ thread 생성
// /source/include/linux/interrupt.h
request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn,
unsigned long flags, const char *name, void *dev);
///
irq : 인터럽트 번호
handler : 인터럽트 핸들러 주소
thread_fn : IRQ 스레드 처리 함수 주소
flags : 인터럽트 핸들링 플래그
name : 인터럽트 이름
인터럽트 핸들러 실행이 끝나면 후반부를 Soft irq
에서 처리
인터럽트 핸들러의 실행이 오래 걸릴 경우 ksoftirq
프로세스를 깨우고 해당 프로세스가 후반부 처리
Soft IRQ 서비스
: Soft IRQ
를 처리하는 단위로 부팅 과정에서 open_softirq()
함수를 호출해 Soft IRQ 서비스 등록
인터럽트 후반부 기법으로 사용되며 해당 형태로 커널을 구성하는 서브시스템(타이머, 스케줄러, 네트워크, UFS 드라이버)이 동작
// /source/kernel/softirq.c
인터럽트 핸들러가 실행될 때 워크를 워크큐에 큐잉하고 프로세스 콘텍스트에서 실행되는 워커 스레드에서 인터럽트 후반부 처리
워크
: 워크큐에서 후반부 처리를 실행하는 단위
워크를 실행하고 워크큐 관련 자료구조를 업데이트 하는 커널 스레드
kworker/
로 이름이 부여되며 워커풀의 종류에 따라 뒤에 번호가 부여됨
worker_thread()
함수에 구현 됨
schedule_work()
queue_work_on()
__queue_work_on()
: 새로운 워크 큐 구조체에 접근하고 insert_work()
를 호출insert_work()
: pool_workqueue
에 지정한 워크를 큐잉wake_up_worker()
worker_thread()
: struct worker_pool
에서 워크를 읽어와 process_one_work()
에 전달하고 호출process_one_work()
: 전달받은 워크를 실행struct worker_pool
: 전체 워크를 관리하는 자료구조// kernel/workqueue.c
struct worker_pool {
raw_spinlock_t lock; /* the pool lock */
int cpu; /* I: the associated cpu */
int node; /* I: the associated node ID */
int id; /* I: pool ID */
unsigned int flags; /* X: flags */
struct list_head worklist; /* L: list of pending works */
int nr_workers; /* L: total number of workers */
int nr_idle; /* L: currently idle workers */
struct list_head idle_list; /* X: list of idle workers */
struct timer_list idle_timer; /* L: worker idle timeout */
struct timer_list mayday_timer; /* L: SOS timer for workers */
...
struct workqueue_struct
: 각 워크를 관리하는 자료구조struct workqueue_struct {
struct list_head pwqs; /* WR: all pwqs of this wq */
struct list_head list; /* PR: list of all workqueues */
struct mutex mutex; /* protects this wq */
int work_color; /* WQ: current work color */
int flush_color; /* WQ: current flush color */
atomic_t nr_pwqs_to_flush; /* flush in progress */
struct wq_flusher *first_flusher; /* WQ: first flusher */
struct list_head flusher_queue; /* WQ: flush waiters */
struct list_head flusher_overflow; /* WQ: flush overflow list */
struct list_head maydays; /* MD: pwqs requesting rescue */
struct worker *rescuer; /* MD: rescue worker */
unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */
struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */
struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */
struct pool_workqueue
: 각 cpu별로 실행중인 워크struct pool_workqueue {
struct worker_pool *pool; /* I: the associated pool */
struct workqueue_struct *wq; /* I: the owning workqueue */
int work_color; /* L: current color */
int flush_color; /* L: flushing color */
int refcnt; /* L: reference count */
int nr_in_flight[WORK_NR_COLORS];
/* L: nr of in_flight works */
int nr_active; /* L: nr of active works */
int max_active; /* L: max active works */
struct list_head inactive_works; /* L: inactive works */
struct list_head pwqs_node; /* WR: node on wq->pwqs */
struct list_head mayday_node; /* MD: node on wq->maydays */
struct work_struct unbound_release_work;
struct rcu_head rcu;
} __aligned(1 << WORK_STRUCT_FLAG_BITS);