#include <asm/cacheflush.h>
 #include <asm/syscall.h>       /* for syscall_get_* */
 
+#include "time/posix-timers.h"
+
 /*
  * SLAB caches for signal bits.
  */
                kick_process(t);
 }
 
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q);
+
+static void sigqueue_free_ignored(struct task_struct *tsk, struct sigqueue *q)
+{
+       if (likely(!(q->flags & SIGQUEUE_PREALLOC) || q->info.si_code != SI_TIMER))
+               __sigqueue_free(q);
+       else
+               posixtimer_sig_ignore(tsk, q);
+}
+
 /* Remove signals in mask from the pending set and queue. */
 static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct sigpending *s)
 {
        list_for_each_entry_safe(q, n, &s->list, list) {
                if (sigismember(mask, q->info.si_signo)) {
                        list_del_init(&q->list);
-                       __sigqueue_free(q);
+                       sigqueue_free_ignored(p, q);
                }
        }
 }
        int sig = q->info.si_signo;
        struct task_struct *t;
        unsigned long flags;
-       int ret, result;
+       int result;
 
        guard(rcu)();
 
         */
        tmr->it_sigqueue_seq = tmr->it_signal_seq;
 
-       ret = 1; /* the signal is ignored */
+       /*
+        * Set the signal delivery status under sighand lock, so that the
+        * ignored signal handling can distinguish between a periodic and a
+        * non-periodic timer.
+        */
+       tmr->it_sig_periodic = tmr->it_status == POSIX_TIMER_REQUEUE_PENDING;
+
        if (!prepare_signal(sig, t, false)) {
                result = TRACE_SIGNAL_IGNORED;
+
+               /* Paranoia check. Try to survive. */
+               if (WARN_ON_ONCE(!list_empty(&q->list)))
+                       goto out;
+
+               /* Periodic timers with SIG_IGN are queued on the ignored list */
+               if (tmr->it_sig_periodic) {
+                       /*
+                        * Already queued means the timer was rearmed after
+                        * the previous expiry got it on the ignore list.
+                        * Nothing to do for that case.
+                        */
+                       if (hlist_unhashed(&tmr->ignored_list)) {
+                               /*
+                                * Take a signal reference and queue it on
+                                * the ignored list.
+                                */
+                               posixtimer_sigqueue_getref(q);
+                               posixtimer_sig_ignore(t, q);
+                       }
+               } else if (!hlist_unhashed(&tmr->ignored_list)) {
+                       /*
+                        * Covers the case where a timer was periodic and
+                        * then the signal was ignored. Later it was rearmed
+                        * as oneshot timer. The previous signal is invalid
+                        * now, and this oneshot signal has to be dropped.
+                        * Remove it from the ignored list and drop the
+                        * reference count as the signal is not longer
+                        * queued.
+                        */
+                       hlist_del_init(&tmr->ignored_list);
+                       posixtimer_putref(tmr);
+               }
                goto out;
        }
 
-       ret = 0;
+       /* This should never happen and leaks a reference count */
+       if (WARN_ON_ONCE(!hlist_unhashed(&tmr->ignored_list)))
+               hlist_del_init(&tmr->ignored_list);
+
        if (unlikely(!list_empty(&q->list))) {
                /* This holds a reference count already */
                result = TRACE_SIGNAL_ALREADY_PENDING;
 out:
        trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
        unlock_task_sighand(t, &flags);
-       return ret;
+       return 0;
+}
+
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q)
+{
+       struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+       /*
+        * If the timer is marked deleted already or the signal originates
+        * from a non-periodic timer, then just drop the reference
+        * count. Otherwise queue it on the ignored list.
+        */
+       if (tmr->it_signal && tmr->it_sig_periodic)
+               hlist_add_head(&tmr->ignored_list, &tsk->signal->ignored_posix_timers);
+       else
+               posixtimer_putref(tmr);
 }
 
 static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
        }
 }
 #else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q) { }
 static inline void posixtimer_sig_unignore(struct task_struct *tsk, int sig) { }
 #endif /* !CONFIG_POSIX_TIMERS */