인터럽트 후반부 기법

EEEFFEE·2023년 11월 28일
0

Armv8 Architecture

목록 보기
6/15

23.11.28 최초 작성
24.01.04 후반부 기법 내용 보충
24.01.23 워크큐 실행 흐름 추가

1. 인터럽트 루틴 구분

디바이스 드라이버는 인터럽트 발생 시 신속히 처리해줘야 한다. 하지만 현실적으로 그럴 수 없는 경우도 있기에 인터럽트를 처리할 코드를 다음과 같이 2단계로 분리해 설계한다.

  • Top Half
    • 인터럽트가 신속히 처리해야 하는 루틴
    • 최소한의 일을 수행함
    • 인터럽트 핸들러 & 컨텍스트에서 처리
  • Bottom Half
    • 인터럽트 루틴 중 나중에 처리해도 되는 부분
    • 처리가 약간 지연되도 상관없는 부분 처리

scheduling while atomic

1.1 FRQ, IRQ 관점 인터럽트 처리 과정

  1. Acknowledge to device : 다음 인터럽트를 받을 수 있도록 세팅
  2. 인터럽트 전반부 처리(Top Half) : 레지스터 read/write 처리
  3. wake_up_interruptible()호출 : 대기 중인 커널 스레드 깨움

2. 인터럽트 후반부 기법

  • 태스크릿 : Soft IRQ서비스를 동적으로 쓸 수 있는 인터페이스 및 자료구조
  • 워크큐 : 인터럽트 핸들러가 실행될 때 워크를 워크큐에 큐잉하고 프로세스 콘텍스트에서 실행되는 워커 스레드에서 인터럽트 후반부 처리

2.1 IRQ thread

  • 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 : 인터럽트 이름

2.2 Soft IRQ

  • 인터럽트 핸들러 실행이 끝나면 후반부를 Soft irq에서 처리
    인터럽트 핸들러의 실행이 오래 걸릴 경우 ksoftirq 프로세스를 깨우고 해당 프로세스가 후반부 처리

  • Soft IRQ 서비스 : Soft IRQ를 처리하는 단위로 부팅 과정에서 open_softirq()함수를 호출해 Soft IRQ 서비스 등록

  • 인터럽트 후반부 기법으로 사용되며 해당 형태로 커널을 구성하는 서브시스템(타이머, 스케줄러, 네트워크, UFS 드라이버)이 동작


// /source/kernel/softirq.c

2.3 워크큐

  • 인터럽트 핸들러가 실행될 때 워크를 워크큐에 큐잉하고 프로세스 콘텍스트에서 실행되는 워커 스레드에서 인터럽트 후반부 처리

  • 워크 : 워크큐에서 후반부 처리를 실행하는 단위

2.3.1 워커 스레드

  • 워크를 실행하고 워크큐 관련 자료구조를 업데이트 하는 커널 스레드

  • kworker/로 이름이 부여되며 워커풀의 종류에 따라 뒤에 번호가 부여됨

  • worker_thread()함수에 구현 됨

2.3.2 실행 흐름

  • 큐잉
    • schedule_work()
    • queue_work_on()
    • __queue_work_on() : 새로운 워크 큐 구조체에 접근하고 insert_work()를 호출
    • insert_work() : pool_workqueue에 지정한 워크를 큐잉
  • 워크 스레드 wake up
    • wake_up_worker()
  • 워커 스레드 실행
    • worker_thread() : struct worker_pool에서 워크를 읽어와 process_one_work()에 전달하고 호출
    • process_one_work() : 전달받은 워크를 실행

2.3.3 처리 방식

  • 인터럽트 핸들러
    1. 인터럽트가 발생한 하드웨어에 인터럽트 잘 받았다고 알림
    2. 인터럽트 처리했다는 플래그 정보 갱신
    3. 워크를 큐잉
  • 워크 핸들러
    1. 유저 공간에 인터럽트로 하드웨어 변경됨을 알림
    2. 인터럽트 처리했다는 것을 디버깅 자료구조에 넘김

2.3.3 워크 큐 자료구조

  • 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);

0개의 댓글