aboutsummaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/spl/spl-taskq.c47
-rw-r--r--module/splat/splat-taskq.c29
2 files changed, 48 insertions, 28 deletions
diff --git a/module/spl/spl-taskq.c b/module/spl/spl-taskq.c
index 201cb5949..5a17f1ccf 100644
--- a/module/spl/spl-taskq.c
+++ b/module/spl/spl-taskq.c
@@ -78,35 +78,36 @@ retry:
if (flags & TQ_NOSLEEP)
SRETURN(NULL);
- /* Sleep periodically polling the free list for an available
- * spl_task_t. If a full second passes and we have not found
- * one gives up and return a NULL to the caller. */
- if (flags & TQ_SLEEP) {
- spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
- schedule_timeout(HZ / 100);
- spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
- if (count < 100)
- SGOTO(retry, count++);
-
- SRETURN(NULL);
- }
-
- /* Unreachable, Neither TQ_SLEEP or TQ_NOSLEEP set */
- PANIC("Neither TQ_SLEEP or TQ_NOSLEEP set");
+ /*
+ * Sleep periodically polling the free list for an available
+ * spl_task_t. Dispatching with TQ_SLEEP should always succeed
+ * but we cannot block forever waiting for an spl_taskq_t to
+ * show up in the free list, otherwise a deadlock can happen.
+ *
+ * Therefore, we need to allocate a new task even if the number
+ * of allocated tasks is above tq->tq_maxalloc, but we still
+ * end up delaying the task allocation by one second, thereby
+ * throttling the task dispatch rate.
+ */
+ spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
+ schedule_timeout(HZ / 100);
+ spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
+ if (count < 100)
+ SGOTO(retry, count++);
}
- spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
+ spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
t = kmem_alloc(sizeof(spl_task_t), flags & (TQ_SLEEP | TQ_NOSLEEP));
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
- if (t) {
- spin_lock_init(&t->t_lock);
+ if (t) {
+ spin_lock_init(&t->t_lock);
INIT_LIST_HEAD(&t->t_list);
- t->t_id = 0;
- t->t_func = NULL;
- t->t_arg = NULL;
- tq->tq_nalloc++;
- }
+ t->t_id = 0;
+ t->t_func = NULL;
+ t->t_arg = NULL;
+ tq->tq_nalloc++;
+ }
SRETURN(t);
}
diff --git a/module/splat/splat-taskq.c b/module/splat/splat-taskq.c
index d4540f37a..a7c537236 100644
--- a/module/splat/splat-taskq.c
+++ b/module/splat/splat-taskq.c
@@ -296,6 +296,10 @@ splat_taskq_test3(struct file *file, void *arg)
* Then use taskq_wait() to block until all the tasks complete, then
* cross check that all the tasks ran by checking tg_arg->count which
* is incremented in the task function. Finally cleanup the taskq.
+ *
+ * First we try with a large 'maxalloc' value, then we try with a small one.
+ * We should not drop tasks when TQ_SLEEP is used in taskq_dispatch(), even
+ * if the number of pending tasks is above maxalloc.
*/
static void
splat_taskq_test4_func(void *arg)
@@ -307,16 +311,18 @@ splat_taskq_test4_func(void *arg)
}
static int
-splat_taskq_test4(struct file *file, void *arg)
+splat_taskq_test4_common(struct file *file, void *arg, int minalloc,
+ int maxalloc, int nr_tasks)
{
taskq_t *tq;
splat_taskq_arg_t tq_arg;
int i, j, rc = 0;
- splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' creating\n",
- SPLAT_TASKQ_TEST4_NAME);
+ splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' creating "
+ "(%d/%d/%d)\n", SPLAT_TASKQ_TEST4_NAME, minalloc, maxalloc,
+ nr_tasks);
if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
- 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
+ minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
"Taskq '%s' create failed\n",
SPLAT_TASKQ_TEST4_NAME);
@@ -326,7 +332,7 @@ splat_taskq_test4(struct file *file, void *arg)
tq_arg.file = file;
tq_arg.name = SPLAT_TASKQ_TEST4_NAME;
- for (i = 1; i <= 1024; i *= 2) {
+ for (i = 1; i <= nr_tasks; i *= 2) {
atomic_set(&tq_arg.count, 0);
splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
"Taskq '%s' function '%s' dispatched %d times\n",
@@ -364,6 +370,19 @@ out:
return rc;
}
+static int splat_taskq_test4(struct file *file, void *arg)
+{
+ int rc;
+
+ rc = splat_taskq_test4_common(file, arg, 50, INT_MAX, 1024);
+ if (rc)
+ return rc;
+
+ rc = splat_taskq_test4_common(file, arg, 1, 1, 32);
+
+ return rc;
+}
+
/*
* Create a taskq and dispatch a specific sequence of tasks carefully
* crafted to validate the order in which tasks are processed. When