1

I'm developing a patch for the linux kernel. I have to use several lists and I have to protect'em against concurrent modification on a multicore machine. I'm trying to use spinlocks for this goal, but there's something I can't understand. I have to lock the entries of a list (I'm using linux default implementation of linked lists) and it can happen that a process invokes a syscall to remove one element of the list while the same element which is locked because some modification is actually being made on it. If I insert a spinlock inside the list entry, what happens if a process manage to remove it while someone is spinlocking on it?? Should I lock the entire list? I'm looking for a piece of code that can explain how to do handle this situation.

For example, this code shouldn't work (see comment on the last line of code):

   struct lista{
    int c;
    spinlock_t lock;
    struct list_head;
}

spinlock_t list_lock;
struct lista lista;


//INSERT
struct lista* cursor;
struct lista* new = (struct lista*) kmalloc(sizeof(struct lista),GFP_KERNEL);

/*do something*/
spin_lock(&list_lock);     //Lock on the whole list
list_for_each_entry(cursor,&lista.list,list){
    if (cursor->c == something ){
        ...
        spin_unlock(&list_lock)  //unlock
        spin_lock(&cursor->lock) // Lock on list entry
        list_add(&new->list, &lista.list);
        spin_unlock(&cursor->lock)  // unlock of the list entry
        ...
    }
}


//REMOVAL
struct lista* cursor;

spin_lock(&list_lock);  
list_for_each_entry(cursor,&lista.list,list){
    if (cursor->c == something ){
        ...
        spin_unlock(&list_lock)  //unlock
        spin_lock(&cursor->lock) // Lock on list entry
        list_del(&cursor.list,&lista.list);
        spin_unlock(&cursor->lock)  // unlock of the list entry
        kfree(cursor);  //WHEN THE ENTRY IS FREED SOMEONE COULD HAVE TAKEN THE LOCK SINCE IT IS UNLOCKED
        ...
    }
}

Can you help me??

4

4 回答 4

3

list_lock在您完成移除项目之前不要释放。

您最终可能会遇到以下稍微尴尬的过程:

  1. 获取列表锁(这将阻塞其他传入线程)
  2. 获取项目锁定,释放项目锁定(这确保所有早期线程都已完成)
  3. 除去项目
  4. 释放列表锁定。

变体:使用读写锁作为列表锁。

想要修改列表项的线程会获得读者锁;这允许多个线程在列表上并行操作。

想要删除列表项的线程会获取写入者锁;这会等待所有读者退出并阻止它们,直到您释放它。在这种情况下,您仍然必须保持列表锁定,直到您完成删除项目,

通过这种方式,您可以避免上面的第 2 步。这在概念上可能看起来更清楚,因为您不需要解释看起来毫无意义的锁定/释放。

于 2010-08-31T20:35:11.383 回答
0

您几乎可以肯定根本不应该使用自旋锁,除非有来自硬 IRQ 上下文的并发访问。请改用互斥锁。

您的列表最简单的选择就是在您操作时锁定整个列表。不要担心每个项目的锁,除非并且直到您发现列表锁上有足够的争用需要它(在这种情况下,您可能希望考虑使用 RCU 来代替)。

于 2010-09-01T01:05:00.647 回答
0

您的列表头不必是 a struct lista,它应该只是 a struct list_head。请注意,您继续使用&lista.list,那应该只是一个list_head称为“列表”的东西。例如,请参见 中的代码drivers/pci/msi.c,注意它dev->msi_list只是 a list_head,而不是 a struct msi_desc

您不能安全地放下列表锁,然后抓住光标锁。有可能在您删除列表锁定之后但在您获得光标锁定之前,其他人进来并释放了您的光标。你可以玩弄锁,但这容易出错。

对于整个列表,您几乎肯定只需要一个锁,而不需要每个项目的锁。mutex除非您需要从中断上下文中操作列表,否则列表锁应该是一个。

于 2010-09-08T07:07:59.533 回答
0

如果您没有处理必须自旋锁的设备和/或内核的关键部分(因为它禁用抢占和中断(根据请求)),那么 Y 2 使用自旋锁,这将不必要地关闭您的抢占和中断. 在列表中使用信号量或互斥量而不是列表项看起来更好的解决方案。

于 2014-02-27T13:14:31.813 回答