Armv8 Synchronization

EEEFFEE·2023년 11월 29일
0

Armv8 Architecture

목록 보기
9/15

23.11.29 최초 작성

1. Race Condition

  • 임계 영역에 대한 다수의 프로세스의 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황

1.1 Race Condition의 원인

  • SMP (Symmetric Multiprocessing) : 여러 cpu가 프로세스를 동시에 실행하기 때문에 발생

  • Preemptive Scheduling : 선점 스케줄링 실행 도중 sleep상태의 프로세스가 사용하던 공간을 수정할 경우

  • Interrupt : 실행하던 작업을 멈추고 인터럽트 핸들러를 수행 시 임계 영역 접근 시 발생

2. Spinlock

  • 누군가 스핀락 획득 시 해제할 때 까지 계속 기다림(busy wait)

  • cpu 아키텍처에 의존적(Armv8 : 디버그 컨피그 : CONFIG_DEBUG_SPINLOCK)

  • spin_lock(), spin_unlock() : 스핀락 획득 / 해제하는 함수

  • spin_lock_irq(), spin_unlock_irq() : 스핀락을 걸 때 로컬 인터럽트 비활성화/활성화 하는 함수

  • spin_lock_irqsave(), spin_unlock_irqsave() : 위 함수에서 인터럽트 상태 활성화 / 비활성화 확인하는 기능 추가

2.1 Spinlock 구조체


//	/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;

  • qspinlock() : 최대 2개의 cpu까지 스핀락을 획득할 수 있으며 3번째 부터 관련 msc_lock에 정보를 저장함

typedef struct qspinlock {
	union {
		atomic_t val;

		/*
		 * By using the whole 2nd least significant byte for the
		 * pending bit, we can allow better optimization of the lock
		 * acquisition for the pending bit holder.
		 */
#ifdef __LITTLE_ENDIAN
		struct {
			u8	locked;
			u8	pending;
		};
		struct {
			u16	locked_pending;
			u16	tail;
		};
#else
		struct {
			u16	tail;
			u16	locked_pending;
		};
		struct {
			u8	reserved[2];
			u8	pending;
			u8	locked;
		};
#endif
	};
} arch_spinlock_t;

  • queued_spin_lock(), queued_spin_unlock() : qspinlock을 획득 / 해제하는 함수

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;

	queued_spin_lock_slowpath(lock, val);
}

static __always_inline void queued_spin_unlock(struct qspinlock *lock)
{
	/*
	 * unlock() needs release semantics:
	 */
	smp_store_release(&lock->locked, 0);
}

3. Mutex

  • 각 운영체제 커널마다 다름

3.1 Mutex 구조체

  • mutex : 뮤텍스를 활용하려는 프로세스의 태스크 디스크립터 정보 저장하는 구조체

//	/source/include/linux/mutex.h

struct mutex {
	atomic_long_t		owner;				// 뮤텍스를 획득한 프로세스의 태스크 디스크립터 주소
	raw_spinlock_t		wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
	struct list_head	wait_list;			// 뮤텍스 기다리는 프로세스 정보
#ifdef CONFIG_DEBUG_MUTEXES					// 디버깅 모드 시 활성화
	void			*magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC				// 디버깅 모드 시 활성화
	struct lockdep_map	dep_map;
#endif
};

3.2 fastpath & slowpath

  • fastpath : 다른 프로세스가 뮤텍스 획득하지 않은 상태에서 바로 획득하는 루틴
  • slowpath : 다른 프로세스가 뮤텍스를 획득한 상태에서 실행되는 루틴
    • 획득하지 못한 프로세스는 대기열에 자신을 등록하고 휴면 상태 전환
    • 뮤텍스 해제하는 프로세스는 위 프로세스를 깨움

3.3 관련 함수

  • /source/kernel/locking/mutex.c
    • mutex_lock() : 뮤텍스 획득하는 함수

      (획득 시 : fastpath, 획득하지 못할 시 : slowpath)

    • __mutex_trylock_fast() : fastpath실행하는 함수

    • __mutex_lock_slowpath() : slowpath실행하는 함수
      (__mutex_lock_common() 호출)

      - __mutex_lock_common() : 프로세스가 깨어날 조건을 설정하고 schedule_preempt_disable()을 통해 sleep 상태로 들어감
    • mutex_unlock() : 뮤텍스 획득하는 함수
      (fastpath해제 시 : __mutex_trylock_fast()
      slowpath해제 시 : __mutex_unlock_slowpath())

    • __mutex_trylock_fast() : current 프로세스(curr)와 뮤텍스의 owner 비교해 같으면 해제

    • __mutex_unlock_slowpath() : wait_list의 프로세스 중 하나를 가져와 sleep상태 해제

순차성 베리어

  • 메모리 읽기 작업과 메모리 쓰기 작업이 프로그램 코드에서 지정
    한 순서대로 진행하는 루틴
  • 컴파일러와 프로세서가 순서 변경할 수 있음

0개의 댓글