2011-03-25 7 views
1

임베디드 CPU에서 실행중인 Linux 장치 드라이버를 개발 중입니다. 이 장치 드라이버는 일부 외부 하드웨어를 제어합니다. 외부 하드웨어에는 자체 DDR 컨트롤러 및 외부 DDR이 있습니다. 하드웨어의 DDR은 이동식 메모리 창을 통해 내장 CPU에서 볼 수 있습니다 (따라서 Linux 드라이버에서 외부 DDR에 대한 액세스를 페이징했습니다). Linux 커널 버전 2.6.33을 사용하고 있습니다.스레드가 버스 오류로 종료 될 때 세마포 잠금을 방지하려면 어떻게합니까?

내 드라이버는 sysfs를 사용하여 사용자 공간에서 외부 하드웨어를 제어 할 수 있도록합니다. 예를 들어 외부 하드웨어는 외부 DDR의 특정 주소를 증가시키는 하트 비트 카운터를 생성합니다. 드라이버는 외부 하드웨어가 여전히 실행 중인지 여부를 감지하기 위해이를 읽습니다.

외부 DDR이 올바르게 작동하지 않는 경우 외부 DDR에 액세스하면 내장 CPU에서 버스 오류가 발생합니다. 동시 다중 스레드 액세스를 방지하기 위해 드라이버는 세마포어를 사용합니다.

문제가 생겼습니다. 스레드가 세마포어를 잡고 버스 오류로 종료하면 세마포어는 여전히 잠겨 있습니다. 이후의 모든 호출은 무한대로 세마포 블록을 잡습니다. 운전 기사가 영원히 걸리지 않도록하기 위해 어떤 기술을 사용할 수 있습니까?

의 예 sysfs를 기능 (간체) 다음을 종료하는 대신 오류를 반환 할 수 있습니다 mwindow_get_reg() 있도록

static ssize_t running_attr_show(struct device *dev, struct device_attribute *attr, char *buffer) 
{ 
    struct my_device * const my_dev = container_of(dev, struct my_device, dev); 
    int ret; 

    if(down_interruptible(&my_dev->sem)) 
    { 
     ret = -ERESTARTSYS; 
    } 
    else 
    { 
     u32 heartbeat; 
     int running; 

     // Following line could cause bus error 
     heartbeat = mwindow_get_reg(&my_dev->mwindow, HEARTBEAT_COUNTER_ADDR); 

     running = (heartbeat != my_dev->last_heartbeat) ? 1 : 0; 
     my_dev->last_heartbeat = heartbeat; 

     ret = sprintf(buffer, "%d\n", result); 

     /* unlock */ 
     up(&my_dev->sem); 
    } 

    return ret; 
} 

답변

1

당신은 mwindow_get_reg()와 버스 오류에 호출있어 가능한 아키텍처 결함 핸들러를 수정해야합니다 방법.

그런 다음 세마포를 해제하고 사용자 공간에 오류를 반환하여 정상적으로 오류를 처리 할 수 ​​있습니다.

+0

감사 픽스 어셈블러는 그때 내 C 코드를 테스트 할 수있는 '폴트'플래그를 설정합니다. 확실히 고려해야 할 것. 예외 처리기가 버스 오류의 원인이 mwindow_get_reg의 호출임을 알 수있는 방법에 대한 제안이 있습니까? 우리는 접근 전에 정적 변수 세트에 의존하고 오류 동안 탐지되어 다른 정적 변수를 설정 한 다음 액세스 후에 검사하는 몇 가지 코드를 이미 가지고 있습니다. 인터럽트는 전체 프로세스 중에 비활성화됩니다. 이것은 매우 우아 해 보이지 않으며 많은 양의 펌웨어를 메모리 창을 통해 대상 DDR에 복사 할 때 장시간 동안 인터럽트가 비활성화되었다는 것을 의미합니다. – qbert220

+0

@ qbert220 : 아키텍쳐의'copy_from_user()'구현을 살펴보십시오.이 함수는 반드시 복구 할 수없는 페이지 오류가 공포감을 유발하기보다는 호출자에게'EFAULT '를 반환하도록 설정해야합니다. 페이지 폴트 처리기가 작업의 일부분을 수행하는 방법). – caf

+0

이것은 매우 간단한 아키텍처입니다. copy_to_user 구현은 access_ok (주소가 예상 범위에 있는지 확인)를 호출하고 false를 반환하면 -EFAULT를 반환합니다. 다른 모든 경우에는 memcpy를 호출하고 복사 요청 된 바이트 수를 반환합니다 (memcpy 반환 값은 무시됩니다). – qbert220

1

@caf 덕분에, 여기 구현 한 해결책이 있습니다.

mwindow_get_reg의 일부를 어셈블리로 변환했습니다. 가능한 오류 읽기에 대해 오류가있는 주소 및 수정 주소가있는 항목을 ex_table 섹션에 추가했습니다. 이로 인해이 주소에서 예외가 발생하면 예외 처리기가 스레드를 종료하는 대신 픽스 업 코드로 이동합니다.

unsigned long ret = 0; 
int faulted; 

asm volatile(
     " 1:  lwi  %0, %2, 0;   "  // ret = *window_addr 
     " 2:  addik %1, r0, 0;   "  // faulted = 0 
     " 3:         " 
     "   .section .fixup, \"ax\"; "  // fixup code executed if exception occurs 
     " 4:  brid 3b;    "  // jump to next line of c code 
     "   addik %1, r0, 1;   "  // faulted = 1 (in delay slot) 
     "   .previous;     " 
     "   .section __ex_table,\"a\"; " 
     "   .word 1b,4b;    "  // ex_table entry. Gives fault address and jump address if fault occurs 
     "   .previous;     " 
      : "=r" (ret), "=r" (faulted)    // output registers 
      : "r" (window_addr)      // input registers 
); 

if (faulted) 
{ 
    printk(KERN_ERROR "%s: %s: FAULTED!", MODNAME, __FUNCTION__); 
    ret = 0xdeadbeef; 
} 

나는 또한 다음과 같은 추가하여 내 DBUS 예외 핸들러를 수정했습니다 :

const struct exception_table_entry *fixup; 
fixup = search_exception_tables(regs->pc); 
if (fixup) { 
    printk(KERN_ERROR "DBUS exception: calling fixup\n"); 
    regs->pc = fixup->fixup; 
    return; 
} 
관련 문제