Home | Info | Research | Blog | Repos | Messages | Contact Me

 


'리눅스 커널'에 해당되는 글 2건

  1. 2008/09/15 리눅스 커널 Atomic 함수 Visual C++ 버전
  2. 2008/09/07 리눅스 커널 비트 연산 함수 Visual C++ 버전

리눅스 커널의 include/asm-i386/atomic.h의 주요 함수를 Visual C++에서 컴파일 할 수 있도록 만들어 보았습니다.

Atomic 함수는 멀티 쓰레드, 멀티 프로세서 환경에서도 안전하게 변수의 값을 연산할 수 있도록 해주는 함수입니다. 이런 기능을 Atomic 연산이라고 합니다.

리눅스 및 유닉스 계열에서는 Atomic 함수라고 하고, 윈도우에서는 Interlocked 함수라고 합니다.

#ifndef __ARCH_I386_ATOMIC__
#define __ARCH_I386_ATOMIC__

#include <linux/compiler.h>
#include <asm/processor.h>

/*
 * Atomic operations that C can't guarantee us.  Useful for
 * resource counting etc..
 */


/*
 * Make sure gcc doesn't try to be clever and move things around
 * on us. We need to use _exactly_ the address the user gave us,
 * not some alias that contains the same information.
 */

typedef struct { int counter; } atomic_t;

#define ATOMIC_INIT(i)    (i)

/**
 * atomic_read - read atomic variable
 * @v: pointer of type atomic_t
 *
 * Atomically reads the value of @v.
 */

#define atomic_read(v)        ((v)->counter)

/**
 * atomic_set - set atomic variable
 * @v: pointer of type atomic_t
 * @i: required value
 *
 * Atomically sets the value of @v to @i.
 */

#define atomic_set(v,i)        (((v)->counter) = (i))

/**
 * atomic_add - add integer to atomic variable
 * @i: integer value to add
 * @v: pointer of type atomic_t
 *
 * Atomically adds @i to @v.
 */

static __inline__ void atomic_add(int i, atomic_t *v)
{
    __asm
    {
        mov eax, v
        mov ecx, i
        lock add dword ptr [eax], ecx
    }
}

/**
 * atomic_sub - subtract the atomic variable
 * @i: integer value to subtract
 * @v: pointer of type atomic_t
 *
 * Atomically subtracts @i from @v.
 */

static __inline__ void atomic_sub(int i, atomic_t *v)
{
    __asm
    {
        mov eax, v
        mov ecx, i
        lock sub dword ptr [eax], ecx
    }
}

/**
 * atomic_sub_and_test - subtract value from variable and test result
 * @i: integer value to subtract
 * @v: pointer of type atomic_t
 *
 * Atomically subtracts @i from @v and returns
 * true if the result is zero, or false for all
 * other cases.
 */

static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
{
    unsigned char c;

    __asm
    {
        mov eax, v
        mov ecx, i
        lock sub dword ptr [eax], ecx
        sete c
    }
    return c;
}

/**
 * atomic_inc - increment atomic variable
 * @v: pointer of type atomic_t
 *
 * Atomically increments @v by 1.
 */

static __inline__ void atomic_inc(atomic_t *v)
{
    __asm
    {
        mov eax, v
        lock inc dword ptr [eax]
    }
}

/**
 * atomic_dec - decrement atomic variable
 * @v: pointer of type atomic_t
 *
 * Atomically decrements @v by 1.
 */

static __inline__ void atomic_dec(atomic_t *v)
{
    __asm
    {
        mov eax, v
        lock dec dword ptr [eax]
    }
}

/**
 * atomic_dec_and_test - decrement and test
 * @v: pointer of type atomic_t
 *
 * Atomically decrements @v by 1 and
 * returns true if the result is 0, or false for all other
 * cases.
 */

static __inline__ int atomic_dec_and_test(atomic_t *v)
{
    unsigned char c;

    __asm
    {
        mov eax, v
        lock dec dword ptr [eax]
        sete c
    }
    return c != 0;
}

/**
 * atomic_inc_and_test - increment and test
 * @v: pointer of type atomic_t
 *
 * Atomically increments @v by 1
 * and returns true if the result is zero, or false for all
 * other cases.
 */

static __inline__ int atomic_inc_and_test(atomic_t *v)
{
    unsigned char c;

    __asm
    {
        mov eax, v
        lock inc dword ptr [eax]
        sete c
    }
    return c != 0;
}

/**
 * atomic_add_negative - add and test if negative
 * @v: pointer of type atomic_t
 * @i: integer value to add
 *
 * Atomically adds @i to @v and returns true
 * if the result is negative, or false when
 * result is greater than or equal to zero.
 */

static __inline__ int atomic_add_negative(int i, atomic_t *v)
{
    unsigned char c;

    __asm
    {
        mov eax, v
        mov ecx, i
        lock add dword ptr [eax], ecx
        sets c
    }
    return c;
}

/**
 * atomic_add_return - add and return
 * @v: pointer of type atomic_t
 * @i: integer value to add
 *
 * Atomically adds @i to @v and returns @i + @v
 */

static __inline__ int atomic_add_return(int i, atomic_t *v)
{
    int __i;
#ifdef CONFIG_M386
    unsigned long flags;
    if(unlikely(boot_cpu_data.x86==3))
        goto no_xadd;
#endif
    /* Modern 486+ processor */
    __i = i;
    __asm
    {
        mov eax, v
        mov ecx, i
        lock xadd dword ptr [eax], ecx
        mov i, ecx
    }
    return i + __i;

#ifdef CONFIG_M386
no_xadd: /* Legacy 386 processor */
    local_irq_save(flags);
    __i = atomic_read(v);
    atomic_set(v, i + __i);
    local_irq_restore(flags);
    return i + __i;
#endif
}

static __inline__ int atomic_sub_return(int i, atomic_t *v)
{
    return atomic_add_return(-i,v);
}

#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
#define atomic_xchg(v, new) (xchg(&((v)->counter), new))

/**
 * atomic_add_unless - add unless the number is already a given value
 * @v: pointer of type atomic_t
 * @a: the amount to add to v...
 * @u: ...unless v is equal to u.
 *
 * Atomically adds @a to @v, so long as @v was not already @u.
 * Returns non-zero if @v was not @u, and zero otherwise.
 */

#define atomic_add_unless(v, a, u)                \
({                                \
    int c, old;                        \
    c = atomic_read(v);                    \
    for (;;) {                        \
        if (unlikely(c == (u)))                \
            break;                    \
        old = atomic_cmpxchg((v), c, c + (a));        \
        if (likely(old == c))                \
            break;                    \
        c = old;                    \
    }                            \
    c != (u);                        \
})
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)

#define atomic_inc_return(v)  (atomic_add_return(1,v))
#define atomic_dec_return(v)  (atomic_sub_return(1,v))

/* Atomic operations are already serializing on x86 */
#define smp_mb__before_atomic_dec()    barrier()
#define smp_mb__after_atomic_dec()    barrier()
#define smp_mb__before_atomic_inc()    barrier()
#define smp_mb__after_atomic_inc()    barrier()

#include <asm-generic/atomic.h>
#endif




리눅스 커널의 include/asm-x86/bitops.h의 주요 함수를 Visual C++에서 컴파일 할 수 있도록 만들어 보았습니다.

#ifndef _I386_BITOPS_H
#define _I386_BITOPS_H

/*
 * Copyright 1992, Linus Torvalds.
 * Copyright 2008, Lee Jae-Hong (pyrasis)
 */


#include <linux/compiler.h>

#define inline __inline

/**
 * set_bit - Atomically set a bit in memory
 * @nr: the bit to set
 * @addr: the address to start counting from
 *
 * This function is atomic and may not be reordered.  See __set_bit()
 * if you do not require the atomic guarantees.
 *
 * Note: there are no guarantees that this function will not be reordered
 * on non x86 architectures, so if you are writting portable code,
 * make sure not to rely on its reordering guarantees.
 *
 * Note that @nr may be almost arbitrarily large; this function is not
 * restricted to acting on a single-word quantity.
 */

static inline void set_bit(int nr, volatile unsigned long * addr)
{
    __asm
    {
        mov eax, addr
        mov ecx, nr
        lock bts dword ptr [eax], ecx
    }
}

/**
 * clear_bit - Clears a bit in memory
 * @nr: Bit to clear
 * @addr: Address to start counting from
 *
 * clear_bit() is atomic and may not be reordered.  However, it does
 * not contain a memory barrier, so if it is used for locking purposes,
 * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
 * in order to ensure changes are visible on other processors.
 */

static inline void clear_bit(int nr, volatile unsigned long * addr)
{
    __asm
    {
        mov eax, addr
        mov ecx, nr
        lock btr dword ptr [eax], ecx
    }
}

/**
 * change_bit - Toggle a bit in memory
 * @nr: Bit to change
 * @addr: Address to start counting from
 *
 * change_bit() is atomic and may not be reordered. It may be
 * reordered on other architectures than x86.
 * Note that @nr may be almost arbitrarily large; this function is not
 * restricted to acting on a single-word quantity.
 */

static inline void change_bit(int nr, volatile unsigned long * addr)
{
    __asm
    {
        mov eax, addr
        mov ecx, nr
        lock btc dword ptr [eax], ecx
    }
}

/**
 * test_and_set_bit - Set a bit and return its old value
 * @nr: Bit to set
 * @addr: Address to count from
 *
 * This operation is atomic and cannot be reordered. 
 * It may be reordered on other architectures than x86.
 * It also implies a memory barrier.
 */

static inline int test_and_set_bit(int nr, volatile unsigned long * addr)
{
    int oldbit;

    __asm
    {
        mov edx, addr
        mov eax, nr
        lock bts dword ptr [edx], eax
        sbb eax, eax
        mov oldbit, eax
    }
    return oldbit;
}

/**
 * test_and_clear_bit - Clear a bit and return its old value
 * @nr: Bit to clear
 * @addr: Address to count from
 *
 * This operation is atomic and cannot be reordered.
 * It can be reorderdered on other architectures other than x86.
 * It also implies a memory barrier.
 */

static inline int test_and_clear_bit(int nr, volatile unsigned long * addr)
{
    int oldbit;

    __asm
    {
        mov edx, addr
        mov eax, nr
        lock btr dword ptr [edx], eax
        sbb eax, eax
        mov oldbit, eax
    }
    return oldbit;
}

/**
 * test_and_change_bit - Change a bit and return its old value
 * @nr: Bit to change
 * @addr: Address to count from
 *
 * This operation is atomic and cannot be reordered. 
 * It also implies a memory barrier.
 */

static inline int test_and_change_bit(int nr, volatile unsigned long* addr)
{
    int oldbit;

    __asm
    {
        mov edx, addr
        mov eax, nr
        lock btc dword ptr [edx], eax
        sbb eax, eax
        mov oldbit, eax
    }
    return oldbit;
}

/**
 * __ffs - find first bit in word.
 * @_word: The word to search
 *
 * Undefined if no bit exists, so code should check against 0 first.
 */

static inline unsigned long __ffs(unsigned long _word)
{
    __asm
    {
        bsf eax, [_word]
        mov _word, eax
    }
    return _word;
}

/**
 * find_first_bit - find the first set bit in a memory region
 * @addr: The address to start the search at
 * @size: The maximum size to search
 *
 * Returns the bit-number of the first set bit, not the number of the byte
 * containing a bit.
 */

static inline unsigned find_first_bit(const unsigned long *addr, unsigned size)
{
    unsigned x = 0;

    while (x < size) {
        unsigned long val = *addr++;
        if (val)
            return __ffs(val) + x;
        x += (sizeof(*addr)<<3);
    }
    return x;
}

/**
 * find_next_bit - find the first set bit in a memory region
 * @addr: The address to base the search on
 * @offset: The bitnumber to start searching at
 * @size: The maximum size to search
 */

int find_next_bit(const unsigned long *addr, int size, int offset);

/**
 * ffz - find first zero in word.
 * @word: The word to search
 *
 * Undefined if no zero exists, so code should check against ~0UL first.
 */

static inline unsigned long ffz(unsigned long word)
{
    int x = ~word;

    __asm
    {
        bsf eax, [x]
    }
}

/**
 * ffs - find first bit set
 * @x: the word to search
 *
 * This is defined the same way as
 * the libc and compiler builtin ffs routines, therefore
 * differs in spirit from the above ffz() (man ffs).
 */

static inline int ffs(int x)
{
    __asm
    {
        bsf eax, [x]
        jnz L1
        mov eax, -1
    L1:
        inc eax
    }
}

#endif /* _I386_BITOPS_H */