일반적인 패킷 처리는 SoftIRQ......2ms 이나 10번의 시도 이후 ksoftirqd thread로 처리, 이때는 일반적인 스케줄링 가능
-> 근데 왜 굳이 workqueue를 사용하는가?
ksoftirqd 에서, __do_softirq는 Real-Time thread에 의해 스케줄링은 가능하지만, sleep/lock은 불가능(deadlock을 방지하기 위함, 유저 프로세스의 lock을 필요로 하는 인터럽트에서 lock waiting하게 되면 ...deadlock)
run_softirqd 함수에서 다음 함수를 호출함.

이때 preempt_count_add를 통해 SOFTIRQ_OFFSET을 thread_info의 preempt_count에 더한다.
-> in_softirq() 에서 true를 반환하게 된다.
mutex_lock/msleep 사용시 __might_resched 함수를 호출하면서, in_softirq를 체크하는데,

이떄 SOFTIRQ_OFFSET이 켜져있으면 return되고, 아래에서 pr_err, error.
따라서 일반 프로세스로서 실행되는 workqueue에서 sleep 작업을 수행하는것.

zero-copy 를 위한 것.
여기서 workqueue의 역할?
문제점: 메모리의 비동기.
devmem TCP 소켓이 닫히며 GPU메모리 주소를 가지고 있는 memory(page pool, 네트워크 고속 처리를 위한 메모리 기법)를 free하려고 할 때, 유저 프로세스나 GPU 내부 드라이버가 해당 메모리 여전히 참조(락 또는 카운트 유지)하고 있을 가능성이 매우 높다.
해결책 (Workqueue의 역할):
GPU 메모리 최종 해제 작업을 Workqueue에 Work로 던져버리고 탈출한다.
이후 백그라운드의 kworker 스레드 실행

1초 지연 스케줄링 work를 위함. 비동기를 확인하기전에 1초동안의 유예시간을 주는것.

release_dw라는 work structure에 page_pool_release_retry 함수 등록

유저나 GPU 드라이버가 사용하고 있는(in-flight) page pool이 0이하일 경우, page pool를 release한 다음, return 한다.
그렇지 않으면 다시 1초 지연 워크큐 스케줄링.
