在持有自旋锁时自动不安全返回?

时间:2015-12-14 21:32:22

标签: linux-kernel linux-device-driver interrupt-handling spinlock

受人尊敬的书籍Linux Driver Development说明了

  

传递给flags的{​​{1}}参数必须是传递给spin_unlock_irqrestore的相同变量。 您还必须在同一个函数中调用spin_lock_irqsavespin_lock_irqsave;否则你的代码可能会破坏某些架构。

然而我找不到the official documentation bundled with the kernel code itself所要求的任何此类限制。我找到driver code that violates this guidance

显然,从单独的函数中调用spin_unlock_irqrestorespin_lock_irqsave并不是一个好主意,因为你应该在保持锁定的同时最小化完成的工作(禁用中断,不能少! )。但是如果对内核进行了更改,那么它是否可以小心使用,它是否真的不会违反API合同,或者它是否仍然禁止这样做?

如果在某些时候删除了限制,它是否适用于版本3.10.17?

2 个答案:

答案 0 :(得分:5)

这只是猜测,但可能不清楚地指的是如果您尝试使用非本地变量或标记的存储位置时可能发生的潜在错误。

基本上, flags 必须是当前执行上下文的私有,这就是spin_lock_irqsave是一个带有flags名称的宏的原因。正在保存标志时,您还没有自旋锁。

这与其他功能中的锁定和解锁有何关系:

考虑一些驱动程序开发人员可能编写的两个函数:

void my_lock(my_object *ctx)
{
  spin_lock_irqsave(&ctx->mylock, ctx->myflags);  /* BUG */
}

void my_unlock(my_object *ctx)
{
  spin_unlock_irqrestore(&ctx->mylock, ctx->myflags);
}

这是一个错误,因为在编写ctx->myflags时,锁尚未保留,并且它是其他上下文和处理器可见的共享变量。必须将本地标志保存到堆栈上的私有位置。然后,当调用者拥有锁时,可以将标志的副本保存到专有对象中。换句话说,它可以像这样修复:

void my_lock(my_object *ctx)
{
  unsigned long flags;
  spin_lock_irqsave(&ctx->mylock, flag);
  ctx->myflags = flags;
}

void my_unlock(my_object *ctx)
{
  unsigned long flags = ctx->myflags; /* probably unnecessary */
  spin_unlock_irqrestore(&ctx->mylock, flags); 
}

如果不能像这样修复,那么实现需要包装IRQ自旋锁的更高级原语是非常困难的。

它如何依赖于拱形:

假设spin_lock_irqsave扩展为机器代码,将当前标志保存在某个寄存器中,然后获取锁定,然后将该寄存器保存到指定的{ {1}}目的地。在这种情况下,有缺陷的代码实际上是安全的。如果扩展代码将标志保存到调用者指定的实际flags对象中,然后尝试获取锁定,那么它就会被破坏。

答案 1 :(得分:1)

除了这本书,我从未见过这种限制。可能,书中的信息只是过时的,或者......简直就是错误的。

在当前内核中(至少从2.6.32开始,我开始使用)实际锁定是通过spin_lock_irqsave的多级嵌套调用完成的(参见例如__raw_spin_lock_irqsave,被称为中间)。因此lockunlock的不同函数的上下文可能不是导致错误的原因。