이미 누군가가 스핀락을 획득했으면 스핀락을 획득할 때까지 계속 기다림
다른 프로세스가 스핀락을 해제하면 바로 스핀락을 획득하고 Critical
Section을 실행
slow path : 스핀락을 획득하기 위해서 기다리는데 실행구간이 짧아야한다.
스핀락을 써서 Critical Section을 보호하는 루틴 1
https://elixir.bootlin.com/linux/v5.4.130/source/kernel/kthread.c
01 struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
02 void *data, int node,
03 const char namefmt[],
04 va_list args)
05 {
06 DECLARE_COMPLETION_ONSTACK(done);
07 struct task_struct *task;
08 struct kthread_create_info *create = kmalloc(sizeof(*create),
09 GFP_KERNEL);
...
10 spin_lock(&kthread_create_lock); // 스핀락을 획득
11 list_add_tail(&create->list, &kthread_create_list); // Critical Section
12 spin_unlock(&kthread_create_lock); // 스핀락을 해제
13
14 wake_up_process(kthreadd_task);
스핀락을 써서 Critical Section을 보호하는 루틴 2
https://elixir.bootlin.com/linux/v5.4.130/source/drivers/rpmsg/qcom_glink_native.c
static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
{
struct glink_channel *channel;
spin_lock(&glink->idr_lock);
channel = idr_find(&glink->lcids, lcid); // 크리티컬 섹샨
spin_unlock(&glink->idr_lock);
if (!channel) {
dev_err(glink->dev, "Invalid open ack packet\n");
return -EINVAL;
}
- spinlock_t(struct spinlock): 리눅스 커널 공통
- raw_spinlock_t(struct raw_spinlock): 아키텍처 인터페이스 자료구조
- arch_spinlock_t: 아키텍처 내 자료구조
스핀락 자료구조 https://elixir.bootlin.com/linux/v5.15.30/source/include/linux/spinlock_types.h typedef struct spinlock { union { struct raw_spinlock rlock; #ifdef CONFIG_DEBUG_LOCK_ALLOC // 컨피그를 활성화 해야한다. # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) struct { u8 __padding[LOCK_PADSIZE]; struct lockdep_map dep_map; }; #endif }; } spinlock_t;
스핀락 자료구조
crash64> struct spinlock_t
typedef struct spinlock {
union {
struct raw_spinlock rlock;
};
} spinlock_t;
SIZE: 4
crash64> struct raw_spinlock
struct raw_spinlock {
arch_spinlock_t raw_lock;
}
crash64> struct arch_spinlock_t
typedef struct qspinlock {
union {
atomic_t val;
struct {
u8 locked;
u8 pending;
};
struct {
u16 locked_pending;
u16 tail;
};
};
} arch_spinlock_t;
- locked: 스핀락을 획득하면 1로 변경됨
- pending: 스핀락을 획득하기 위해 기다리는 상황에서 1로 변경됨
- tail: mcs_lock의 CPU 번호를 저장함
스핀락 자료구조
https://elixir.bootlin.com/linux/v5.15.30/source/include/asm-generic/qspinlock_types.h
typedef struct qspinlock {
union {
atomic_t val;
struct {
u16 tail;
u16 locked_pending;
};
struct {
u8 reserved[2];
u8 pending;
u8 locked; // 획득을 해제하면 0으로 바뀜,
};
};
} arch_spinlock_t;
스핀락을 걸었을 때 필드
(spinlock_t) bdi_lock = (
(struct raw_spinlock) rlock = (
(arch_spinlock_t) raw_lock = (
(atomic_t) val = (
(int) counter = 0x1),
(u8) locked = 0x1, // 1
(u8) pending = 0x0,
(u16) locked_pending = 0x1,
(u16) tail = 0x0)))
------------------------------- 해제했을 때 필드
(spinlock_t) bdi_lock = (
(struct raw_spinlock) rlock = (
(arch_spinlock_t) raw_lock = (
(atomic_t) val = (
(int) counter = 0x0),
(u8) locked = 0x0, // 0
(u8) pending = 0x0,
(u16) locked_pending = 0x0,
(u16) tail = 0x0)))
스핀락 코드 분석
https://elixir.bootlin.com/linux/v5.15.30/source/include/asm-generic/qspinlock.h
static __always_inline void queued_spin_lock(struct qspinlock *lock)
{
int val = 0;
if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)))
return; // 이미 다른 cpu코어가 스핀락읗 획득한 적이 없다면 fast path 루틴으로 이 코드만 실행, locked를 1로 바꿈 , 있다면 busy wait 동작을 무한히
queued_spin_lock_slowpath(lock, val);
}
스핀락 코드 분석
https://elixir.bootlin.com/linux/v5.15.30/source/include/asm-generic/qspinlock.h
static __always_inline void queued_spin_unlock(struct qspinlock *lock)
{
/*
* unlock() needs release semantics:
*/
smp_store_release(&lock->locked, 0);
}
상황
- dmaengine: sf-pdma: apply proper spinlock flags in sf_pdma_prep_dma_memcpy()
<출처>
https://git.kernel.org/pub/scm/linux/kernel/git/next/linuxnext.git/commit/?id=94b4cd7c5fc0dd6858a046b00ca729fb0512b9ba
dmaengine: sf-pdma: apply proper spinlock flags in sf_pdma_prep_dma_memcpy()
The second parameter of spinlock_irq[save/restore] function is flags,
which is the last input parameter of sf_pdma_prep_dma_memcpy().
So declare local variable 'iflags' to be used as the second parameter of
spinlock_irq[save/restore] function.
Signed-off-by: Austin Kim <austin.kim@lge.com>
Link: https://lore.kernel.org/r/20210611065336.GA1121@raspberrypi
Signed-off-by: Vinod Koul <vkoul@kernel.org>
패치
스핀락 업데이트 패치
diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c
index c4c4e85757643..f12606aeff87c 100644
--- a/drivers/dma/sf-pdma/sf-pdma.c
+++ b/drivers/dma/sf-pdma/sf-pdma.c
@@ -94,6 +94,7 @@ sf_pdma_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest, dma_addr_t src,
{
struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
struct sf_pdma_desc *desc;
+ unsigned long iflags;
if (chan && (!len || !dest || !src)) {
dev_err(chan->pdma->dma_dev.dev,
@@ -109,10 +110,10 @@ sf_pdma_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest, dma_addr_t src,
desc->dirn = DMA_MEM_TO_MEM;
desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
- spin_lock_irqsave(&chan->vchan.lock,flags); // 원래 코드
+ spin_lock_irqsave(&chan->vchan.lock, iflags); //
chan->desc = desc;
sf_pdma_fill_desc(desc, dest, src, len);
- spin_unlock_irqrestore(&chan->vchan.lock,flags);
+ spin_unlock_irqrestore(&chan->vchan.lock, iflags);
return desc->async_tx;
}
spinlock_fastpa-985 [002] .... 471.920711: _raw_spin_lock+0x4/0x78 <-ksys_dup3+0x60/0x140 // 스핀락이 컴파일되면 raw spin lock이라는 심볼로 보임
spinlock_fastpa-985 [002] .... 471.920715: <stack trace>
=> _raw_spin_lock+0x8/0x78
=> ksys_dup3+0x60/0x140
=> __arm64_sys_dup3+0x28/0x38
=> el0_svc_common.constprop.2+0x9c/0x1a0
=> do_el0_svc+0x2c/0x98
=> el0_svc+0x20/0x30
=> el0_sync_handler+0x90/0xb8
=> el0_sync+0x160/0x180
spinlock_fastpa-985 [002] ...1 471.920719: _raw_spin_unlock+0x4/0x48 <-do_dup2+0xe8/0x148
spinlock_fastpa-985 [002] ...1 471.920720: <stack trace>
=> _raw_spin_unlock+0x8/0x48
=> do_dup2+0xe8/0x148
=> ksys_dup3+0xbc/0x140
=> __arm64_sys_dup3+0x28/0x38
=> el0_svc_common.constprop.2+0x9c/0x1a0
=> do_el0_svc+0x2c/0x98
=> el0_svc+0x20/0x30
=> el0_sync_handler+0x90/0xb8
=> el0_sync+0x160/0x180