// BUG: workqueue lockup (4)
// https://syzkaller.appspot.com/bug?id=0041bf1423916e9ae458b08b760e269a33c14960
// status:open
// autogenerated by syzkaller (https://github.com/google/syzkaller)

#define _GNU_SOURCE

#include <endian.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <linux/futex.h>

static void sleep_ms(uint64_t ms)
{
  usleep(ms * 1000);
}

static uint64_t current_time_ms(void)
{
  struct timespec ts;
  if (clock_gettime(CLOCK_MONOTONIC, &ts))
    exit(1);
  return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}

static void thread_start(void* (*fn)(void*), void* arg)
{
  pthread_t th;
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setstacksize(&attr, 128 << 10);
  if (pthread_create(&th, &attr, fn, arg))
    exit(1);
  pthread_attr_destroy(&attr);
}

typedef struct {
  int state;
} event_t;

static void event_init(event_t* ev)
{
  ev->state = 0;
}

static void event_reset(event_t* ev)
{
  ev->state = 0;
}

static void event_set(event_t* ev)
{
  if (ev->state)
    exit(1);
  __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE);
  syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG);
}

static void event_wait(event_t* ev)
{
  while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
    syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0);
}

static int event_isset(event_t* ev)
{
  return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE);
}

static int event_timedwait(event_t* ev, uint64_t timeout)
{
  uint64_t start = current_time_ms();
  uint64_t now = start;
  for (;;) {
    uint64_t remain = timeout - (now - start);
    struct timespec ts;
    ts.tv_sec = remain / 1000;
    ts.tv_nsec = (remain % 1000) * 1000 * 1000;
    syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts);
    if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED))
      return 1;
    now = current_time_ms();
    if (now - start > timeout)
      return 0;
  }
}

struct thread_t {
  int created, call;
  event_t ready, done;
};

static struct thread_t threads[16];
static void execute_call(int call);
static int running;

static void* thr(void* arg)
{
  struct thread_t* th = (struct thread_t*)arg;
  for (;;) {
    event_wait(&th->ready);
    event_reset(&th->ready);
    execute_call(th->call);
    __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED);
    event_set(&th->done);
  }
  return 0;
}

static void loop(void)
{
  int i, call, thread;
  for (call = 0; call < 4; call++) {
    for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0]));
         thread++) {
      struct thread_t* th = &threads[thread];
      if (!th->created) {
        th->created = 1;
        event_init(&th->ready);
        event_init(&th->done);
        event_set(&th->done);
        thread_start(thr, th);
      }
      if (!event_isset(&th->done))
        continue;
      event_reset(&th->done);
      th->call = call;
      __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED);
      event_set(&th->ready);
      event_timedwait(&th->done, 45);
      break;
    }
  }
  for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++)
    sleep_ms(1);
}

uint64_t r[1] = {0xffffffffffffffff};

void execute_call(int call)
{
  long res;
  switch (call) {
  case 0:
    memcpy((void*)0x20000040, "/dev/rtc0", 10);
    res = syscall(__NR_openat, 0xffffffffffffff9c, 0x20000040, 0, 0);
    if (res != -1)
      r[0] = res;
    break;
  case 1:
    syscall(__NR_ioctl, r[0], 0x7003);
    break;
  case 2:
    *(uint32_t*)0x20000000 = 0;
    *(uint32_t*)0x20000004 = 0;
    *(uint32_t*)0x20000008 = 0;
    *(uint32_t*)0x2000000c = 0x1f;
    *(uint32_t*)0x20000010 = 0;
    *(uint32_t*)0x20000014 = 0xa0;
    *(uint32_t*)0x20000018 = 0;
    *(uint32_t*)0x2000001c = 0;
    *(uint32_t*)0x20000020 = 0;
    syscall(__NR_ioctl, r[0], 0x4024700a, 0x20000000);
    break;
  case 3:
    memcpy((void*)0x200000c0,
           "\x2a\xf4\x9e\xe8\x10\xa9\xdb\x8a\xd3\x10\x2f\x61\x86\xaa\x74\x0f"
           "\xf0\x9a\x70\x78\x21\x19\xad\xe7\xa3\xb6\x79\x3d\x91\xaf\xcd\x26"
           "\xe6\x3f\xeb\x21\xf7\x60\xbf\xe8\x45\x3a\x86\xc5\xfb\xc7\xcb\x66"
           "\x39\x41\x6e\x63\x70\xa4\x71\x34\xee\xef\xbe\x23\xbe\x3a\x8d\xf8"
           "\x0b\xcf\x38\xe3\x1d\xb8\x51\x73\x54\x80\xfa\x50\xb1\x99\xdb\x1e"
           "\xa6\xf1\x7a\x36\x1e\xbb\x5e\xfd\x4b\xaf\x08\x31\x07\xcf\x22\xc7"
           "\xb9\xc2\xa0\x9d\xb8\x3f\x6c\xa2\xb1\xdb\x67\x5e\xf4\xe2\xf9\x90"
           "\x83\x75\x63\xb6\xeb\x49\xb7\xc7\xa7\x50\x3a\x0a\xff\x9f\xda\xc4"
           "\x8e\x96\xd9\x30\xfe\x55\xb5\x95\x83\x94\xb4\x92\x8b\x14\x3c\xb7"
           "\x34\xc1\xbf\x7e\x63\x9b\x25\x74\x64\x5d\xd1\xf9\x06\x75\x21\xec"
           "\x70\xde\x55\xb1\xbc\x07\xe0\xd9\x7c\x0c\x49\xc3\x19\x74\x0d\xa6"
           "\xb9\xc6\xd7\xe9\x19\x85\x38\x97\x24\x4d\x6e\x2c\x99\x5f\x57\xe1"
           "\xb4\xa4\xd2\x69\x93\xcd\x58\x6f\x1a\x89\xeb\x9e\x0b\x14\x47\x01"
           "\x83\x51\xbc\x3c\xe1\x9e\x95\x43\x80\x62\xb3\x0a\x76\x3b\x94\x1e"
           "\x7b\x8d\xbd\x3f\x9f\x90\xf1\x12\x8b\xf7\xb8\xa9\x3d\x32\x2e\xac"
           "\xe1\xa9\xb4\xb8\x55\x07\x26\x47\x6a\x08\xa3\xbc\xb6\x7f\x07\x8b",
           256);
    syscall(__NR_ioctl, r[0], 0x41009432, 0x200000c0);
    break;
  }
}
int main(void)
{
  syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0);
  loop();
  return 0;
}
