linux Unexpected decrement of the preemption counter

__run_timers() executes a task and expects that the preemption count before the execution equals the preemption count after the execution. When this task is inet_twdr_hangman() the condition is violated; if TCP_MD5SIG is enabled we call tcp_put_md5sig_pool(), which entails a put_cpu().
Bug fixed by commit 657e9649e74
Type FatalAssertionViolation
Config "TCP_MD5SIG && PREEMPT" (2nd degree)
Fix-in code
Location net/ipv4
#include <assert.h>

__attribute__ ((noinline)) int nondet() { return 42; }

static int preempt_counter = 0;

#define preempt_count() (preempt_counter)

#ifdef CONFIG_PREEMPT
#define put_cpu() (preempt_counter--)
#else
#define put_cpu()
#endif

#ifdef CONFIG_TCP_MD5SIG
#define free_cpu()
#endif

static inline void tcp_free_md5sig_pool(void)
{
  free_cpu();
}

static inline void tcp_put_md5sig_pool(void)
{
  put_cpu(); // (9)
}

void tcp_twsk_destructor()
{
#ifdef CONFIG_TCP_MD5SIG
  if (nondet())
    tcp_put_md5sig_pool(); // (8)
#endif
}

static inline void twsk_destructor()
{
  tcp_twsk_destructor(); // (7)
}

static void inet_twsk_free()
{
  twsk_destructor(); // (6)
}

void inet_twsk_put()
{
  inet_twsk_free(); // (5)
}

static int inet_twdr_do_twkill_work()
{
  inet_twsk_put(); // (4)
  return 0;
}

void inet_twdr_hangman()
{
  inet_twdr_do_twkill_work(); // (3)
}

static inline void __run_timers()
{
  int preempt_count = preempt_count();
  inet_twdr_hangman(); // (2)
  if (preempt_count != preempt_count()) {
    assert(0); // (10) ERROR
  }
}

int main(void)
{
  __run_timers(); // (1)
  return 0;
}

diff --git a/simple/657e964.c b/simple/657e964.c
--- a/simple/657e964.c
+++ b/simple/657e964.c
@@ -37,7 +37,7 @@
 {
 #ifdef CONFIG_TCP_MD5SIG
   if (unk())
-    tcp_put_md5sig_pool();
+    tcp_free_md5sig_pool();
 #endif
 }
 
#include <assert.h>
#include <stdbool.h>

#ifdef UNK_TRUE
#define unk() 1
#else
#include <stdlib.h>
#define unk() (rand() % 2)
#endif

int main(int argc, char** argv)
{
//  __run_timers();
  int preempt_counter = 0;
  int preempt_count = preempt_counter;
//  inet_twdr_hangman();
  #ifdef CONFIG_TCP_MD5SIG
  if (unk())
    #ifdef CONFIG_PREEMPT
      preempt_counter--
    #else
    #endif
  #endif

  if (preempt_count != preempt_counter) {
    assert(false); // ERROR
  }
  return 0;
}

. // net/ipv4/tcp_minisocks.c:45: sets a tw_timer to inet_twdr_hangman
. call kernel/timer.c:929:__run_timers()
. // eventually the timer is executed
. 964: int preempt_count = preempt_count();
. 987: fn(data);

.. dyn-call net/ipv4/inet_timewait_sock.c:201:inet_twdr_hangman()
.. 213: if (inet_twdr_do_twkill_work(twdr, twdr->slot)) {
... call net/ipv4/inet_timewait_sock.c:153:inet_twdr_do_twkill_work()
... 177: inet_twsk_put(tw);
.... call net/ipv4/inet_timewait_sock.c:65:inet_twsk_put()
.... 68: inet_twsk_free(tw);
..... call net/ipv4/inet_timewait_sock.c:53:inet_twsk_free()
..... 56: twsk_destructor((struct sock *)tw);
...... call include/net/timewait_sock.h:33:twsk_destructor()
...... 39: sk->sk_prot->twsk_prot->twsk_destructor(sk);
....... dyn-call net/ipv4/tcp_minisocks.c:361:tcp_twsk_destructor()
....... [TCP_MD5SIG] 366: tcp_put_md5sig_pool();
........ call include/net/tcp.h:1217:tcp_put_md5sig_pool()
........ 1220: put_cpu();
// if CONFIG_PREEMPT put_cpu() decrements preempt_counter

. 991: if (preempt_count != preempt_count()) {
// thus this test succeeds
. ERROR 997: BUG();