Skip to content

Commit

Permalink
Annotate tasks which require KM_PUSHPAGE
Browse files Browse the repository at this point in the history
When the txg_sync thread issues an I/O and blocks in zio_wait().
Then it is critical that all processes involved in handling that
I/O use KM_PUSHPAGE when performing allocations.

If they use KM_SLEEP then it is possible that during a low memory
condition direct reclaim will be invoked and it may attempt to
flush dirty data to the file system.  This will result in the
thread attempting to assign a TX will block until the txg_sync
thread completes.

The end result is a deadlock with the txg_sync thread blocked on
the original I/O, and the taskq thread blocked on the txg_sync
thread.

To prevent developers from accidentally introducing this type
of deadlock.  This patch passes the TQ_PUSHPAGE_THREAD flag when
dispatching a I/O to a taskq which the txg_sync thread is waiting
on.  This causes the taskq thread to set the PF_NOFS flag in its
task struct while the zio_execute() function is being executed.
Thereby ensuring that any accidental misuse of the KM_SLEEP is
quickly causes and fixed.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue openzfs#1928
  • Loading branch information
behlendorf committed Jan 16, 2014
1 parent 35d3e32 commit 198862a
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/sys/zfs_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ typedef struct taskq_ent {
#define TQ_PUSHPAGE KM_PUSHPAGE /* Cannot perform I/O */
#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
#define TQ_FRONT 0x08 /* Queue in front */
#define TQ_PUSHPAGE_THREAD 0x0

extern taskq_t *system_taskq;

Expand Down
13 changes: 13 additions & 0 deletions module/zfs/zio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,8 @@ zio_taskq_dispatch(zio_t *zio, zio_taskq_type_t q, boolean_t cutinline)
{
spa_t *spa = zio->io_spa;
zio_type_t t = zio->io_type;
zio_t *lio = zio->io_logical;
dsl_pool_t *dp;
int flags = (cutinline ? TQ_FRONT : 0);

/*
Expand All @@ -1239,6 +1241,17 @@ zio_taskq_dispatch(zio_t *zio, zio_taskq_type_t q, boolean_t cutinline)

ASSERT3U(q, <, ZIO_TASKQ_TYPES);

/*
* If the root waiter for this I/O is the txg_sync thread the pass the
* TQ_PUSHPAGE_THREAD flag. This enables run time verification that
* the work function never allocations memory using KM_SLEEP. This
* could lead to a deadlock if direct memory reclaim issues an I/O
* in the work function which blocks on the txg_sync thread.
*/
dp = spa_get_dsl(spa);
if (lio && dp && lio->io_waiter == dp->dp_tx.tx_sync_thread)
flags |= TQ_PUSHPAGE_THREAD;

/*
* NB: We are assuming that the zio can only be dispatched
* to a single taskq at a time. It would be a grievous error
Expand Down

0 comments on commit 198862a

Please sign in to comment.