Author: mjg
Date: Tue Oct 13 20:40:09 2020
New Revision: 366685
URL: https://svnweb.freebsd.org/changeset/base/366685

Log:
  FreeBSD: fix panic due to tqid overflow
  
  The 32-bit counter eventually wraps to 0 which is a sentinel for invalid
  id.
  
  Make it 64-bit on LP64 platforms and 0-check otherwise.
  
  Note: Linux counterpart uses id stored per queue instead of a global.
  I did not check going that way is feasible with the goal being the
  minimal fix doing the job.
  
  Reported by:  YAMAMOTO Shigeru <shig...@os-hackers.jp>
  Reviewed by:  mav
  Differential Revision:        https://reviews.freebsd.org/D26759

Modified:
  head/sys/contrib/openzfs/module/os/freebsd/spl/spl_taskq.c

Modified: head/sys/contrib/openzfs/module/os/freebsd/spl/spl_taskq.c
==============================================================================
--- head/sys/contrib/openzfs/module/os/freebsd/spl/spl_taskq.c  Tue Oct 13 
20:04:13 2020        (r366684)
+++ head/sys/contrib/openzfs/module/os/freebsd/spl/spl_taskq.c  Tue Oct 13 
20:40:09 2020        (r366685)
@@ -67,7 +67,7 @@ static unsigned long tqenthash;
 static unsigned long tqenthashlock;
 static struct sx *tqenthashtbl_lock;
 
-static uint32_t tqidnext = 1;
+static taskqid_t tqidnext;
 
 #define        TQIDHASH(tqid) (&tqenthashtbl[(tqid) & tqenthash])
 #define        TQIDHASHLOCK(tqid) (&tqenthashtbl_lock[((tqid) & 
tqenthashlock)])
@@ -90,7 +90,6 @@ system_taskq_init(void *arg)
            M_TASKQ, M_WAITOK | M_ZERO);
        for (i = 0; i < tqenthashlock + 1; i++)
                sx_init_flags(&tqenthashtbl_lock[i], "tqenthash", SX_DUPOK);
-       tqidnext = 1;
        taskq_zone = uma_zcreate("taskq_zone", sizeof (taskq_ent_t),
            NULL, NULL, NULL, NULL,
            UMA_ALIGN_CACHE, 0);
@@ -121,6 +120,28 @@ system_taskq_fini(void *arg)
 SYSUNINIT(system_taskq_fini, SI_SUB_CONFIGURE, SI_ORDER_ANY, system_taskq_fini,
     NULL);
 
+#ifdef __LP64__
+static taskqid_t
+__taskq_genid(void)
+{
+
+       return (atomic_fetchadd_long(&tqidnext, 1) + 1);
+}
+#else
+static taskqid_t
+__taskq_genid(void)
+{
+       taskqid_t tqid;
+
+       for (;;) {
+               tqid = atomic_fetchadd_int(&tqidnext, 1) + 1;
+               if (__predict_true(tqid != 0))
+                       break;
+       }
+       return (tqid);
+}
+#endif
+
 static taskq_ent_t *
 taskq_lookup(taskqid_t tqid)
 {
@@ -140,8 +161,10 @@ taskq_lookup(taskqid_t tqid)
 static taskqid_t
 taskq_insert(taskq_ent_t *ent)
 {
-       taskqid_t tqid = atomic_fetchadd_int(&tqidnext, 1);
+       taskqid_t tqid;
 
+       tqid = __taskq_genid();
+       VERIFY(tqid);
        ent->tqent_id = tqid;
        ent->tqent_registered = B_TRUE;
        sx_xlock(TQIDHASHLOCK(tqid));
@@ -289,7 +312,7 @@ taskq_dispatch_delay(taskq_t *tq, task_func_t func, vo
     uint_t flags, clock_t expire_time)
 {
        taskq_ent_t *task;
-       taskqid_t tid;
+       taskqid_t tqid;
        clock_t timo;
        int mflag;
 
@@ -310,13 +333,13 @@ taskq_dispatch_delay(taskq_t *tq, task_func_t func, vo
        task->tqent_type = TIMEOUT_TASK;
        task->tqent_cancelled = B_FALSE;
        refcount_init(&task->tqent_rc, 1);
-       tid = taskq_insert(task);
+       tqid = taskq_insert(task);
        TIMEOUT_TASK_INIT(tq->tq_queue, &task->tqent_timeout_task, 0,
            taskq_run, task);
 
        taskqueue_enqueue_timeout(tq->tq_queue, &task->tqent_timeout_task,
            timo);
-       return (tid);
+       return (tqid);
 }
 
 taskqid_t
@@ -324,7 +347,7 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *ar
 {
        taskq_ent_t *task;
        int mflag, prio;
-       taskqid_t tid;
+       taskqid_t tqid;
 
        if ((flags & (TQ_SLEEP | TQ_NOQUEUE)) == TQ_SLEEP)
                mflag = M_WAITOK;
@@ -344,11 +367,10 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *ar
        task->tqent_arg = arg;
        task->tqent_cancelled = B_FALSE;
        task->tqent_type = NORMAL_TASK;
-       tid = taskq_insert(task);
+       tqid = taskq_insert(task);
        TASK_INIT(&task->tqent_task, prio, taskq_run, task);
        taskqueue_enqueue(tq->tq_queue, &task->tqent_task);
-       VERIFY(tid);
-       return (tid);
+       return (tqid);
 }
 
 static void
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to