What is a Wait Channel?
Threads in the kernel can wait for resources using various APIs such as tsleep() or cv_wait(). The resource being waited on is associated with a pointer in the kernel's virtual address space. These pointers are known as wait channels. In addition to the raw pointer itself, each thread stores a reference to a short string describing the wait channel. This string is known as a wait message.
Wait Channels of Active Threads
There are various ways of determining which wait channel a thread is waiting on. The raw pointer can be displayed via the "nwchan" keyword for ps(1):
> ps O nwchan PID NWCHAN TT STAT TIME COMMAND 6515 - v0 Z 0:00.19 <defunct> 6517 fffff8000deffa18 v0 IW 0:00.00 -tcsh (tcsh) 6545 fffff8000dbea970 v0 IW+ 0:00.00 /bin/sh /usr/local/bin/startx ...
The wait message is available via the "wchan" keyword:
> ps O wchan PID WCHAN TT STAT TIME COMMAND 6515 - v0 Z 0:00.19 <defunct> 6517 pause v0 IW 0:00.00 -tcsh (tcsh) 6545 wait v0 IW+ 0:00.00 /bin/sh /usr/local/bin/startx ...
The ps(1) command also includes another keyword called "mwchan". This keyword displays the wait message for a thread blocked on a wait channel. For a thread blocked on a mutex, read-mostly lock or read/write lock, the name of the lock is displayed after a "*" prefix. Note that other locks such as lockmanager and shared/exclusive locks are implemented using wait channels, so threads blocked on one of those locks display the lock name as the wait message without a "*" prefix.
In addition, the STATE column in top(1) displays the same data as the "mwchan" keyword for threads blocked on either a wait channel or a lock. Wait messages generally use lower-case letters in contrast to the non-waiting status messages displayed in this column such as CPU x, RUN, or WAIT.
PID USERNAME PRI NICE SIZE RES STATE C TIME WCPU COMMAND 11 root 155 ki31 0K 64K CPU3 3 275.1H 92.09% idle{idle: cpu 11 root 155 ki31 0K 64K RUN 2 273.6H 89.45% idle{idle: cpu 11 root 155 ki31 0K 64K RUN 0 273.6H 87.89% idle{idle: cpu 11 root 155 ki31 0K 64K RUN 1 273.6H 86.77% idle{idle: cpu 6692 john 39 0 445M 119M CPU1 1 267:50 22.07% akonadi_imap_r 6637 john 33 0 217M 73660K CPU2 2 158:27 15.19% akonadiserver{ 6638 john 27 0 242M 153M sbwait 0 204:18 7.86% mysqld{mysqld} 6692 john 24 0 445M 119M select 0 3:22 4.49% akonadi_imap_r 6559 john 21 0 3330M 101M select 3 127:07 0.88% Xorg 6622 john 20 0 613M 93880K select 3 85:02 0.29% kwin{kwin} 6629 john 20 0 849M 154M select 3 49:13 0.10% kdeinit4{kdein 6638 john 20 0 242M 153M sbwait 2 219:54 0.00% mysqld{mysqld} 7546 john 20 0 505M 86916K select 3 48:41 0.00% kdeinit4{kdein 12 root -96 - 0K 320K WAIT 3 42:18 0.00% intr{irq264: v 1765 root 20 0 28876K 4180K select 1 37:53 0.00% hald-addon-mou 8 root 16 - 0K 16K syncer 3 32:32 0.00% syncer 6692 john 20 0 445M 119M select 3 17:17 0.00% akonadi_imap_r
When a process is sent SIGINFO, the kernel outputs the wait channel of the "most interesting" thread in the process on the terminal. Shells send SIGINFO to the running process when Ctrl+T is pressed:
> cat load: 0.07 cmd: cat 44115 [ttyin] 0.67r 0.00u 0.00s 0% 1532k
The procstat(1) utility can also be used to query the wait channel of processes either via its default output or via the -t flag:
> procstat -t `pgrep firefox` PID TID COMM TDNAME CPU PRI STATE WCHAN 16864 100646 firefox - 1 120 sleep select 16864 100723 firefox Gecko_IOThread 0 124 sleep kqread 16864 100724 firefox Socket Thread 1 120 sleep select 16864 100725 firefox JS GC Helper 0 120 sleep uwait 16864 100726 firefox JS Watchdog 0 120 sleep uwait 16864 100727 firefox Hang Monitor 3 122 sleep uwait 16864 100728 firefox Analysis Helper 2 120 sleep uwait ...
In the in-kernel debugger, DDB, wait channels for threads are displayed via the ps, show proc, and show thread commands.
In kgdb(1), the wait message of a given thread can be found by the string pointed to by the td_wmesg field of the associated thread structure. The wait channel pointer can be found via the td_wchan field.
Finding Wait Channels
Wait channels are associated with wait messages in the kernel source code. To determine the meaning of an unfamiliar wait channel, use a tool like grep(1) to search for the wait message string in the kernel sources. Once you have found the relevant string, examine the nearby code to determine what the thread is waiting for. For example, the "biord" message is used here:
> grep -4Hr biord /sys /sys/kern/vfs_bio.c-int /sys/kern/vfs_bio.c-bufwait(struct buf *bp) /sys/kern/vfs_bio.c-{ /sys/kern/vfs_bio.c- if (bp->b_iocmd == BIO_READ) /sys/kern/vfs_bio.c: bwait(bp, PRIBIO, "biord"); /sys/kern/vfs_bio.c- else /sys/kern/vfs_bio.c- bwait(bp, PRIBIO, "biowr"); /sys/kern/vfs_bio.c- if (bp->b_flags & B_EINTR) { /sys/kern/vfs_bio.c- bp->b_flags &= ~B_EINTR;
Common Wait Channels
This is a table of some of the common wait channel messages one might see in a system and a brief description of what state the associated thread is in (for example, which resource a thread is waiting on). Note that this list is not exhaustive.
Wait Channel |
Description |
biord |
Waiting for a disk read operation to complete |
biowr |
Waiting for a disk write operation to complete |
nfsreq |
Waiting for a reply to an NFS request |
wdrain |
Waiting for pending writes to a file to drain to disk |
flushbuf |
Waiting for pending writes to a file to drain to disk so the pages can be flushed from RAM via posix_fadvise(2) |
sbwait |
Waiting for data to arrive on a socket, e.g. a blocking read(2) on a socket |
kqread |
Waiting for an event to arrive on a kqueue |
select |
Waiting for an event to trigger on a file descriptor via select(2) or poll(2), usually a socket |
piperd |
Waiting for data to arrive on a pipe, i.e. a blocking read(2) |
pipewr |
Waiting for room to write data into a pipe, i.e. a blocking write(2) |
fifoor |
Waiting in a blocking read-only open of a FIFO (O_RDONLY) for another process to open a writable descriptor on this FIFO (O_RDWR or O_WRONLY) |
fifoow |
Waiting in a blocking write-only open of a FIFO (O_WRONLY) for another process to open a readable descriptor on this FIFO (O_RDWR or O_RDONLY) |
wait |
Waiting for a child process to exit, e.g. a shell waiting for a command to complete |
ttyin |
Waiting to read data from a terminal, e.g. idle shell for an ssh session or xterm |
ttyout |
Waiting to write data to a terminal |
pause |
Waiting for a signal (tcsh uses this instead of "wait") |
nanslp |
Waiting for a specified amount of time to pass, e.g. "sleep 1" |
ucond |
Waiting for a userland condition variable (pthread_cond_wait()) |
getblk |
Waiting to access file data that is being accessed by another thread |
ufs |
Waiting to read from a file or directory on a UFS filesystem that is being written to by another thread, or waiting to write to a local file or directory that is being read or written by another thread |
nfs |
Same as "ufs" but for an NFS file or directory. Often if you see this it's because the other thread is blocked in "nfsreq". |
zfs |
Same as "ufs" but for a ZFS file or directory. |