Как я могу использовать SpinLocks в списке записей внутри ядра Linux?
-
25-09-2019 - |
Вопрос
Я разрабатываю патч для ядра Linux. Я должен использовать несколько списков, и я должен защитить от одновременной модификации на многократной машине. Я пытаюсь использовать SpinLocks для этой цели, но что-то я не могу понять. Я должен заблокировать записи списка (я использую реализацию связанных списков Linux по умолчанию), и может случиться, что процесс вызывает SYSCall для удаления одного элемента списка, когда тот же элемент, который заблокирован, потому что некоторые модификации на самом деле быть сделанным на нем. Если я вставку спинлок внутри ввода списка, что произойдет, если процесс удастся удалить его, пока кто-то спинет на нем ?? Должен ли я заблокировать весь список? Я ищу кусок кода, который может объяснить, как выполнять эту ситуацию.
Например, этот код не должен работать (см. Комментарий к последней строке кода):
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
...
}
}
Вы можете помочь мне??
Решение
Не выпускай list_lock
пока вы не сделаете удаление предмета.
Вы можете в конечном итоге с слегка неловкой процедурой:
- Приобретение блокировки списка (это заблокирует другие входящие потоки)
- Приобретайте блокировку элементов, отпустите блокировку элементов (это страшено, что все более ранние потоки выполнены)
- Убрать предмет
- Выпуск списка замок.
Вариация: используйте замок для чтения писателя для блокировки списка.
Темы, которые хотят изменять элементы списка, возьмите замок считывателя; Это позволяет нескольким потокам работать в списке параллельно.
Темы, которые хотят удалить элементы списка Возьмите замок писателя; Это ждет всех читателей выходить и блокирует их, пока вы не выпустите его. В этом случае вы все еще должны держать блокировку списка, пока не закончите удаление элемента,
Таким образом, вы можете избежать шага 2 выше. Это может показаться концептуально яснее, так как вам не нужно объяснять бессмысленно выглядящий замок / выпуск.
Другие советы
Вы почти наверняка наверняка не должны использовать SpinLocks вообще, если нет одновременного доступа от Card IRQ контекста. Используйте Mutexes вместо этого.
Самый простой вариант для вашего списка - просто заблокировать весь список, пока вы работаете на нем. Не беспокойтесь о замках в зависимости от предметов, если и до тех пор, пока вы не обнаружите, что в блокировке списка есть достаточное соглашение о том, что вам это нужно (и в этом случае, вы, вероятно, хотите посмотреть на использование RCU вместо этого).
Ваш список головой не должен быть struct lista
, это должно быть просто struct list_head
. Отказ Обратите внимание, что вы продолжаете использовать &lista.list
, что должно быть просто list_head
называется «список» или что-то. См. Например, код в drivers/pci/msi.c
, Заметь dev->msi_list
просто А. list_head
, а не а struct msi_desc
.
Вы не можете безопасно бросить блокировку списка, а затем захватить блокировку курсора. Возможно, после того, как вы бросили замок списка, но перед тем, как вы получите замок курсора, кто-то еще вошел и освободил ваш курсор. Вы можете жонглировать замки, но это очень легко ошибаться.
Вы почти определенно просто хотите, чтобы один замок, для всего списка, и без замков на элементы. И замок списка должен быть mutex
Если вам не нужно манипулировать списком из контекста прерывания.
Если вы не имеете дело с устройствами и или критически важные разделы ядра, где спиновой замок является обязательным (поскольку он отключает вытеснение и прерывание (по запросу)), то y 2 используют спиновые замки, которые будут не обязательно закрывать вашу докурие и прерывания Отказ Использование семафорна или MUTEX, которые тоже в списке и не списком элемент выглядит лучшее решение.