Bcc: biosnoop/biotop to use tracepoints

Created on 29 Nov 2016  路  31Comments  路  Source: iovisor/bcc

Low priority enhancement...

For 4.7+ kernels, biosnoop & biotop should use the block: tracepoints. Probably block:block_rq_issue and block:block_rq_complete. It might make sense to bump the type of I/O field ("T" and "D") from 2 characters to 4, and then just print the tracepoint's rwbs field in that.

Older versions of biosnoop/biotop should be put in tools/old for 4.6 and earlier kernels.

enhancement low

Most helpful comment

Someone else at Facebook ran into this so I just wanted to share, flushes in the kernel are special-cased so the events are a little tricky.

There are two flushing request flags: REQ_PREFLUSH (called REQ_FLUSH in older kernels), which means flush the disk's write cache before writing the request data; and REQ_FUA, which means this write should bypass the write cache. Depending on what the underlying hardware supports, one of these requests might turn into 0-3 different steps.

For example, a REQ_OP_WRITE | REQ_PREFLUSH request with no data is a simple request to flush the disk cache. If the disk does not have a writeback cache, this is a noop, so the request can be completed without ever being issued. If the disk does support a writeback cache, we don't issue this request directly, but rather a separate "flush request" which happens to have a sector of -1. We then complete the original request.

A REQ_OP_WRITE | REQ_FUA request with data also depends on the hardware. If the hardware supports FUA, the original request can be issued directly. If it does not, the request is translated to a write followed by a full flush, which means we issue and complete the original request, and then we issue and complete a special flush request like for preflush.

Of course, these can be combined, so a REQ_OP_WRITE | REQ_PREFLUSH | REQ_FUA request with data on a device with a writeback cache without FUA support would turn into a preflush, a normal write, and a postflush.

The request that XFS is making here is the first example, a simple flush with no data. So the "FW" is the original request, then the "FF" is the special flush request, which is REQ_OP_FLUSH | REQ_PREFLUSH.

The moral of the story is that flushes are messy, so they're going to have to be a special case in these tools. So just keep in mind that

  1. A REQ_PREFLUSH request with no data will be inserted and completed without being issued.
  2. Depending on the hardware, a separate REQ_OP_FLUSH request (see here) may be issued after the original REQ_PREFLUSH request is inserted.
  3. Similarly, depending on the hardware, a separate REQ_OP_FLUSH request may be issued after a REQ_FUA request is completed.
  4. We fiddle with the request flags in multiple places in the flush code, so you can't rely on that to track a request (e.g. here, here and here).
  5. This may change in the future, so you should handle unexpected sequences.

All 31 comments

s/It might make sense to/I'm pretty sure we should/

I wouldn't mind tackling this, just wanted to make sure we're on the same page. Currently, there are four kernel functions that these tools attach to:

  • blk_account_io_start -- to associate the struct req with a pid/comm
  • blk_start_request and blk_mq_start_request to trace the request start
  • blk_account_io_completion to trace the request end

Instead, we'd just attach to the block:block_rq_issue and block:block_rq_complete tracepoints. These have the dev_t rather than the disk name -- how would we translate back to the disk name?

I guess we could do MAJOR(args->dev) and MINOR(args->dev) and then grab these values and look in /proc/partitions. Feels a bit like a hack, but would probably work (from limited testing). Is that the way you'd want to go? @brendangregg

Thanks @goldshtn , please take a swing at it if it interests you. We need at least three tracepoints. Here's what I think they would be and what I think they do:

So biolatency would do issue->complete by default, and insert->complete for -Q.

biosnoop only does device latency right now, but it would be good to have a -Q option. Here's a mockup where I'm adding it as an extra column:

# ./biosnoop -Q
TIME(s)        COMM           PID    DISK    T  SECTOR    BYTES  QLAT(ms)  LAT(ms)
0.000004001    supervise      1950   xvda1   W  13092560  4096       0.01     0.74
0.000178002    supervise      1950   xvda1   W  13092432  4096       1.03     0.61
0.001469001    supervise      1956   xvda1   W  13092440  4096       0.63     1.24
0.001588002    supervise      1956   xvda1   W  13115128  4096       0.01     1.09

It's over 80 char width, but OTOH, we're not breaking the default output -- you need a -Q to do this.

I think the /proc/partitions cache and using major/minor from the tracepoints is probably the best option. I'm sure I could dig it out of the dev_t, but that would be unstable, and /proc/partitions & major/minor numbers are stable. Plus we're using tracepoints, which are also stable, so wouldn't it be nice if these scripts were "done" and don't break in the future. :)

Come to think of it, how do we correlate the issue and complete events, or the insert and complete events? The current tools do this using struct request, but the tracepoints don't have a struct request. @brendangregg

Ah, right, it can be associated using an ID from: device (dev_t pointer) + sector. I don't know how important it is, but one could include size and rwbs field as well.

Couldn't we theoretically have multiple requests in flight with these parameters being the same? I'm just saying that we would lose consistency compared to tracing struct request.

Maybe. But:

A) That's how my old iosnoop has worked forever (shipped with OSX, Oracle Solaris, etc), and for the same reasons: using what was available in the static probe.

B) We could (now or later) write a script to check if two I/O can be in-flight for the same block address. Especially if someone says: this iosnoop output doesn't make sense. In fact, easiest might be an awk script to process the output of:

perf record -e block:block_rq_issue -e block:block_rq_complete -a -- sleep 60; perf script

Which will have the timestamps, major/minor numbers, and block addresses in the output.

OK, so I sort of ported biosnoop but I'm seeing an issue. The sector number in block_rq_insert doesn't match up with the sector number in block_rq_issue. Example trace output while I am hitting :w in vim:

# /usr/share/bcc/tools/trace 't:block:block_rq_insert "insert dev=%d sec=%d", args->dev, args->sector' 't:block:block_rq_issue "issue dev=%d sec=%d", args->dev, args->sector'
PID    TID    COMM         FUNC             -
29746  29746  kworker/0:2  block_rq_insert  insert dev=0 sec=0
29746  29746  kworker/0:2  block_rq_issue   issue dev=0 sec=0
29746  29746  kworker/0:2  block_rq_insert  insert dev=0 sec=0
29746  29746  kworker/0:2  block_rq_issue   issue dev=0 sec=0
14139  14139  vim          block_rq_insert  insert dev=8388608 sec=9163536
14139  14139  vim          block_rq_insert  insert dev=8388608 sec=0
14139  14139  vim          block_rq_issue   issue dev=8388608 sec=9163536
14139  14139  vim          block_rq_issue   issue dev=8388608 sec=-1
29746  29746  kworker/0:2  block_rq_insert  insert dev=8388608 sec=22417744
29746  29746  kworker/0:2  block_rq_issue   issue dev=8388608 sec=22417744

Note vim's lines: where there is a sector, it matches up OK in both probes, but where there isn't a sector, we get 0 in the insert and -1 in the issue. Thoughts? @brendangregg

Now that I'm tracing the rwbs field, it also changes: from FW in the insert, to FF in the issue.

This is the current code if you'd like to take a look: https://github.com/goldshtn/bcc/tree/bio-tracepoints

just a FYI the new version the pid is not showing correctly compared to biosnoop. I'm running 4.8.0-45-generic, the trace command above does show the correct pid.

from trace:
46525 46525 dd block_rq_insert insert dev=8388608 sec=15125744
from ps
root 46525 6638 4 16:40 pts/2 00:00:00 dd if=/dev/zero of=/var/tmp/a bs=8192 count=2000 oflag=direct

from biosnoop
0.782716000 dd 51380 sda W 15124704 8192 0.23
from ps:
root 51380 6476 2 16:43 pts/1 00:00:00 dd if=/dev/zero of=/var/tmp/a bs=8192 count=2000 oflag=direct

new version looks to be picking up the kworker thread.
from ps:
root 52926 6638 3 16:44 pts/2 00:00:00 dd if=/dev/zero of=/var/tmp/a bs=8192 count=2000 oflag=direct

from biosnoop_new:
19.941982529 kworker/1:1H 558 sda WS 15156464 8192 0.000

doh!! running dd without oflag=direct shows the dd process as the pid issuing the I/O. That would seem correct as its writing to the page cache. perf record shows the same as the new version.

@brendangregg Ping -- I'd like to move this forward, but I'm stuck with the issue above, where the block_rq_issue and block_rq_insert parameters do not match up. Any thoughts?

I'll have to look at stack traces for those 0 sector requests; it could be a special non-I/O request, and we can special case it (ie, know that the return will be -1).

@brendangregg Here are the stacks -- captured while saving a file in vim:

# trace -K -p $(pidof vim) \
  't:block:block_rq_insert "insert sec=%d typ=%s", args->sector, args->rwbs' \
  't:block:block_rq_issue  "issue  sec=%d typ=%s", args->sector, args->rwbs'
PID    TID    COMM         FUNC             -
7686   7686   vim          block_rq_insert  insert sec=30862128 typ=WS
        __elv_add_request+0xbe [kernel]
        blk_queue_bio+0x3fa [kernel]
        generic_make_request+0xf2 [kernel]
        submit_bio+0x73 [kernel]
        xfs_submit_ioend+0x66 [kernel]
        xfs_vm_writepages+0xbe [kernel]
        do_writepages+0x1e [kernel]
        __filemap_fdatawrite_range+0xc6 [kernel]
        filemap_write_and_wait_range+0x41 [kernel]
        xfs_file_fsync+0x63 [kernel]
        vfs_fsync_range+0x49 [kernel]
        do_fsync+0x3d [kernel]
        sys_fsync+0x10 [kernel]
        do_syscall_64+0x67 [kernel]
        return_from_SYSCALL_64+0x0 [kernel]

7686   7686   vim          block_rq_insert  insert sec=0 typ=FW
        __elv_add_request+0xbe [kernel]
        blk_flush_plug_list+0x207 [kernel]
        blk_finish_plug+0x2c [kernel]
        _xfs_buf_ioapply+0x336 [kernel]
        xfs_buf_submit+0x69 [kernel]
        xlog_bdstrat+0x2b [kernel]
        xlog_sync+0x2bb [kernel]
        xlog_state_release_iclog+0x6d [kernel]
        _xfs_log_force_lsn+0x20d [kernel]
        xfs_file_fsync+0xe6 [kernel]
        vfs_fsync_range+0x49 [kernel]
        do_fsync+0x3d [kernel]
        sys_fsync+0x10 [kernel]
        do_syscall_64+0x67 [kernel]
        return_from_SYSCALL_64+0x0 [kernel]

7686   7686   vim          block_rq_issue   issue sec=30862128 typ=WS
        blk_peek_request+0x1fc [kernel]
        scsi_request_fn+0x3f [kernel]
        __blk_run_queue+0x33 [kernel]
        cfq_insert_request+0x2d7 [kernel]
        __elv_add_request+0x1bc [kernel]
        blk_queue_bio+0x3fa [kernel]
        generic_make_request+0xf2 [kernel]
        submit_bio+0x73 [kernel]
        xfs_submit_ioend+0x66 [kernel]
        xfs_vm_writepages+0xbe [kernel]
        do_writepages+0x1e [kernel]
        __filemap_fdatawrite_range+0xc6 [kernel]
        filemap_write_and_wait_range+0x41 [kernel]
        xfs_file_fsync+0x63 [kernel]
        vfs_fsync_range+0x49 [kernel]
        do_fsync+0x3d [kernel]
        sys_fsync+0x10 [kernel]
        do_syscall_64+0x67 [kernel]
        return_from_SYSCALL_64+0x0 [kernel]

7686   7686   vim          block_rq_issue   issue sec=-1 typ=FF
        blk_peek_request+0x1fc [kernel]
        scsi_request_fn+0x3f [kernel]
        __blk_run_queue+0x33 [kernel]
        queue_unplugged+0x2a [kernel]
        blk_flush_plug_list+0x22d [kernel]
        blk_finish_plug+0x2c [kernel]
        _xfs_buf_ioapply+0x336 [kernel]
        xfs_buf_submit+0x69 [kernel]
        xlog_bdstrat+0x2b [kernel]
        xlog_sync+0x2bb [kernel]
        xlog_state_release_iclog+0x6d [kernel]
        _xfs_log_force_lsn+0x20d [kernel]
        xfs_file_fsync+0xe6 [kernel]
        vfs_fsync_range+0x49 [kernel]
        do_fsync+0x3d [kernel]
        sys_fsync+0x10 [kernel]
        do_syscall_64+0x67 [kernel]
        return_from_SYSCALL_64+0x0 [kernel]
^C

Note the mismatched sector (0 and -1) and the mismatched type (FW and FF).

doing a few writes to a file on my system and I cant replicate this data below. 4.8.0-45-generic #48-Ubuntu

PID TID COMM FUNC -
20587 20587 vim block_rq_insert insert sec=24318352 typ=WS
__elv_add_request+0xc5 [kernel]
blk_flush_plug_list+0x238 [kernel]
blk_finish_plug+0x2c [kernel]
ext4_writepages+0x537 [kernel]
do_writepages+0x1e [kernel]
__filemap_fdatawrite_range+0xc6 [kernel]
filemap_write_and_wait_range+0x41 [kernel]
ext4_sync_file+0xe8 [kernel]
vfs_fsync_range+0x4b [kernel]
do_fsync+0x3d [kernel]
sys_fsync+0x10 [kernel]
entry_SYSCALL_64_fastpath+0x1e [kernel]

20587 20587 vim block_rq_issue issue sec=24318352 typ=WS
blk_peek_request+0x1ee [kernel]
scsi_request_fn+0x3e [kernel]
__blk_run_queue+0x33 [kernel]
queue_unplugged+0x2a [kernel]
blk_flush_plug_list+0x1ff [kernel]
blk_finish_plug+0x2c [kernel]
ext4_writepages+0x537 [kernel]
do_writepages+0x1e [kernel]
__filemap_fdatawrite_range+0xc6 [kernel]
filemap_write_and_wait_range+0x41 [kernel]
ext4_sync_file+0xe8 [kernel]
vfs_fsync_range+0x4b [kernel]
do_fsync+0x3d [kernel]
sys_fsync+0x10 [kernel]
entry_SYSCALL_64_fastpath+0x1e [kernel]

20587 20587 vim block_rq_insert insert sec=24317224 typ=WS
__elv_add_request+0xc5 [kernel]
blk_flush_plug_list+0x238 [kernel]
blk_finish_plug+0x2c [kernel]
ext4_writepages+0x537 [kernel]
do_writepages+0x1e [kernel]
__filemap_fdatawrite_range+0xc6 [kernel]
filemap_write_and_wait_range+0x41 [kernel]
ext4_sync_file+0xe8 [kernel]
vfs_fsync_range+0x4b [kernel]
do_fsync+0x3d [kernel]
sys_fsync+0x10 [kernel]
entry_SYSCALL_64_fastpath+0x1e [kernel]

20587 20587 vim block_rq_issue issue sec=24317224 typ=WS
blk_peek_request+0x1ee [kernel]
scsi_request_fn+0x3e [kernel]
__blk_run_queue+0x33 [kernel]
queue_unplugged+0x2a [kernel]
blk_flush_plug_list+0x1ff [kernel]
blk_finish_plug+0x2c [kernel]
ext4_writepages+0x537 [kernel]
do_writepages+0x1e [kernel]
__filemap_fdatawrite_range+0xc6 [kernel]
filemap_write_and_wait_range+0x41 [kernel]
ext4_sync_file+0xe8 [kernel]
vfs_fsync_range+0x4b [kernel]
do_fsync+0x3d [kernel]
sys_fsync+0x10 [kernel]
entry_SYSCALL_64_fastpath+0x1e [kernel]

20587 20587 vim block_rq_insert insert sec=24318360 typ=WS
__elv_add_request+0xc5 [kernel]
blk_flush_plug_list+0x238 [kernel]
blk_finish_plug+0x2c [kernel]
ext4_writepages+0x537 [kernel]
do_writepages+0x1e [kernel]
__filemap_fdatawrite_range+0xc6 [kernel]
filemap_write_and_wait_range+0x41 [kernel]
ext4_sync_file+0xe8 [kernel]
vfs_fsync_range+0x4b [kernel]
do_fsync+0x3d [kernel]
sys_fsync+0x10 [kernel]
entry_SYSCALL_64_fastpath+0x1e [kernel]

20587 20587 vim block_rq_issue issue sec=24318360 typ=WS
blk_peek_request+0x1ee [kernel]
scsi_request_fn+0x3e [kernel]
__blk_run_queue+0x33 [kernel]
queue_unplugged+0x2a [kernel]
blk_flush_plug_list+0x1ff [kernel]
blk_finish_plug+0x2c [kernel]
ext4_writepages+0x537 [kernel]
do_writepages+0x1e [kernel]
__filemap_fdatawrite_range+0xc6 [kernel]
filemap_write_and_wait_range+0x41 [kernel]
ext4_sync_file+0xe8 [kernel]
vfs_fsync_range+0x4b [kernel]
do_fsync+0x3d [kernel]
sys_fsync+0x10 [kernel]
entry_SYSCALL_64_fastpath+0x1e [kernel]

I am using ext4 however so not sure if this is possibly a writeback cache issue.
blktrace.c has the F defined as

if (tc & BLK_TC_FLUSH)
rwbs[i++] = 'F';
&
if (tc & BLK_TC_FUA)
rwbs[i++] = 'F';

@brendangregg Any thoughts considering these stacks?

I haven't had a chance to dig much yet (I'm spending more time with cgroups/namespaces for DockerCon). So a flush + write (FW) is becoming a flush + force unit access (FF). I've looked at some kernel code paths that set a REQ_FUA...

The answer may still be in the stacks. It's starting with xlog_sync(), and then diverges with blk_flush_plug_list(). It's doing the __elv_add_request() for the FW, then the queue_unplugged() for the FF. Is it modifying the FW to become a FF, or is the FW discarded and a new rq created for FF? I haven't been able to answer that yet.

Here's a function graph:

> /apps/perf-tools/bin/funcgraph xlog_sync
Tracing "xlog_sync"... Ctrl-C to end.
 7)               |  xlog_sync [xfs]() {
 7)   0.191 us    |    xlog_grant_add_space.isra.6 [xfs]();
 7)   0.078 us    |    xlog_grant_add_space.isra.6 [xfs]();
 7)   0.251 us    |    xlog_pack_data.isra.7 [xfs]();
 7)               |    xlog_cksum [xfs]() {
 7)               |      crypto_shash_update() {
 7)   0.147 us    |        crc32c_pcl_intel_update();
 7)   1.018 us    |      }
 7)               |      crypto_shash_update() {
 7)   0.124 us    |        crc32c_pcl_intel_update();
 7)   0.681 us    |      }
 7)               |      crypto_shash_update() {
 7)   0.192 us    |        crc32c_pcl_intel_update();
 7)   0.675 us    |      }
 7)               |      crypto_shash_update() {
 7)               |        crc32c_pcl_intel_update() {
 7)   0.144 us    |          irq_fpu_usable();
 7)               |          kernel_fpu_begin() {
 7)               |            __kernel_fpu_begin() {
 7)   0.131 us    |              irq_fpu_usable();
 7)               |              kernel_fpu_disable() {
 7)               |              xen_evtchn_do_upcall() {
 7)               |                irq_enter() {
 7)   0.132 us    |                  rcu_irq_enter();
 7)   0.638 us    |                }
 7)   0.081 us    |                exit_idle();
 7)               |                __xen_evtchn_do_upcall() {
 7)               |                  evtchn_2l_handle_events() {
 7)   0.068 us    |                    irq_from_virq();
 7)               |                    evtchn_from_irq() {
 7)               |                      irq_get_irq_data() {
 7)   0.148 us    |                        irq_to_desc();
 7)   0.677 us    |                      }
 7)   1.406 us    |                    }
 7)               |                    get_evtchn_to_irq() {
 7)   0.075 us    |                      evtchn_2l_max_channels();
 7)   0.661 us    |                    }
 7)               |                    generic_handle_irq() {
 7)               |                      handle_percpu_irq() {
 7)               |                        ack_dynirq() {
 7)               |                          evtchn_from_irq() {
 7)               |                            irq_get_irq_data() {
 7)   0.136 us    |                              irq_to_desc();
 7)   0.642 us    |                            }
 7)   1.143 us    |                          }
 7)   0.074 us    |                          evtchn_2l_clear_pending();
 7)   2.440 us    |                        }
 7)               |                        handle_irq_event_percpu() {
 7)               |                          __handle_irq_event_percpu() {
 7)               |                            xen_irq_work_interrupt() {
 7)               |                              irq_enter() {
 7)   0.063 us    |                                rcu_irq_enter();
 7)   0.596 us    |                              }
 7)               |                              __wake_up() {
 7)   0.071 us    |                                _raw_spin_lock_irqsave();
 7)   0.073 us    |                                __wake_up_common();
 7)   0.076 us    |                                _raw_spin_unlock_irqrestore();
 7)   2.362 us    |                              }
 7)               |                              irq_exit() {
 7)   0.070 us    |                                idle_cpu();
 7)   0.159 us    |                                rcu_irq_exit();
 7)   1.291 us    |                              }
 7)   6.046 us    |                            }
 7)   6.600 us    |                          }
 7)   0.098 us    |                          add_interrupt_randomness();
 7)   0.158 us    |                          note_interrupt();
 7)   8.436 us    |                        }
 7) + 12.434 us   |                      }
 7) + 12.965 us   |                    }
 7) + 17.388 us   |                  }
 7) + 17.951 us   |                }
 7)               |                irq_exit() {
 7)   0.070 us    |                  idle_cpu();
 7)   0.065 us    |                  rcu_irq_exit();
 7)   1.069 us    |                }
 7) + 21.612 us   |              } /* xen_evtchn_do_upcall */
 7)   0.068 us    |              xen_maybe_preempt_hcall();
 7)   0.115 us    |              } /* kernel_fpu_disable */
 7) + 25.827 us   |            }
 7) + 26.409 us   |          }
 7)               |          kernel_fpu_end() {
 7)               |            __kernel_fpu_end() {
 7)   0.082 us    |              kernel_fpu_enable();
 7)   0.760 us    |            }
 7)   1.345 us    |          }
 7) + 31.701 us   |        }
 7) + 32.188 us   |      }
 7) + 38.122 us   |    }
 7)               |    xlog_bdstrat [xfs]() {
 7)               |      xfs_buf_lock [xfs]() {
 7)               |        down() {
 7)   0.159 us    |          _raw_spin_lock_irqsave();
 7)   0.075 us    |          _raw_spin_unlock_irqrestore();
 7)   1.197 us    |        }
 7)   1.775 us    |      }
 7)               |      xfs_buf_submit [xfs]() {
 7)   0.086 us    |        xfs_buf_wait_unpin [xfs]();
 7)   0.073 us    |        xfs_buf_hold [xfs]();
 7)               |        _xfs_buf_ioapply [xfs]() {
 7)   0.093 us    |          blk_start_plug();
 7)               |          bio_alloc_bioset() {
 7)               |            mempool_alloc() {
 7)   0.084 us    |              _cond_resched();
 7)               |              mempool_alloc_slab() {
 7)               |                kmem_cache_alloc() {
 7)   0.137 us    |                  memcg_kmem_put_cache();
 7)   0.820 us    |                }
 7)   1.337 us    |              }
 7)   2.413 us    |            }
 7)               |            bvec_alloc() {
 7)               |              kmem_cache_alloc() {
 7)               |                __slab_alloc() {
 7)   0.217 us    |                  ___slab_alloc();
 7)   0.797 us    |                }
 7)   0.071 us    |                memcg_kmem_put_cache();
 7)   1.834 us    |              }
 7)   2.405 us    |            }
 7)   6.375 us    |          }
 7)   0.096 us    |          bio_add_page();
 7)   0.091 us    |          bio_add_page();
 7)               |          submit_bio() {
 7)               |            generic_make_request() {
 7)               |              generic_make_request_checks() {
 7)   0.067 us    |                _cond_resched();
 7)               |                create_task_io_context() {
 7)               |                  kmem_cache_alloc_node() {
 7)   0.083 us    |                    memcg_kmem_put_cache();
 7)   1.545 us    |                  }
 7)   0.060 us    |                  _raw_spin_lock();
 7)   2.764 us    |                }
 7)   0.502 us    |                blkg_lookup_slowpath();
 7)   0.327 us    |                blk_throtl_bio();
 7)   6.686 us    |              }
 7)   0.204 us    |              blk_queue_enter();
 7)               |              md_make_request() {
 7)   0.349 us    |                blk_queue_split();
 7)               |                raid0_make_request [raid0]() {
 7)               |                  md_flush_request() {
 7)   0.159 us    |                    _raw_spin_lock_irq();
 7)               |                    queue_work_on() {
 7)               |                      __queue_work() {
 7)   0.080 us    |                        get_work_pool();
 7)   0.078 us    |                        _raw_spin_lock();
 7)               |                        insert_work() {
 7)   0.075 us    |                          get_pwq.isra.21();
 7)               |                          wake_up_process() {
 7)               |                            try_to_wake_up() {
 7)   0.086 us    |                              _raw_spin_lock_irqsave();
 7)   0.073 us    |                              _raw_spin_lock();
 7)               |                              ttwu_do_activate() {
 7)               |                                activate_task() {
 7)   0.246 us    |                                  update_rq_clock.part.81();
 7)               |                                  enqueue_task_fair() {
 7)               |                                    enqueue_entity() {
 7)               |                                      update_curr() {
 7)   0.141 us    |                                        __calc_delta();
 7)   0.067 us    |                                        update_min_vruntime();
 7)   1.251 us    |                                      }
 7)   0.094 us    |                                      account_entity_enqueue();
 7)   0.123 us    |                                      update_cfs_shares();
 7)   0.073 us    |                                      place_entity();
 7)   0.210 us    |                                      __enqueue_entity();
 7)   4.578 us    |                                    }
 7)   0.063 us    |                                    hrtick_update();
 7)   5.779 us    |                                  }
 7)   7.432 us    |                                }
 7)               |                                wq_worker_waking_up() {
 7)   0.063 us    |                                  kthread_data();
 7)   0.577 us    |                                }
 7)               |                                ttwu_do_wakeup() {
 7)               |                                  check_preempt_curr() {
 7)               |                                    check_preempt_wakeup() {
 7)   0.085 us    |                                      update_curr();
 7)   0.076 us    |                                      wakeup_preempt_entity.isra.61();
 7)   1.285 us    |                                    }
 7)   2.062 us    |                                  }
 7)   2.755 us    |                                }
 7) + 12.410 us   |                              }
 7)   0.066 us    |                              ttwu_stat();
 7)   0.066 us    |                              _raw_spin_unlock_irqrestore();
 7) + 15.437 us   |                            }
 7) + 15.933 us   |                          }
 7) + 17.214 us   |                        }
 7) + 19.122 us   |                      }
 7) + 19.780 us   |                    }
 7) + 21.236 us   |                  }
 7) + 21.968 us   |                }
 7) + 23.754 us   |              }
 7) + 32.351 us   |            }
 7) + 32.952 us   |          }
 7)               |          blk_finish_plug() {
 7)   0.126 us    |            blk_flush_plug_list();
 7)   0.698 us    |          }
 7) + 43.806 us   |        }
 7)   0.102 us    |        xfs_buf_rele [xfs]();
 7) + 46.409 us   |      }
 7) + 49.390 us   |    }
 7) + 91.377 us   |  }
^C
Ending tracing...

I'm not sure that's relevant, though, as I don't have the same child functions...

Hmm. So, do we conclude that we can't reliably trace queue to issue latency with tracepoints? I see three options:

1) Do nothing: keep the kprobe-based bio* tools, and update them with more hacks as kernel changes inevitably occur (is it obvious how much I like this option?)

2) Remove the -Q option for tracing queue to issue latency.

3) Keep the -Q option, but when there is an obviously wrong result, i.e. a mismatched queue to issue, report "unknown" as the queue to issue time.

@brendangregg I'd really like to bring this to a wrap, so I'd appreciate your opinion.

Isn't this just the XFS sync codpath that's breaking it (that we know of)? I'd be tempted to add:

// workaround for XFS FW's that become FF's:
if (key.sector < 0) { key.sector = 0; }

Sure, I can do that. I hope it's only that path - and I hope we don't end up introducing bogus results because we correlate only by dev in that case. I haven't tested this extensively in other environments so for all I know there could be more cases that we'd need to handle.

right, hence putting a comment in the code.

Someone else at Facebook ran into this so I just wanted to share, flushes in the kernel are special-cased so the events are a little tricky.

There are two flushing request flags: REQ_PREFLUSH (called REQ_FLUSH in older kernels), which means flush the disk's write cache before writing the request data; and REQ_FUA, which means this write should bypass the write cache. Depending on what the underlying hardware supports, one of these requests might turn into 0-3 different steps.

For example, a REQ_OP_WRITE | REQ_PREFLUSH request with no data is a simple request to flush the disk cache. If the disk does not have a writeback cache, this is a noop, so the request can be completed without ever being issued. If the disk does support a writeback cache, we don't issue this request directly, but rather a separate "flush request" which happens to have a sector of -1. We then complete the original request.

A REQ_OP_WRITE | REQ_FUA request with data also depends on the hardware. If the hardware supports FUA, the original request can be issued directly. If it does not, the request is translated to a write followed by a full flush, which means we issue and complete the original request, and then we issue and complete a special flush request like for preflush.

Of course, these can be combined, so a REQ_OP_WRITE | REQ_PREFLUSH | REQ_FUA request with data on a device with a writeback cache without FUA support would turn into a preflush, a normal write, and a postflush.

The request that XFS is making here is the first example, a simple flush with no data. So the "FW" is the original request, then the "FF" is the special flush request, which is REQ_OP_FLUSH | REQ_PREFLUSH.

The moral of the story is that flushes are messy, so they're going to have to be a special case in these tools. So just keep in mind that

  1. A REQ_PREFLUSH request with no data will be inserted and completed without being issued.
  2. Depending on the hardware, a separate REQ_OP_FLUSH request (see here) may be issued after the original REQ_PREFLUSH request is inserted.
  3. Similarly, depending on the hardware, a separate REQ_OP_FLUSH request may be issued after a REQ_FUA request is completed.
  4. We fiddle with the request flags in multiple places in the flush code, so you can't rely on that to track a request (e.g. here, here and here).
  5. This may change in the future, so you should handle unexpected sequences.

@goldshtn I tested your revised version of biolatency (using tracepoints) against some logical volumes on a loop device, and unfortunately (like the old version) it shows loop0 as the device, rather than "dm-X". See #910 also. Do you think there is an elegant way that this could generically handle devmapper devices (including mdraid), such that the original device name is tracked?

Partially answering my own question, the block_bio_remap tracepoint will catch devmapper activity:

TRACEPOINT_PROBE(block, block_bio_remap)
{
    if (args->dev != args->old_dev) {
        bpf_trace_printk("block_bio_remap old_dev: %d,%d\n",
            MAJOR(args->old_dev), MINOR(args->old_dev));
        bpf_trace_printk("block_bio_remap dev: %d,%d\n",
            MAJOR(args->dev), MINOR(args->dev));
    }
    return 0;
}
              dd-19925 [005] .... 41096.710821: : block_bio_remap old_dev: 253,1
              dd-19925 [005] .... 41096.710831: : block_bio_remap dev: 7,0
              dd-19925 [005] .... 41096.710846: : block_bio_remap old_dev: 253,1
              dd-19925 [005] .... 41096.710849: : block_bio_remap dev: 7,0

(requests split across two lines due to 3-argument limit of bpf_trace_printk)

Note that this also will be triggered for bio reqs to partitions, which get remapped to the raw block device (with an offset), i.e., old_dev: 8,2 -> dev: 8,0.

PS: @goldshtn Python has os.major() / os.minor() functions for handling dev_t numbers - no need to reimplement.

Should we get back to this? There were two LTS releases since the creation of this ticket.

My opinion is that tracepoint based biosnoop without queuing time is better than kprobe based biosnoop we have now.

I had the issue with a couple of disks obviously getting stuck (same picture for multiple seconds):

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdb               0.00     0.00    0.00    0.00     0.00     0.00     0.00     2.00    0.00    0.00    0.00   0.00 100.00
sdc               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdd               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sde               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdf               0.00     0.00    0.00    0.00     0.00     0.00     0.00     2.00    0.00    0.00    0.00   0.00 100.00

In current biosnoop I saw this:

$ sudo /usr/share/bcc/tools/biosnoop | awk '$NF > 1000 { print $0 }'
...
70.634468000   dmcrypt_write  11437  sdb     W  134019072 1212416 1037.38
70.638229000   dmcrypt_write  11437  sdb     W  134021440 884736  1041.13
70.643547000   dmcrypt_write  11437  sdb     W  134023168 1212416 1046.42
70.648452000   dmcrypt_write  11437  sdb     W  134025536 884736  1051.31
70.652595000   dmcrypt_write  11437  sdb     W  134027264 1212416 1055.43
70.657393000   dmcrypt_write  11437  sdb     W  134029632 884736  1060.21
105.586649000  dmcrypt_write  11523  sdf     W  46485504  1212416 1119.34
105.764376000  dmcrypt_write  11523  sdf     W  46487872  884736  1297.05
105.934872000  dmcrypt_write  11523  sdf     W  46489600  1212416 1467.52
105.940061000  dmcrypt_write  11523  sdf     W  46491968  884736  1472.68
131.745480000  dmcrypt_write  11523  sdf     W  236050432 1048576 1133.57
131.895918000  dmcrypt_write  11523  sdf     W  236052480 1048576 1283.99
132.046939000  dmcrypt_write  11523  sdf     W  236054528 1048576 1434.99
132.197379000  dmcrypt_write  11523  sdf     W  236056576 1048576 1585.41

While in tracepoint based from @goldshtn I saw this:

$ sudo python biosnoop.py | awk '$NF > 1 { print $0 }'
...
70.207464448   kworker/13:1   32442  sdf     FF   -1        0         7.016
72.211996159   kworker/13:1   32442  sdf     FF   -1        0         1.959
72.622031800   kworker/31:1H  4821   sdb     FF   -1        0         7.021
76.414599594   cadvisor       2025   sdb     FF   -1        0         3.793
78.315507469   kworker/13:1   32442  sdb     FF   -1        0         1.704
79.988741039   kworker/20:0   7035   sdb     FF   -1        0         1.500
81.931542687   kworker/23:1   31573  sdb     FF   -1        0         1.617
83.825293856   kworker/23:0   8742   sdb     FF   -1        0         1.476
89.633290696   kworker/0:1H   4910   sdf     FF   -1        0         7.019
93.367898802   kworker/1:1    7049   sdd     FF   -1        0         3.685
94.496078864   kworker/31:1H  4821   sdb     FF   -1        0         7.114
94.630775111   swapper/0      0      sdd     FF   -1        0         1.250
96.711452486   md127_raid10   11796  sdf     FF   -1        0         7.028
99.033392542   kworker/0:1H   4910   sdf     FF   -1        0         1.419
100.488801563  dmcrypt_write  11523  sdf     W    46485504  1212416   1.119
100.666527907  dmcrypt_write  11523  sdf     W    46487872  884736    1.297
100.837022524  dmcrypt_write  11523  sdf     W    46489600  1212416   1.468
100.842217468  kworker/23:1H  4931   sdf     W    46491968  884736    1.473
101.602938628  md127_raid10   11796  sdb     FF   -1        0         7.046
102.957954320  swapper/0      0      sdb     FF   -1        0         1.355
109.978891443  kworker/3:0    27396  sdf     FF   -1        0         7.020
110.639134262  ksoftirqd/28   180    sdb     FF   -1        0         7.020
111.282573173  kworker/3:0    27396  sdf     FF   -1        0         1.303
117.657342479  swapper/0      0      sdb     FF   -1        0         7.018
117.659401959  dmcrypt_write  11437  sdb     WS   234784128 32768     7.020
120.328640783  kworker/0:1H   4910   sdb     FF   -1        0         2.669
122.182632767  kworker/3:0    27396  sdf     FF   -1        0         1.511
126.647631013  dmcrypt_write  11523  sdf     W    236050432 1048576   1.134
126.798071106  dmcrypt_write  11523  sdf     W    236052480 1048576   1.284
126.949090145  dmcrypt_write  11523  sdf     W    236054528 1048576   1.435
127.099531929  kworker/3:1H   4263   sdf     W    236056576 1048576   1.585
128.730211688  kworker/3:1H   4263   sdb     FF   -1        0         6.557
134.114154880  kworker/27:1H  4008   sdd     FF   -1        0         7.014
134.270628181  kworker/3:1H   4263   sdf     FF   -1        0         7.171
135.136450090  swapper/0      0      sdd     FF   -1        0         1.019
136.611427399  kworker/0:1H   4910   sdb     FF   -1        0         7.022
138.633013874  kworker/0:1H   4910   sdb     FF   -1        0         1.193

It seems that tracepoint catches more issues.

I think we can work on multiple requests to the same sector and queuing separately.

Linux 4.17 provides raw_tracepoint support and bcc currently supports it as well.

block_rq_insert:
        TP_PROTO(struct request_queue *q, struct request *rq),
block_rq_issue:
        TP_PROTO(struct request_queue *q, struct request *rq),
block_rq_complete:
        TP_PROTO(struct request *rq, int error, unsigned int nr_bytes),

We probably should use raw tracepoints to resolve changing
sector number and rwbs issue related with tracepoint.

@yonghong-song, raw_tracepoint look like a great middle ground between unstable kprobes and annoyances of current tracepoints:

  • Sector number changes you mentioned
  • Lack of ability to resolve dev name in kernel space (I want sda, not (8, 1) that maps to sda1)
  • RWBS field is filled with blk_fill_rwbs and it's non-trivial to fold back

The problem with raw tracepoints is that they are 4.17+ and 4.17 is not even released, not to mention it's not even LTS. Is there a sane way to make this work on 4.14 (which is the latest LTS)?

cc @4ast

Is there a sane way to make this work on 4.14 (which is the latest LTS)?
Probably not. This is a feature so backporting is not possible in general.

In the meantime, maybe tracepoint based solution should be used instead of kprobe.
Although we may not be able to resolve dev name in the kernel, I guess we can still do it in user space using /proc partition table.

@brendangregg @goldshtn @mcaleavya @osandov @dswarbrick @bobrik @yonghong-song Sorry to bother you, I used iosnoop (which impled by tracepoints) and met similar problem like https://github.com/brendangregg/perf-tools/issues/27, from output, I didn't see LOST.*EVENTS
image so why the latency is about 9.4 min? It's impossible. So under what circumstances will this problem occur?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

a0viedo picture a0viedo  路  7Comments

ChaosData picture ChaosData  路  4Comments

iaguis picture iaguis  路  4Comments

paipanka picture paipanka  路  6Comments

butterl picture butterl  路  6Comments