tcpdump 와 tcpreplay를 이용한 패킷 릴레이

iptables를 이용한 패킷 릴레이의 경우 
app 레벨에서 해당 패킷이 필요한 경우에는 사용할 수 없다는 단점이 존재한다.

패킷이 필요한 app에서 릴레이를 해주는 것이 가장 좋으나
개발이 필요하기에 시간이 필요하다는 단점이 존재한다.

tcpdump와 tcpreplay를 이용한 패킷 릴레이의 경우 
두가지 문제를 모두 해결 할 수 있지만 이 또한 실시간 릴레이가 필요한 경우에는
시간차가 존재한다.

다만 일정 시간(1분,10분,1시간)단위로 패킷을 모아서 전송해도 무방한 경우에는 
유용하게 사용할 수 있다.



리눅스에서의 패킷 릴레이를 위한 tcpdump  tcpreplay활용

 

1.     사용 환경

1)    전송 장비 : Cisco Switch 6506

2)    릴레이 서버 : CentOS 5.5 32bit (이하 A)

3)    리시브 서버 : CentOS 4.7 32bit (이하 B)

 

2.     사용 툴

1)    Iptables: 방화벽의 용도가 아닌 패킷 릴레이를 위한 용도로 사용

2)    Tcpdump: 릴레이 서버가 받는 패킷을 캡쳐하기 위한 툴

3)    Tcpreplay: 받아들인 패킷을 재 전송하기 위한 툴

4)    Tcprewrite: tcpreplay에 포함되어 있는 툴로써 패킷의 dst,dmac 등을 수정하기 위한 툴

 

3.     릴레이 시나리오

1)    CiscoSwitch에서 A로의 snmpTrap 전송

2)    A에서 해당하는 패킷을 tcpdump로 캡쳐

3)    tcprewrite를 이용해 패킷 수정

4)    tcpreplay를 이용해 재전송

5)    B에서 확인

 

4.     테스트

1)    A에서 tcpdump로 받은 패킷 (수정전 원본 패킷)

2)    Tcprewrite로 수정 후

 

 

3)    Tcpreplay로 전송 후 B에서 캡쳐한 패킷


 

A에서 받은 패킷과 수정 후 패킷이 동일함은 Time으로 확인할 수 있으며

스크린샷 상에서는 확인이 불가하지만 Source mac주소도 동일함을 확인할 수 있다.

 

5.     Tcpdump사용법

tcpdump -nn -i eth0 dst port 162 -wsample.pcap -s 1000

> eth0으로들어오는 패킷 중 dst port 162인 패킷을 1000byte까지의 사이즈로 sample.pcap으로 수집하라

상세한 내용은 man page를 참조하세요.

 

 

6.     Tcprewrite사용법

tcprewrite -i ./sample.pcap -o./sample_rewrite.pcap --dstipmap [수정될IP]:[수정할IP] --portmap [수정될port]:[수정할port] --enet-dmac=[목적지 mac주소] -C -v

> sample.pcap파일의 내용을 dstip,dmac,port를 변경 후  sample_rewrite.pcap으로 저장

 

옵션

tcprewrite(tcprewrite) - Rewrite the packets in a pcap file.

USAGE:  tcprewrite [ -<flag> [<val>] |--<name>[{=| }<val>] ]...

 

   -r, --portmap=str          Rewrite TCP/UDP ports

   -s, --seed=num             Randomize src/dst IPv4/v6addresses w/ given seed

   -N, --pnat=str             Rewrite IPv4/v6 addresses usingpseudo-NAT

   -S, --srcipmap=str         Rewrite source IPv4/v6 addresses usingpseudo-NAT

   -D, --dstipmap=str         Rewrite destination IPv4/v6 addressesusing pseudo                                -NAT

   -e, --endpoints=str        Rewrite IP addresses to be between twoendpoints

   -b, --skipbroadcast        Skip rewriting broadcast/multicastIPv4/v6 address                                es

   -C, --fixcsum              Force recalculation ofIPv4/TCP/UDP header checksu                                 ms

   -m, --mtu=num              Override default MTU length (1500bytes)

       --mtu-trunc            Truncate packets larger thenspecified MTU

   -E, --efcs                 Remove Ethernet checksums(FCS) from end of frames

       --ttl=str              Modify the IPv4/v6 TTL/Hop Limit

       --tos=num              Set the IPv4 TOS/DiffServ/ECN byte

       --tclass=num           Set the IPv6 Traffic Class byte

       --flowlabel=num        Set the IPv6 Flow Label

   -F, --fixlen=str           Pad or truncate packet data to matchheader length

       --skipl2broadcast      Skip rewriting broadcast/multicast Layer2 address                                es

       --dlt=str              Override output DLT encapsulation

       --enet-dmac=str        Override destination ethernet MACaddresses

       --enet-smac=str        Override source ethernet MAC addresses

       --enet-vlan=str        Specify ethernet 802.1q VLAN tag mode

       --enet-vlan-tag=num    Specify the new ethernet 802.1q VLAN tagvalue

       --enet-vlan-cfi=num    Specify the ethernet 802.1q VLAN CFI value

       --enet-vlan-pri=num    Specify the ethernet 802.1q VLAN priority

       --hdlc-control=num     Specify HDLC control value

       --hdlc-address=num     Specify HDLC address

       --user-dlt=num         Set output file DLT type

       --user-dlink=str       Rewrite Data-Link layer with userspecified data

   -i, --infile=str           Input pcap file to be processed

   -o, --outfile=str          Output pcap file

   -c, --cachefile=str        Split traffic via tcpprep cache file

   -v,--verbose              Print decodedpackets via tcpdump to STDOUT

   -A, --decode=str           Arguments passed to tcpdump decoder

       --fragroute=str        Parse fragroute configuration file

       --fragdir=str          Which flows to apply fragroute to:c2s, s2c, both

       --skip-soft-errors     Skip writing packets with soft errors

   -V, --version              Print version information

   -h, --less-help            Display less usage information andexit

   -H, --help                 Display usage information andexit

   -!, --more-help            Extended usage information passedthru pager

   -*, --save-opts[=arg]      Save the option state to a config file

   -+,--load-opts=str        Load options froma config file

 

7.     Tcpreplay

tcpreplay --topspeed --intf1=eth0./sample_rewrite.pcap

sample_rewrite.pcap 파일을 최고의 속력으로 eth0을 통해 전송

 

tcpreplay(tcpreplay) - Replay network traffic stored in pcap files

USAGE:  tcpreplay [ -<flag> [<val>] | --<name>[{=|}<val>] ]... <pcap_file(s)>

 

   -q, --quiet                Quiet mode

   -T, --timer=str            Select packet timing mode: select,ioport, rdtsc, gtod, nano, abstime

       --sleep-accel=num      Reduce the amount of time to sleep byspecified usec

       --rdtsc-clicks=num     Specify the RDTSC clicks/usec

   -v, --verbose              Print decoded packets via tcpdumpto STDOUT

   -A, --decode=str           Arguments passed to tcpdump decoder

   -K, --enable-file-cache    Enable caching of packets to internalmemory

   -c, --cachefile=str        Split traffic via a tcpprep cache file

   -i, --intf1=str            Server/primary traffic outputinterface

   -I, --intf2=str            Client/secondary traffic outputinterface

       --listnics             List available network interfacesand exit

   -l, --loop=num             Loop through the capture file Xtimes

       --pktlen               Override the snaplen and use theactual packet len

   -L, --limit=num            Limit the number of packets to send

   -x, --multiplier=str       Modify replay speed to a given multiple

   -p, --pps=num              Replay packets at a givenpackets/sec

   -M, --mbps=str             Replay packets at a given Mbps

   -t, --topspeed             Replay packets as fast as possible

   -o, --oneatatime           Replay one packet at a time for eachuser input

       --pps-multi=num        Number of packets to send for each timeinterval

   -P, --pid                  Print the PID of tcpreplay atstartup

   -V, --version              Print version information

   -h, --less-help            Display less usage information andexit

   -H, --help                 Display usage information andexit

   -!, --more-help            Extended usage information passedthru pager

       --save-opts[=arg]      Save the option state to a config file

 

      --load-opts=str        Loadoptions from a config file

c출처 :  

http://mrain7.egloos.com/2839320 

'Developer's Infos > Network Programming' 카테고리의 다른 글

uint32_t을 IP로 표현하기  (0) 2011.09.15
Posted by 삼성동고양이
/*
* 파일시스템 정보 얻어오기
* /proc/mounts 정보와 statfs를 이용한다. 
*/
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include <sys/types.h>

const char *MMOUNT = "/proc/mounts";

struct f_size
{
    long blocks;
    long avail; 
};

typedef struct _mountinfo 
{
    FILE *fp;                // 파일 스트림 포인터    
    char devname[80];        // 장치 이름
    char mountdir[80];        // 마운트 디렉토리 이름
    char fstype[12];        // 파일 시스템 타입
    struct f_size size;        // 파일 시스템의 총크기/사용율 
} MOUNTP;

MOUNTP *dfopen()
{
    MOUNTP *MP;

    // /proc/mounts 파일을 연다.
    MP = (MOUNTP *)malloc(sizeof(MOUNTP));
    if(!(MP->fp = fopen(MMOUNT, "r")))
    {
        return NULL;
    }
    else
        return MP;
}

MOUNTP *dfget(MOUNTP *MP)
{
    char buf[256];
    char *bname;
    char null[16];
    struct statfs lstatfs;
    struct stat lstat; 
    int is_root = 0;

    // /proc/mounts로 부터 마운트된 파티션의 정보를 얻어온다.
    while(fgets(buf, 255, MP->fp))
    {
        is_root = 0;
        sscanf(buf, "%s%s%s",MP->devname, MP->mountdir, MP->fstype);
         if (strcmp(MP->mountdir,"/") == 0) is_root=1;
        if (stat(MP->devname, &lstat) == 0 || is_root)
        {
            if (strstr(buf, MP->mountdir) && S_ISBLK(lstat.st_mode) || is_root)
            {
                // 파일시스템의 총 할당된 크기와 사용량을 구한다.        
                statfs(MP->mountdir, &lstatfs);
                MP->size.blocks = lstatfs.f_blocks * (lstatfs.f_bsize/1024); 
                MP->size.avail  = lstatfs.f_bavail * (lstatfs.f_bsize/1024); 
                return MP;
            }
        }
    }
    rewind(MP->fp);
    return NULL;
}

int dfclose(MOUNTP *MP)
{
    fclose(MP->fp);
}

int main()
{
    MOUNTP *MP;
    if ((MP=dfopen()) == NULL)
    {
        perror("error");
        return 1;
    }

    while(1)
    {
        while(dfget(MP))
        {
            printf("%-14s%-20s%10lu%10lu\n", MP->mountdir, MP->devname, 
                                MP->size.blocks,
                                MP->size.avail);
        }
        printf("=========================\n\n");
        sleep(1);
    }
}

[출처] df 소스|작성자 포도조아


'Developer's Infos > Linux Programming' 카테고리의 다른 글

PCI 드라이버(PCI-Driver)  (0) 2011.10.12
pci-driver  (0) 2011.10.10
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
Posted by 삼성동고양이

1 PCI 드라이버의 구조 


2가지 종류의 PCI 드라이버가 존재한다: 신식 (new-style) 드라이버는 장치를 검색 (probe) 하는 대부분의 과정을 PCI 레이어에게 넘겨주었고 장치의 온라인 삽입과 제거를 지원한다. (즉 PCI, hot-pluggable PCI, CardBus 를 하나의 드라이버로 지원한다) 다른 하나는 구식 (old-style) 드라이버인데 모든 장치 검색 과정을 스스로 처리한다. 만약 꼭 그래야 할 이유가 없다면, 새로 작성하는 코드에서 구식의 방식을 이용하여 장치 검색을 수행하지 않도록 하자. 장치가 검색되고 나면 드라이버는 (구식이던 신식이던 간에) 장치를 동작시키고 싶어한다. 그러기 위해서는 다음 과정이 필요하다:

  • 장치를 활성화한다
  • 장치의 설정 공간에 접근한다
  • 장치가 제공하는 자원 (주소와 IRQ 번호) 를 알아낸다
  • 이 자원들을 할당한다
  • 장치와 통신한다 

이들은 대부분 다음 절에서 설명한다. 나머지 부분은 주석이 잘 달려있는 <linux/pci.h> 코드를 보게 될 것이다.

만약 PCI subsystem 이 설정되지 않았다면 (CONFIG_PCI 가 n 값을 가짐) 다음에 소개하는 대부분의 함수들은 내용이 없는 인라인 함수 혹은 드라이버 상의 많은 ifdef 들을 피하기 위한 적절한 에러코드를 리턴하는 함수로 정의될 것이다.

2 신식 (New-style) 드라이버 


신식 드라이버는 초기화 과정에서 단지 다음과 같은 멤버를 포함하는 드라이버를 표현하기 위한 구조체 (struct pci_driver) 의 포인터를 인수로 pci_register_driver 함수를 호출한다.

name 드라이버 이름
id_table 드라이버가 처리하는 디바이스 ID 의 테이블의 포인터. 대부분의 드라이버에서는 이 테이블을 MODULE_DEVICE_TABLE(pci,...) 매크로를 이용해서 공개 (export) 해야 한다. 시스템이 알고있는 모든 PCI 장치들의 probe() 함수를 호출할 때는 NULL 로 설정하라..?
probe ID 테이블과 매치되고 아직 다른 드라이버에 의해 처리되지 않은 모든 장치들에 대한 장치 검색 함수의 포인터 (기존에 존재하던 장치나 이후에 새롭게 추가된 장치에 대하여 pci_register_driver 함수가 실행되는 과정에서 호출됨). 이 함수는 장치를 나타내는 pci_dev 구조체의 포인터와 장치와 매치되는 ID 테이블의 엔트리의 포인터를 인자로 받는다. 장치가 드라이버를 수락하면 (accepted) 0 을 반환하고 그렇지 않으면 (음수값인) 에러 코드를 반환한다. 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
remove 이 드라이버에 의해 처리되던 장치가 제거될 때에 호출되는 함수의 포인터 (드라이버의 등록해제 (deregistration) 과정이나 hot-pluggable 슬롯에서 기계적으로 제거한 경우에 호출됨). 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
save_state 장치가 대기 모드 (suspend) 로 들어가기 전에 상태 정보를 저장한다.
suspend 장치를 절전 상태 (low-power state) 로 만든다.
resume 장치를 절전 상태에서 깨어나게 한다
enable_wake wake event 를 만들어서 장치를 절전 상태로부터 활성화 시킨다.
(PCI 전원 관리와 그에 관련된 함수들은 Documentation/power/pci.txt 를 참고하기 바란다)

ID 테이블은 마지막이 모두 0 인 엔트리로 끝나는 struct pci_device_id 의 배열이다. 이 구조체는 다음과 같은 멤버들을 가진다:

vendor, device 매치시킬 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
subvendor, subdevice 매치시킬 subsystem 의 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
class, class_mask 매치시킬 장치의 클래스. class_mask 는 비교시 어떤 비트들을 검사해야 하는지를 나타냄
driver_data 드라이버마다 자체적으로 사용하는 데이터
대부분의 드라이버에서는 driver_data 를 사용할 필요가 없다. driver_data 를 사용하는 가장 좋은 예로는 동일한 장치 타입의 정적인 리스트의 인덱스로 사용하는 것이다 (포인터로 사용하는 것이 아니다).

시스템이 알고있는 모든 PCI 장치에게 probe() 가 호출되도록 하려면 테이블의 엔트리를 {PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID} 로 만들면 된다.

/sys/bus/pci/drivers/{driver}/new_id 파일에 쓰는 것으로 런타임에 디바이스 드라이버에게 새로운 PCI ID 가 추가될 수 있다. 일단 추가되면, 드라이버는 이것을 지원하는 모든 장치를 검색 (probe) 할 것이다.

echo "vendor device subvendor subdevice class class_mask driver_data" > \ 
   /sys/bus/pci/drivers/{driver}/new_id 
  


(위의 모든 필드들은 (0x 을 생략한 형태의) 16 진수로 넘겨진다.)

사용자는 단지 필요한 필드만을 넘길 필요가 있다; vendor, device, subvendor, subdevice 필드는 기본값으로 PCI_ANY_ID (FFFFFFFF) 를 가지고 class 와 classmask 는 0 을, driver_data 필드는 OUL 을 각각 기본값으로 가진다. 디바이스 드라이버는 인자로 넘겨진 driver_data 필드를 위해서 다음 함수를 반드시 호출해야 한다.

pci_dynids_set_use_driver_data(pci_driver *, 1) 
  


그렇지 않으면 해당 필드로는 단지 0 이 넘겨진다.

드라이버가 제거될 (exit) 때는, 단지 pci_unregister_driver() 함수를 호출하고 PCI 레이어는 자동적으로 이 드라이버에게 처리되고 있던 모든 장치에 대해서 remove hook 함수를 호출한다.

초기화 (initialization) 함수와 제거 (clean-up) 함수는 (<linux/init.h> 에 정의된 매크로 들을 이용하여) 적절히 표시해 주기 바란다:

__init 초기화 코드. 드라이버가 초기화 된 이후에는 사라진다.
__exit 종료 코드. 모듈로 컴파일 되지 않은 드라이버에서는 무시된다.
__devinit 장치 초기화 코드. 커널이 CONFIG_HOTPLUG 가 설정되지 않은 상태로 컴파일 되었다면 __init 와 동일하다. 그렇지 않으면 일반 함수이다.
devexit exit 와 동일하다.
팁: 

module_init()/module_exit() 함수들 (그리고 오직 여기서만 호출되는 모든 초기화 함수들) 은 __init/exit 로 표시 (mark) 되어야 한다. struct pci_driver 는 이러한 태그들로 표시되면 안된다. ID 테이블 배열은 __devinitdata 로 표시되어야 한다. probe() 와 remove() 함수들 (그리고 오직 여기서만 호출되는 모든 초기화 함수들) 은 __devinit/exit 로 표시되어야 한다. 만약 드라이버가 hotplug 를 지원하는 드라이버가 아니라고 확신한다면 그냥 __init/exit 와 __initdata/exitdata 만을 사용하라.

__devexit 로 표시된 함수에 대한 포인터는 반드시 __devexit_p(함수이름) 을 사용하여 생성되어야 한다. 이 매크로는 함수이름을 생성하거나 __devexit 로 표시된 함수가 버려질 (discard) 경우 NULL 을 반환한다.

3 수동으로 PCI 장치를 검색하는 방법 (구식 드라이버) 


pci_register_driver() 인터페이스를 이용하지 않는 PCI 드라이버의 경우에는 다음과 같은 방식으로 수동으로 (manually) PCI 장치를 검색할 수 있다:

제품 공급자와 장치 ID 검색:

struct pci_dev *dev = NULL; 
while (dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev)) 
    configure_device(dev); 
  


클래스 ID 검색 (동일한 방식으로 루프를 이용):

    pci_find_class(CLASS_ID, dev) 
  


제품 공급자/장치 ID 및 subsystem 의 제품 공급자/장치 ID 검색:

    pci_find_subsys(VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev) 
  


VENDOR_ID 나 DEVICE_ID 위치에는 와일드 카드로 PCI_ANY_ID 라는 상수를 이용할 수 있다. 이 경우에는 예를 들어 '특정 공급자가 제공하는 모든 장치' 와 같은 검색이 가능하게 된다.

이 함수들은 hotplug 에 대해서 안전하지 않다는 (not hotplug-safe) 것을 주의하기 바란다. 위의 각 함수들에 대해서 hotplug 를 안전하게 지원하는 버전은 pci_get_device(), pci_get_class(), pci_get_subsys() 이다. 이 함수들은 자신이 반환하는 장치에 해당하는 pci_dev 구조체의 참조 횟수 (reference count) 를 증가시킨다. 해당 장치를 사용하지 않을 경우에는 (아마도 모듈 언로드시) pci_dev_put() 함수를 이용하여 반드시 이 장치의 참조 횟수를 감소시켜야 한다.

4 장치 활성화하기 


검색한 장치를 이용해서 어떤 작업을 수행하기 전에, pci_enable_device() 함수를 이용해 장치를 활성화 시킬 필요가 있다. 이 함수는 I/O 와 메모리 영역을 활성화시키고, 필요한 경우 잃은 자원들을 할당하며, 장치가 절전 상태에 있었던 경우에는 깨어나도록 한다. 이 함수가 실패할 수도 있다는 사실에 주의하기 바란다.

만약 장치를 buf mastering mode 로 사용하고 싶은 경우에는, pci_set_master() 함수를 호출해서 PCI_COMMAND 레지스터의 bus master 비트를 활성화하고, BIOS 에 의해 어떤 bogus 로 설정된 경우?? 레이턴시 타이머 값을 수정한다.

만약 PCI Memory-Write-Invalidate 트랜잭션을 사용하고 싶은 경우에는 pci_set_mwi() 함수를 호출하라. 이 함수는 Mem-Wr-Inval 의 PCI_COMMAND 비트를 활성화하고, cache inline size 레지스터가 적절하게 설정되도록 해준다. 모든 아키텍처에서 Memory-Write-Invalidate 를 지원하지는 않기 때문에, pci_set_mwi() 의 리턴값을 체크해야 한다.

5 PCI 설정 공간에 접근하는 방법 


struct pci_dev * 로 표현되는 장치의 설정 공간에 접근하기 위해서 pci_(read/write)_config_(byte|word|dword) 함수를 사용할 수 있다. 이 함수들은 모두 성공인 경우에 0 을 리턴하고, 실패인 경우 pcibios_strerror 에 의해 문자열로 변환될 수 있는 에러 코드 (PCIBIOS_... 의 형태) 를 리턴한다. 대부분의 드라이버에서 올바른 PCI 장치에 접근하는 경우에는 실패하지 않을 것이다.

만약 가능한 struct pci_dev 가 없는 경우에는 pci_bus(read|write)_config(byte|word|dword) 함수를 이용해서 해당 버스상의 장치와 function ? 에 접근할 수 있다.

만약 config header 의 standard portion 의 필드에 접근하는 경우에는 <linux/pci.h> 에 선언된 심볼들을 이용하기 바란다.

만약 Extended PCI Capability 레지스터에 접근할 필요가 있는 경우에는 단지 특정한 capability 를 위해 pci_find_capability() 를 호출하라. 그러면 이 함수는 해당하는 레지스터 블럭을 찾아줄 것이다.

6 주소와 인터럽트 


메모리와 포트 주소와 인터럽트 번호는 설정 공간으로 부터 읽혀져서는 안된다. 대신에 커널에 의해 매핑된 struct pci_dev 의 값을 사용해야 한다.

장치의 메모리에 접근하는 방법은 Documentation/IO-mapping.txt 를 참조하기 바란다.

다른 누군가가 같은 장치를 사용하지 않음을 나타내기 위해 I/O 영역에 대해서 request_region() 함수를, 메모리 영역에 대해서 request_mem_region() 함수를 호출할 필요가 있다.

모든 인터럽트 핸들러는 SA_SHIRQ 플래그와 함께 등록되어 있어야 하며, IRQ 와 장치를 매핑하기 위해서 devid 를 사용해야 한다 (모든 PCI 인터럽트는 공유된다는 것을 기억하라).

7 다른 흥미로운 함수들 


pci_fiind_slot() 주어진 버스와 슬롯 번호에 대응하는 pci_dev 를 찾는다.
pci_set_power_state() PCI Power Management state (0=D0 ... 3=D3) 를 설정한다.
pci_find_capability() 장치의 capability 리스트로 부터 특정한 capability 를 찾는다.
pci_module_init() 올바른 pci_driver 의 초기화와 에러 처리를 도와주는 인라인 함수이다.
pci_resource_start() 주어진 PCI 영역에 해당하는 버스의 시작 주소를 리턴한다.
pci_resource_end() 주어진 PCI 영역에 해당하는 버스의 마지막 주소를 리턴한다.
pci_resource_len() PCI 영역의 바이트 길이를 리턴한다.
pci_set_drvdata() pci_dev 에 대한 private driver data 포인터를 설정한다.
pci_get_drvdata() pci_dev 에 대한 private driver data 포인터를 얻어온다.
pci_set_mwi() Memory-Write-Invalidate 트랜잭션을 활성화한다.
pci_clear_mwi() Memory-Write-Invalidate 트랜잭션을 비활성화한다.

8 그 밖의 힌트들 


(드라이버에서 사용자에게 어떤 카드가 발견되었는지를 보여주고 싶은 경우와 같이) 사용자에게 PCI 슬롯 네임을 보여줄 때에는 pci_name(pci_dev) 를 사용하기 바란다.

PCI 장치를 참조할 경우에는 항상 struct pci_dev 의 포인터를 이용하라. 모든 PCI 레이어 함수들은 이 정보를 사용하며, 이것이 오직 올바른 것이다. (semantics 가 상당히 복잡한 multiple primary bus 를 사용하는 시스템에서와 같이) 아주 특별한 경우가 아니라면 bus/slot/function 번호를 사용하지 말자.

만약 PCI bus mastering DMA 를 사용하고자 할 경우에는, Documentation/DMA-mapping.txt 를 읽어보기 바란다.

당신이 사용하는 장치에서 Fast Back to Back write 를 켜려고 (turn on) 시도하지 마라. 버스상의 모든 장치들이 이를 지원해야 하므로, 이것은 플랫폼과 일반적인 코드 (generic code) 로 처리할 필요가 있는 것이지 하나의 드라이버 차원에서 다루어 져서는 안된다.

9 사용되지 않는 함수들 


오래된 드라이버를 새로운 PCI 인터페이스로 포팅하려고 할 때 주의해야 할 몇가지 함수들이 있따. 이들은 hotplug 나 PCI domain 이나 locking 에 호환되지 않기 때문에 더이상 커널에 존재하지 않는 것들이다.

pcibios_present(), pci_present() 예전부터, PCI subsystem 과 통신하려고 할 때 그것이 존재하는지 테스트할 필요가 없었다. 만약 존재하지 않는다면 PCI 장치의 리스트는 비어 있을 것이고 장치를 검색하는 모든 함수들은 단지 NULL 을 리턴할 것이다.
pcibios_(read|write)_* 대응되는 pci_(read|write)_* 함수들로 대체되었다.
pcibios_find_* 대응되는 pci_find_* 함수들로 대체되었다.
pci_for_each_dev() pci_find_device() 로 대체되었다.
pci_for_each_dev_reverse() pci_find_device_reverse() 로 대체되었다.
pci_for_each_bus() pci_find_next_bus() 로 대체되었다.
pci_find_device() pci_get_device() 로 대체되었다.
pci_find_subsys() pci_get_subsys() 로 대체되었다.
pcibios_find_class() pci_find_class() 로 대체되었다.
pci_(read|write)_*_nodev() pci_bus_(read|write)_*() 로 대체되었다.


출처 :  http://wiki.kldp.org

'Developer's Infos > Linux Programming' 카테고리의 다른 글

df 소스  (0) 2011.11.06
pci-driver  (0) 2011.10.10
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
Posted by 삼성동고양이
PCI 의 세계는 광대하고 (별로 즐겁지 않겠지만) 놀라움으로 가득하다. 각각의 PCI 장치들은 서로 다른 요구사항과 버그들을 가진다. 이때문에 리눅스 커널의 PCI 지원 레이어는 우리가 바라는 만큼 간단하지가 않다. 이 짧은 글은 잠재적인 모든 PCI 드라이버 개발자들에게 PCI handling 이라는 깊은 숲속에서 자신 만의 길을 찾아내도록 도와주려고 한다.




1 PCI 드라이버의 구조 


2가지 종류의 PCI 드라이버가 존재한다: 신식 (new-style) 드라이버는 장치를 검색 (probe) 하는 대부분의 과정을 PCI 레이어에게 넘겨주었고 장치의 온라인 삽입과 제거를 지원한다. (즉 PCI, hot-pluggable PCI,CardBus 를 하나의 드라이버로 지원한다) 다른 하나는 구식 (old-style) 드라이버인데 모든 장치 검색 과정을 스스로 처리한다. 만약 꼭 그래야 할 이유가 없다면, 새로 작성하는 코드에서 구식의 방식을 이용하여 장치 검색을 수행하지 않도록 하자. 장치가 검색되고 나면 드라이버는 (구식이던 신식이던 간에) 장치를 동작시키고 싶어한다. 그러기 위해서는 다음 과정이 필요하다:

  • 장치를 활성화한다
  • 장치의 설정 공간에 접근한다
  • 장치가 제공하는 자원 (주소와 IRQ 번호) 를 알아낸다
  • 이 자원들을 할당한다
  • 장치와 통신한다 

이들은 대부분 다음 절에서 설명한다. 나머지 부분은 주석이 잘 달려있는 <linux/pci.h> 코드를 보게 될 것이다.

만약 PCI subsystem 이 설정되지 않았다면 (CONFIG_PCI 가 n 값을 가짐) 다음에 소개하는 대부분의 함수들은 내용이 없는 인라인 함수 혹은 드라이버 상의 많은 ifdef 들을 피하기 위한 적절한 에러코드를 리턴하는 함수로 정의될 것이다.

2 신식 (New-style) 드라이버 


신식 드라이버는 초기화 과정에서 단지 다음과 같은 멤버를 포함하는 드라이버를 표현하기 위한 구조체 (struct pci_driver) 의 포인터를 인수로 pci_register_driver 함수를 호출한다.

name 드라이버 이름
id_table 드라이버가 처리하는 디바이스 ID 의 테이블의 포인터. 대부분의 드라이버에서는 이 테이블을 MODULE_DEVICE_TABLE(pci,...) 매크로를 이용해서 공개 (export) 해야 한다. 시스템이 알고있는 모든 PCI 장치들의 probe() 함수를 호출할 때는 NULL 로 설정하라..?
probe ID 테이블과 매치되고 아직 다른 드라이버에 의해 처리되지 않은 모든 장치들에 대한 장치 검색 함수의 포인터 (기존에 존재하던 장치나 이후에 새롭게 추가된 장치에 대하여 pci_register_driver 함수가 실행되는 과정에서 호출됨). 이 함수는 장치를 나타내는 pci_dev 구조체의 포인터와 장치와 매치되는 ID 테이블의 엔트리의 포인터를 인자로 받는다. 장치가 드라이버를 수락하면 (accepted) 0 을 반환하고 그렇지 않으면 (음수값인) 에러 코드를 반환한다. 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
remove 이 드라이버에 의해 처리되던 장치가 제거될 때에 호출되는 함수의 포인터 (드라이버의 등록해제 (deregistration) 과정이나 hot-pluggable 슬롯에서 기계적으로 제거한 경우에 호출됨). 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
save_state 장치가 대기 모드 (suspend) 로 들어가기 전에 상태 정보를 저장한다.
suspend 장치를 절전 상태 (low-power state) 로 만든다.
resume 장치를 절전 상태에서 깨어나게 한다
enable_wake wake event 를 만들어서 장치를 절전 상태로부터 활성화 시킨다.
(PCI 전원 관리와 그에 관련된 함수들은 Documentation/power/pci.txt 를 참고하기 바란다)

ID 테이블은 마지막이 모두 0 인 엔트리로 끝나는 struct pci_device_id 의 배열이다. 이 구조체는 다음과 같은 멤버들을 가진다:

vendor, device 매치시킬 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
subvendor, subdevice 매치시킬 subsystem 의 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
class, class_mask 매치시킬 장치의 클래스. class_mask 는 비교시 어떤 비트들을 검사해야 하는지를 나타냄
driver_data 드라이버마다 자체적으로 사용하는 데이터
대부분의 드라이버에서는 driver_data 를 사용할 필요가 없다. driver_data 를 사용하는 가장 좋은 예로는 동일한 장치 타입의 정적인 리스트의 인덱스로 사용하는 것이다 (포인터로 사용하는 것이 아니다).

시스템이 알고있는 모든 PCI 장치에게 probe() 가 호출되도록 하려면 테이블의 엔트리를 {PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID} 로 만들면 된다.

/sys/bus/pci/drivers/{driver}/new_id 파일에 쓰는 것으로 런타임에 디바이스 드라이버에게 새로운 PCI ID 가 추가될 수 있다. 일단 추가되면, 드라이버는 이것을 지원하는 모든 장치를 검색 (probe) 할 것이다.

echo "vendor device subvendor subdevice class class_mask driver_data" > \ 
   /sys/bus/pci/drivers/{driver}/new_id 
  


(위의 모든 필드들은 (0x 을 생략한 형태의) 16 진수로 넘겨진다.)

사용자는 단지 필요한 필드만을 넘길 필요가 있다; vendor, device, subvendor, subdevice 필드는 기본값으로 PCI_ANY_ID (FFFFFFFF) 를 가지고 class 와 classmask 는 0 을, driver_data 필드는 OUL 을 각각 기본값으로 가진다. 디바이스 드라이버는 인자로 넘겨진 driver_data 필드를 위해서 다음 함수를 반드시 호출해야 한다.

pci_dynids_set_use_driver_data(pci_driver *, 1) 
  


그렇지 않으면 해당 필드로는 단지 0 이 넘겨진다.

드라이버가 제거될 (exit) 때는, 단지 pci_unregister_driver() 함수를 호출하고 PCI 레이어는 자동적으로 이 드라이버에게 처리되고 있던 모든 장치에 대해서 remove hook 함수를 호출한다.

초기화 (initialization) 함수와 제거 (clean-up) 함수는 (<linux/init.h> 에 정의된 매크로 들을 이용하여) 적절히 표시해 주기 바란다:

__init 초기화 코드. 드라이버가 초기화 된 이후에는 사라진다.
__exit 종료 코드. 모듈로 컴파일 되지 않은 드라이버에서는 무시된다.
__devinit 장치 초기화 코드. 커널이 CONFIG_HOTPLUG 가 설정되지 않은 상태로 컴파일 되었다면 __init 와 동일하다. 그렇지 않으면 일반 함수이다.
devexit exit 와 동일하다.
팁: 

module_init()/module_exit() 함수들 (그리고 오직 여기서만 호출되는 모든 초기화 함수들) 은 __init/exit 로 표시 (mark) 되어야 한다. struct pci_driver 는 이러한 태그들로 표시되면 안된다. ID 테이블 배열은 __devinitdata 로 표시되어야 한다. probe() 와 remove() 함수들 (그리고 오직 여기서만 호출되는 모든 초기화 함수들) 은 __devinit/exit 로 표시되어야 한다. 만약 드라이버가 hotplug 를 지원하는 드라이버가 아니라고 확신한다면 그냥 __init/exit 와 __initdata/exitdata 만을 사용하라.

__devexit 로 표시된 함수에 대한 포인터는 반드시 __devexit_p(함수이름) 을 사용하여 생성되어야 한다. 이 매크로는 함수이름을 생성하거나 __devexit 로 표시된 함수가 버려질 (discard) 경우 NULL 을 반환한다.

3 수동으로 PCI 장치를 검색하는 방법 (구식 드라이버) 


pci_register_driver() 인터페이스를 이용하지 않는 PCI 드라이버의 경우에는 다음과 같은 방식으로 수동으로 (manually) PCI 장치를 검색할 수 있다:

제품 공급자와 장치 ID 검색:

struct pci_dev *dev = NULL; 
while (dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev)) 
    configure_device(dev); 
  


클래스 ID 검색 (동일한 방식으로 루프를 이용):

    pci_find_class(CLASS_ID, dev) 
  


제품 공급자/장치 ID 및 subsystem 의 제품 공급자/장치 ID 검색:

    pci_find_subsys(VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev) 
  


VENDOR_ID 나 DEVICE_ID 위치에는 와일드 카드로 PCI_ANY_ID 라는 상수를 이용할 수 있다. 이 경우에는 예를 들어 '특정 공급자가 제공하는 모든 장치' 와 같은 검색이 가능하게 된다.

이 함수들은 hotplug 에 대해서 안전하지 않다는 (not hotplug-safe) 것을 주의하기 바란다. 위의 각 함수들에 대해서 hotplug 를 안전하게 지원하는 버전은 pci_get_device(), pci_get_class(), pci_get_subsys() 이다. 이 함수들은 자신이 반환하는 장치에 해당하는 pci_dev 구조체의 참조 횟수 (reference count) 를 증가시킨다. 해당 장치를 사용하지 않을 경우에는 (아마도 모듈 언로드시) pci_dev_put() 함수를 이용하여 반드시 이 장치의 참조 횟수를 감소시켜야 한다.

4 장치 활성화하기 


검색한 장치를 이용해서 어떤 작업을 수행하기 전에, pci_enable_device() 함수를 이용해 장치를 활성화 시킬 필요가 있다. 이 함수는 I/O 와 메모리 영역을 활성화시키고, 필요한 경우 잃은 자원들을 할당하며, 장치가 절전 상태에 있었던 경우에는 깨어나도록 한다. 이 함수가 실패할 수도 있다는 사실에 주의하기 바란다.

만약 장치를 buf mastering mode 로 사용하고 싶은 경우에는, pci_set_master() 함수를 호출해서 PCI_COMMAND 레지스터의 bus master 비트를 활성화하고, BIOS 에 의해 어떤 bogus 로 설정된 경우?? 레이턴시 타이머 값을 수정한다.

만약 PCI Memory-Write-Invalidate 트랜잭션을 사용하고 싶은 경우에는 pci_set_mwi() 함수를 호출하라. 이 함수는 Mem-Wr-Inval 의 PCI_COMMAND 비트를 활성화하고, cache inline size 레지스터가 적절하게 설정되도록 해준다. 모든 아키텍처에서 Memory-Write-Invalidate 를 지원하지는 않기 때문에, pci_set_mwi() 의 리턴값을 체크해야 한다.

5 PCI 설정 공간에 접근하는 방법 


struct pci_dev * 로 표현되는 장치의 설정 공간에 접근하기 위해서 pci_(read/write)_config_(byte|word|dword) 함수를 사용할 수 있다. 이 함수들은 모두 성공인 경우에 0 을 리턴하고, 실패인 경우 pcibios_strerror 에 의해 문자열로 변환될 수 있는 에러 코드 (PCIBIOS_... 의 형태) 를 리턴한다. 대부분의 드라이버에서 올바른 PCI 장치에 접근하는 경우에는 실패하지 않을 것이다.

만약 가능한 struct pci_dev 가 없는 경우에는 pci_bus(read|write)_config(byte|word|dword) 함수를 이용해서 해당 버스상의 장치와 function ? 에 접근할 수 있다.

만약 config header 의 standard portion 의 필드에 접근하는 경우에는 <linux/pci.h> 에 선언된 심볼들을 이용하기 바란다.

만약 Extended PCI Capability 레지스터에 접근할 필요가 있는 경우에는 단지 특정한 capability 를 위해 pci_find_capability() 를 호출하라. 그러면 이 함수는 해당하는 레지스터 블럭을 찾아줄 것이다.

6 주소와 인터럽트 


메모리와 포트 주소와 인터럽트 번호는 설정 공간으로 부터 읽혀져서는 안된다. 대신에 커널에 의해 매핑된 struct pci_dev 의 값을 사용해야 한다.

장치의 메모리에 접근하는 방법은 Documentation/IO-mapping.txt 를 참조하기 바란다.

다른 누군가가 같은 장치를 사용하지 않음을 나타내기 위해 I/O 영역에 대해서 request_region() 함수를, 메모리 영역에 대해서 request_mem_region() 함수를 호출할 필요가 있다.

모든 인터럽트 핸들러는 SA_SHIRQ 플래그와 함께 등록되어 있어야 하며, IRQ 와 장치를 매핑하기 위해서 devid 를 사용해야 한다 (모든 PCI 인터럽트는 공유된다는 것을 기억하라).

7 다른 흥미로운 함수들 


pci_fiind_slot() 주어진 버스와 슬롯 번호에 대응하는 pci_dev 를 찾는다.
pci_set_power_state() PCI Power Management state (0=D0 ... 3=D3) 를 설정한다.
pci_find_capability() 장치의 capability 리스트로 부터 특정한 capability 를 찾는다.
pci_module_init() 올바른 pci_driver 의 초기화와 에러 처리를 도와주는 인라인 함수이다.
pci_resource_start() 주어진 PCI 영역에 해당하는 버스의 시작 주소를 리턴한다.
pci_resource_end() 주어진 PCI 영역에 해당하는 버스의 마지막 주소를 리턴한다.
pci_resource_len() PCI 영역의 바이트 길이를 리턴한다.
pci_set_drvdata() pci_dev 에 대한 private driver data 포인터를 설정한다.
pci_get_drvdata() pci_dev 에 대한 private driver data 포인터를 얻어온다.
pci_set_mwi() Memory-Write-Invalidate 트랜잭션을 활성화한다.
pci_clear_mwi() Memory-Write-Invalidate 트랜잭션을 비활성화한다.

8 그 밖의 힌트들 


(드라이버에서 사용자에게 어떤 카드가 발견되었는지를 보여주고 싶은 경우와 같이) 사용자에게 PCI 슬롯 네임을 보여줄 때에는 pci_name(pci_dev) 를 사용하기 바란다.

PCI 장치를 참조할 경우에는 항상 struct pci_dev 의 포인터를 이용하라. 모든 PCI 레이어 함수들은 이 정보를 사용하며, 이것이 오직 올바른 것이다. (semantics 가 상당히 복잡한 multiple primary bus 를 사용하는 시스템에서와 같이) 아주 특별한 경우가 아니라면 bus/slot/function 번호를 사용하지 말자.

만약 PCI bus mastering DMA 를 사용하고자 할 경우에는, Documentation/DMA-mapping.txt 를 읽어보기 바란다.

당신이 사용하는 장치에서 Fast Back to Back write 를 켜려고 (turn on) 시도하지 마라. 버스상의 모든 장치들이 이를 지원해야 하므로, 이것은 플랫폼과 일반적인 코드 (generic code) 로 처리할 필요가 있는 것이지 하나의 드라이버 차원에서 다루어 져서는 안된다.

9 사용되지 않는 함수들 


오래된 드라이버를 새로운 PCI 인터페이스로 포팅하려고 할 때 주의해야 할 몇가지 함수들이 있따. 이들은 hotplug 나 PCI domain 이나 locking 에 호환되지 않기 때문에 더이상 커널에 존재하지 않는 것들이다.

pcibios_present(), pci_present() 예전부터, PCI subsystem 과 통신하려고 할 때 그것이 존재하는지 테스트할 필요가 없었다. 만약 존재하지 않는다면 PCI 장치의 리스트는 비어 있을 것이고 장치를 검색하는 모든 함수들은 단지 NULL 을 리턴할 것이다.
pcibios_(read|write)_* 대응되는 pci_(read|write)_* 함수들로 대체되었다.
pcibios_find_* 대응되는 pci_find_* 함수들로 대체되었다.
pci_for_each_dev() pci_find_device() 로 대체되었다.
pci_for_each_dev_reverse() pci_find_device_reverse() 로 대체되었다.
pci_for_each_bus() pci_find_next_bus() 로 대체되었다.
pci_find_device() pci_get_device() 로 대체되었다.
pci_find_subsys() pci_get_subsys() 로 대체되었다.
pcibios_find_class() pci_find_class() 로 대체되었다.
pci_(read|write)_*_nodev() pci_bus_(read|write)_*() 로 대체되었다.



 출처 : http://wiki.kldp.org/wiki.php
 

'Developer's Infos > Linux Programming' 카테고리의 다른 글

df 소스  (0) 2011.11.06
PCI 드라이버(PCI-Driver)  (0) 2011.10.12
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
Posted by 삼성동고양이

1
vim:ft=cpp
2
3 @ 개요 4 wait_queue 설명 5 6 @ wait_queue 7 어떤 일이 발생할때 깨어날 task(process)를 저장하는 queue. 8 9 동일한 커널함수(코드)가 여러 task에 의해 실행될수 있어 어떤 조건을 10 만족할때까지 여러개의 task를 보류했다가 조건 발생시 task를 실행하기 위해 사용. 11 12 A constext에서 schdule()를 호출하게 되면 context는 다른 task로 넘어가게 되는데 13 A를 어떤일이 발생하면 다시 실행(TASK_RUNNING)으로 만들기 위해 wait queue를 사용한다. 14 15 A context에서 schedule()를 호출하기전 자신을 깨어날 조건 (TASK_INTERRUPTIBLEE등) 16 으로 변경한후 wait_queue 에 추가한다. 그리고 schedule()를 호출하여 context를 17 다른 task로 넘긴다. 다른 task실행중 A task가 깨어날 조건 (interrupt등)이 발생되면 18 관련함수(코드)가 실행되고 이곳에서 wake_up_xxx()함수를 호출하여 wait_queue의 19 task를 깨운다(TASK_RUNNING으로 변경). 20 그러면 A task의 schedule()아래 코드부터 다시 실행된다. 21 이때 주의할것이 22 - A task의 schedule()아래에서 remove_wait_queue()를 호출하여 23 wait_queue에서 자신을 (A task)를 제거해야 한다. 24 25 - add_wait_queue(), remove_wait_queue(), wake_up_xxx()에 인자로 넘어가는 26 wait_queue는 동일한 instance이다. 27 28 @ 용법 29 30 - wait 시작하는 함수 31 DECLARE_WAITQUEUE(wait, current); 32 add_wait_queue(&gQName, &wait); 33 set_current_state(TASK_INTERRUPTIBLEE); 34 schedule_timeout(timeout); 35 current->state = TASK_RUNNING; 36 remove_wait_queue(&gQName, &wait); 37 38 void tty_wait_until_sent(struct tty_struct * tty, long timeout) 39 { 40 DECLARE_WAITQUEUE(wait, current); 41 42 ... 43 add_wait_queue(&tty->write_wait, &wait); 44 if (!timeout) 45 timeout = MAX_SCHEDULE_TIMEOUT; 46 do { 47 ... 48 set_current_state(TASK_INTERRUPTIBLE); 49 ... 50 timeout = schedule_timeout(timeout); 51 } while (timeout); 52 if (tty->driver.wait_until_sent) 53 tty->driver.wait_until_sent(tty, timeout); // <- wait에서 깨어나서 해야할 일 54 stop_waiting: 55 current->state = TASK_RUNNING; 56 remove_wait_queue(&tty->write_wait, &wait); 57 } 58 59 - wait를 깨우는 함수 60 wake_up_interruptible(&gQName); 61 62 /* 63 * This can be called through the "tq_scheduler" 64 * task-list. That is process synchronous, but 65 * doesn't hold any locks, so we need to make 66 * sure we have the appropriate locks for what 67 * we're doing.. 68 */ 69 void do_tty_hangup(void *data) 70 { 71 ... 72 wake_up_interruptible(&tty->write_wait); 73 wake_up_interruptible(&tty->read_wait); 74 ... 75 } 76 77 @ wake_up_interruptible() 78 wait_queue list에서 TASK_INTERRUPTIBLEE로 설정된 79 task를 TASK_RUNNING로 변경 하고 add_to_runqueue()호출 80 81 #define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 82 #define wake_up_all(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE) 83 #define wake_up_sync(x) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 84 #define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 85 #define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE) 86 #define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 87 88 void __wake_up(wait_queue_head_t *q, unsigned int mode) 89 { 90 __wake_up_common(q, mode, 0); 91 } 92 93 static inline void __wake_up_common(wait_queue_head_t *q, unsigned int mode, const int sync) 94 { 95 struct list_head *tmp, *head; 96 struct task_struct *p; 97 unsigned long flags; 98 ... 99 list_for_each(tmp, head) { 100 unsigned int state; 101 wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list); 102 103 #if WAITQUEUE_DEBUG 104 CHECK_MAGIC(curr->__magic); 105 #endif 106 p = curr->task; 107 state = p->state; 108 if (state & (mode & ~TASK_EXCLUSIVE)) { 109 #if WAITQUEUE_DEBUG 110 curr->__waker = (long)__builtin_return_address(0); 111 #endif 112 if (sync) 113 wake_up_process_synchronous(p); 114 else 115 wake_up_process(p); 116 if (state & mode & TASK_EXCLUSIVE) 117 break; 118 } 119 } 120 wq_write_unlock_irqrestore(&q->lock, flags); 121 out: 122 return; 123 } 124 125 /* 126 * Wake up a process. Put it on the run-queue if it's not 127 * already there. The "current" process is always on the 128 * run-queue (except when the actual re-schedule is in 129 * progress), and as such you're allowed to do the simpler 130 * "current->state = TASK_RUNNING" to mark yourself runnable 131 * without the overhead of this. 132 */ 133 inline void wake_up_process(struct task_struct * p) 134 { 135 unsigned long flags; 136 137 /* 138 * We want the common case fall through straight, thus the goto. 139 */ 140 spin_lock_irqsave(&runqueue_lock, flags); 141 p->state = TASK_RUNNING; 142 if (task_on_runqueue(p)) 143 goto out; 144 add_to_runqueue(p); 145 reschedule_idle(p, flags); // spin_unlocks runqueue 146 147 return; 148 out: 149 spin_unlock_irqrestore(&runqueue_lock, flags); 150 } 151 152 /* 153 * Careful! 154 * 155 * This has to add the process to the _beginning_ of the 156 * run-queue, not the end. See the comment about "This is 157 * subtle" in the scheduler proper.. 158 */ 159 static inline void add_to_runqueue(struct task_struct * p) 160 { 161 list_add(&p->run_list, &runqueue_head); 162 nr_running++; 163 }

[출처] wait Queue|작성자 달려라


'Developer's Infos > Linux Programming' 카테고리의 다른 글

PCI 드라이버(PCI-Driver)  (0) 2011.10.12
pci-driver  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
[Shared Memory] shmget() 공유 메모리 생성  (0) 2011.09.06
Posted by 삼성동고양이
이전버튼 1 2 3 이전버튼