Pergunta

Estou desenvolvendo um patch para o kernel Linux. Eu tenho que usar várias listas e tenho que proteger a modificação simultânea em uma máquina multicore. Estou tentando usar spinlocks para esse objetivo, mas há algo que não consigo entender. Eu tenho que bloquear as entradas de uma lista (estou usando a implementação padrão do Linux de listas vinculadas) e pode acontecer que um processo invoca um syscall para remover um elemento da lista enquanto o mesmo elemento bloqueado porque alguma modificação é realmente sendo feito nele. Se eu inserir um spinlock dentro da entrada da lista, o que acontece se um processo conseguir removê -lo enquanto alguém estiver spinlocking nele? Devo trancar a lista inteira? Estou procurando um código que possa explicar como lidar com essa situação.

Por exemplo, esse código não deve funcionar (consulte o comentário na última linha de código):

   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
        ...
    }
}

Pode me ajudar??

Foi útil?

Solução

Não solte list_lock até terminar de remover o item.

Você pode acabar com o procedimento um pouco estranho de:

  1. Adquirir bloqueio da lista (isso bloqueará outros threads de entrada)
  2. Adquira trava de itens, bloqueio de item de liberação (isso garante que todos os threads anteriores são feitos)
  3. Remover item
  4. Lançamento da lista de liberação.

Variação: use um bloqueio de leitor-escritor para o bloqueio da lista.

Tópicos que desejam modificar os itens da lista Pegue o bloqueio do leitor; Isso permite que vários threads operem na lista em paralelo.

Tópicos que desejam remover os itens da lista Pegue o bloqueio do escritor; Isso espera que todos os leitores saiam e os bloqueiem até você lançá -lo. Nesse caso, você ainda precisa manter o bloqueio da lista até terminar de remover o item,

Dessa maneira, você pode evitar a etapa 2 acima. Isso pode parecer conceitualmente mais claro, pois você não precisa explicar o bloqueio/liberação sem sentido.

Outras dicas

Você quase certamente não deveria estar usando spinlocks, a menos que haja acesso simultâneo do contexto de IRQ rígido. Use mutexes em vez disso.

A opção mais fácil da sua lista é apenas bloquear a lista inteira enquanto você opera nela. Não se preocupe com bloqueios por item, a menos e até você descobrir que há uma contenção suficiente na bloqueio da lista que você precisa (e, nesse caso, você provavelmente deseja usar o uso da RCU, de qualquer maneira).

Sua cabeça de lista não precisa ser um struct lista, deveria ser apenas um struct list_head. Observe que você continua usando &lista.list, isso deve ser apenas um list_head chamado de "lista" ou algo assim. Veja, por exemplo, o código em drivers/pci/msi.c, notar que dev->msi_list é apenas um list_head, não a struct msi_desc.

Você não pode largar com segurança o bloqueio da lista e, em seguida, pegue o bloqueio do cursor. É possível que, depois de deixar o bloqueio da lista, mas antes de você travar o cursor, alguém entrou e liberte seu cursor. Você pode fazer malabarismos com as fechaduras, mas isso é muito fácil de errar.

Você quase definitivamente quer apenas uma trava, para toda a lista e sem bloqueios por itens. E o bloqueio da lista deve ser um mutex A menos que você precise manipular a lista do contexto de interrupção.

Se você não estiver lidando com dispositivos e / ou as seções críticas do kernel onde o bloqueio de spin é uma obrigação (pois desativa a preempção e interrupção (mediante solicitação)), então você use bloqueios de spin que não serão necessários, fecharão sua preempção e interrupções e interrupções . Usando o semáforo ou o mutex que também na lista e não é um item de lista parece melhor solução.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top