Pergunta

Em uma máquina SMP devemos usar spin_lock_irqsave e não spin_lock_irq do contexto de interrupção.

Por que quereríamos salvar os sinalizadores (que contêm o IF)?

Existe outra rotina de interrupção que poderia nos interromper?

Foi útil?

Solução

Eu sou novo no kernel, mas pelo que eu recebo no livro de Robert Love, "Linux Kernel Development", se as interrupções já estiverem desativadas no processador antes do seu código começar a bloquear, quando você ligar para Spin_unlock_irq, você lançará o bloqueio de maneira errônea. Se você salvar os sinalizadores e liberá -lo com os sinalizadores, a função spin_lock_irqsave retornará a interrupção ao seu estado anterior.

Exemplo com spin_lock_irqsave

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irqsave(&mLock, flags); // save the state, if locked already it is saved in flags
// Critical section
spin_unlock_irqrestore(&mLock, flags); // return to the formally state specified in flags

Exemplo com spin_lock_irq(sem irqsave):

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irq(&mLock); // Does not know if already locked
// Critical section
spin_unlock_irq(&mLock); // Could result in an error unlock...

Outras dicas

spin_lock_irqsave é basicamente usado para salvar o estado de interrupção antes de fazer o bloqueio de rotação, isso ocorre porque o bloqueio de spin desativa a interrupção, quando o bloqueio é levado no contexto de interrupção e reencontra-o quando desbloqueou. O estado de interrupção é salvo para restabelecer as interrupções novamente.

Exemplo:

  1. Digamos que a interrupção X tenha sido desativada antes que o bloqueio do spin fosse adquirido
  2. spin_lock_irq desativará a interrupção x e pega o bloqueio
  3. spin_unlock_irq permitirá a interrupção x.

Portanto, na terceira etapa acima, após a liberação da fechadura, teremos interrupção X ativada, que foi desativada anteriormente antes da adquirida o bloqueio.

Então, somente quando você tem certeza de que as interrupções não estão desativadas apenas então você deve spin_lock_irq Caso contrário, você deve sempre usar spin_lock_irqsave.

A necessidade de spin_lock_irqsave além do mais spin_lock_irq é bastante semelhante ao motivo local_irq_save(flags) é necessário além local_irq_disable.Aqui está uma boa explicação deste requisito retirada do Linux Kernel Development Second Edition, de Robert Love.

A rotina local_irq_disable () é perigosa se as interrupções já estiverem desativadas antes de sua invocação.A chamada correspondente para local_irq_enable () permite incondicionalmente as interrupções, apesar do fato de elas estarem começando.Em vez disso, é necessário um mecanismo para restaurar interrupções em um estado anterior.Essa é uma preocupação comum, porque um determinado caminho de código no kernel pode ser alcançado com e sem interrupções ativadas, dependendo da cadeia de chamadas.Por exemplo, imagine que o snippet de código anterior faz parte de uma função maior.Imagine que essa função é chamada por duas outras funções, uma que desativa as interrupções e uma que não o faz.Como está se tornando mais difícil à medida que o kernel cresce em tamanho e complexidade conhecer todos os caminhos de código que levam a uma função, é muito mais seguro salvar o estado do sistema de interrupção antes de desativá -lo.Então, quando você estiver pronto para reativar as interrupções, basta restaurá -las ao estado original:

unsigned long flags;

local_irq_save(flags);    /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */

Observe que esses métodos são implementados pelo menos em parte como macros, portanto, o parâmetro Flags (que deve ser definido como um longo não assinado) é aparentemente transmitido pelo valor.Este parâmetro contém dados específicos da arquitetura que contêm o estado dos sistemas de interrupção.Como pelo menos uma arquitetura suportada incorpora informações de pilha no valor (ahem, SPARC), os sinalizadores não podem ser passados ​​para outra função (especificamente, ela deve permanecer no mesmo quadro de pilha).Por esse motivo, a chamada para salvar e a chamada para restaurar as interrupções devem ocorrer na mesma função.

Todas as funções anteriores podem ser chamadas do contexto de interrupção e processo.

Leitura Por que o código/thread do kernel executando no contexto de interrupção não pode dormir? que links para Robert Loves artigo, Eu li isso:

Alguns manipuladores de interrupção (conhecidos no Linux como manipuladores de interrupção rápida) são executados com todas as interrupções no processador local desativado. Isso é feito para garantir que o manipulador de interrupção funcione sem interrupção, o mais rápido possível. Mais ainda, todos os manipuladores de interrupção são executados com sua linha de interrupção atual desativada em todos os processadores. Isso garante que dois manipuladores de interrupção para a mesma linha de interrupção não sejam executados simultaneamente. Também impede que os escritores de driver de dispositivo precisem lidar com interrupções recursivas, que complicam a programação.

Abaixo faz parte do código no kernel Linux 4.15.18, que mostra que spiin_lock_irq () chamará __raw_spin_lock_irq (). No entanto, ele não salvará sinalizadores, como você pode ver abaixo do código, mas desativará a interrupção.

  static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
    {
        local_irq_disable();
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }

Abaixo, o código mostra spin_lock_irqsave () que salva o estágio atual do sinalizador e depois preveja a desativação.

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}

Esta pergunta começa com a falsa afirmação: On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

Nenhum deles deve ser usado no contexto de interrupção, no SMP ou no UP. Dito isto, spin_lock_irqsave() poderia ser usado no contexto de interrupção, como sendo mais universal (pode ser usado em contextos de interrupção e normais), mas você deve usar spin_lock() do contexto de interrupção e spin_lock_irq() ou spin_lock_irqsave() do contexto normal. O uso de spin_lock_irq() É quase sempre a coisa errada a fazer no contexto de interrupção, sendo este SMP ou Up. Pode funcionar porque a maioria dos manipuladores de interrupção é executada com IRQs ativados localmente, mas você não deve tentar isso.

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