我正在开发在嵌入式CPU上运行的Linux设备驱动程序。该设备驱动程序控制一些外部硬件。外部硬件拥有自己的DDR控制器和外部DDR。通过可移动存储器窗口在嵌入式CPU上可以看到硬件的DDR(因此我从Linux驱动程序中分页访问外部DDR)。我正在使用Linux内核版本2.6.33。
我的驱动程序使用sysfs来允许从用户空间控制外部硬件。例如,外部硬件会生成一个心跳计数器,该计数器会增加外部DDR中的特定地址。驱动程序读取此内容以检测外部硬件是否仍在运行。
如果外部DDR工作不正常,则访问外部DDR会在嵌入式CPU上产生总线错误。为了防止同时多线程访问,驱动程序使用信号量。
现在问题。如果线程抓取信号量,然后以总线错误终止,则信号量仍然被锁定。所有后续调用都会无限期地获取信号量块。我可以使用哪些技术来避免永远挂起驱动程序?
示例sysfs函数(简化):
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;
}
答案 0 :(得分:1)
您需要修改mwindow_get_reg()
以及可能在总线错误上调用的体系结构错误处理程序,以便mwindow_get_reg()
可以返回错误,而不是终止进程。
然后,您可以通过释放信号量并将错误返回给用户空间来优雅地处理该错误。
答案 1 :(得分:1)
感谢@caf,这是我实施的解决方案。
我已将mwindow_get_reg的一部分转换为程序集。对于可能的错误读取,我在ex_table部分添加了一个带有错误地址和修正地址的条目。这会导致异常处理程序跳转到fixup代码,而不是在此地址发生异常时终止该线程。 fixup汇编器设置一个'故障'标志,我可以在我的c代码中测试它:
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;
}