Отслеживать жесткие запросы в Linux
Мой опыт работы с ядром Linux очень минимален. Я только начал играть с этим недавно.
Я пытался отследить самое раннее время прибытия пакета для моих исследовательских целей. Я могу сделать это на уровне драйвера устройства, изменив драйвер устройства и записав временные метки в функции обработчика прерываний драйвера устройства. Мне жаль, что этот пост может быть немного длиннее.
Например, я изменил эту функцию ( https://elixir.bootlin.com/linux/v4.7/source/drivers/net/ethernet/intel/i40e/i40e_main.c#L3232), чтобы отслеживать временную метку вызова этой функции.
углубившись далее и следуя трассировке стека этого вызова, мы найдем трассировку стека, как показано ниже:
- i40e_msix_clean_rings () - i40e_main.c драйвера i40e по приведенной выше ссылке
- __handle_irq_event_percpu () - kernel / irq / handle.c
- handle_irq_event_percpu () - kernel / irq / handle.c
- handle_irq_event () - kernel / irq / handle.c
- handle_edge_irq () - kernel / irq / chip.c
- handle_irq () - arch / x86 / kernel / irq_64.c
- do_IRQ() - arch / x86 / kernel / irq.c
- common_interrupt () - не совсем уверен, но реализация должна быть похожа на early_idt_handler_common() в arch/x86/kernel/head_32.s
Я пытаюсь отследить отметку времени прибытия моего пакета в функцию do_IRQ() (жирный шрифт в трассировке стека выше). Для справки, функция do_IRQ выглядит так:
__visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc * desc;
/* high bit used in ret_from_ code */
unsigned vector = ~regs->orig_ax;
**int int_number;**
entering_irq();
/* entering_irq() tells RCU that we're not quiescent. Check it. */
RCU_LOCKDEP_WARN(!rcu_is_watching(), "IRQ failed to wake up RCU");
desc = __this_cpu_read(vector_irq[vector]);
**int_number = desc->irq_data.irq;**
**printk(KERN_INFO "IRQ Number=%d; Vector=%d \n", int_number, vector);**
if (!handle_irq(desc, regs)) {
ack_APIC_irq();
if (desc != VECTOR_RETRIGGERED) {
pr_emerg_ratelimited("%s: %d.%d No irq handler for vector\n",
__func__, smp_processor_id(),
vector);
} else {
__this_cpu_write(vector_irq[vector], VECTOR_UNUSED);
}
}
exiting_irq();
set_irq_regs(old_regs);
return 1;
}
Чтобы прояснить свои намерения, я указал свои изменения в этой функции, заключенные в " ** ".
Например, на моей тестовой машине моя сетевая карта привязана к IRQ Number 19. Переменная int_number представляет это число. Таким образом, это позволяет мне отслеживать IRQ для определенного номера IRQ.
Возможно, это не звучит релевантно для адаптера NIC с одной очередью, но будет применимо для адаптера с несколькими очередями, поскольку я могу направлять свои пакеты в фиксированную очередь с директором потока, и каждая очередь привязана к определенному номеру IRQ. Таким образом, это поможет мне легко отслеживать мои пакеты.
Мои подходы:
- добавление ручных реализаций внутри этой функции; который я не считаю правильным подходом.
- используя kprobes. Но позволяет ли это фильтровать мои трассировки на основе содержимого переменных или аргументов?
- используя jprobe. Полагаю, что при таком подходе мы сможем поиграть с аргументами. Я смог справиться с этим событием. Я просто последовал примеру jprobe. ( https://stuff.mit.edu/afs/sipb/contrib/linux/samples/kprobes/jprobe_example.c) и еще несколько.
- Я также сталкивался с другими инструментами, проходя через упомянутые выше подходы. как perf, perf-tools, eBPF. Но я не уверен, что будет лучшим подходом для меня.
Просто чтобы уточнить мою последнюю задачу: я пытаюсь зафиксировать временные метки самого раннего прибытия моих пакетов, например:
t1
t2
t3
t4
Я был бы признателен за любые материалы по этому вопросу.
Спасибо!