Повреждение данных при memcpy из DMA

#linux #linux-kernel #network-programming #linux-device-driver #dma

#linux #linux-ядро #сетевое программирование #linux-драйвер устройства #dma

Вопрос:

Редактировать:

Не уверен, проблема это или нет, но вот что я заметил:
rx_skbuffers выделяются в двух местах: один раз, когда драйвер инициализирован, он вызывает __netdev_alloc_skb_ip_align с GPF_KERNEL, и второй раз, если rx_skbuff уже установлен.освобожденный он вызывает netdev_alloc_skb_ip_align (который внутренне использует GPF_ATOMIC).

Разве эти выделения skb не должны вызываться с помощью GPF_DMA ?

==========================================================================

У меня возникли проблемы с повреждением данных в драйвере Ethernet (для ST MAC 10/100/1000) Я работаю с. Драйвер работает на Allwinner A20 (ARM Cortex-A7).

Некоторые подробности:

  • Драйвер содержит кольцо Rx sk_buffers (выделенных с __netdev_alloc_skb_ip_align помощью ).
  • Данные ( rx_skbuff[i]->data ) каждого из sk_buffers сопоставляются с использованием DMA dma_map_single .
  • Сопоставление выполнено успешно (проверено с dma_mapping_error помощью).

Проблема:

Через некоторое время (минуты, часы, дни… очень случайно), ядро паникует из-за повреждения данных.

Отладка (ОТРЕДАКТИРОВАНО):

  • Покопавшись еще немного, я обнаружил, что иногда через некоторое время одна из структур sk_buffer повреждена, и это может привести к тому, что программа будет делать то, чего не должна, и, таким образом, вызвать панику ядра.
  • После еще некоторых поисков я обнаружил, что повреждение происходит после skb_copy_to_linear_data (что точно так же, как memcpy ). Имейте в виду, что это повреждение не происходит после каждого вызова skb_copy_to_linear_data , но когда повреждение происходит, оно всегда происходит после вызова skb_copy_to_linear_data .
  • Когда происходит повреждение, это не происходит в rx_q->rx_skbuff текущей записи ( rx_q->rx_skbuff[entry] ). Например, если мы выполним skb_copy_to_linear_data on rx_q->rx_skbuff[X] , поврежденная sk_buff структура будет rx_q->rx_skbuff[Y] (где X не равно Y).
  • Кажется, что физический адрес skb->data (который был выделен непосредственно перед skb_copy_to_linear_data вызовом) имеет тот же физический адрес rx_q->rx_skbuff[Y]->end . Первое, что я подумал, это то, что, возможно, драйвер не знает, что rx_q-> rx_skbuff [Y] был выпущен, но когда происходит это столкновение, я вижу, что rx_q-> rx_skbuff [Y]-> users равно 1. Как это может быть, есть идеи?
  • Не уверен, проблема это или нет, но вот что я заметил:
    rx_skbuffers выделяются в двух местах: один раз, когда драйвер инициализирован, он вызывает __netdev_alloc_skb_ip_align с GPF_KERNEL, и второй раз, если rx_skbuff уже установлен.освобожденный он вызывает netdev_alloc_skb_ip_align (который внутренне использует GPF_ATOMIC). Разве эти выделения skb не должны вызываться с помощью GPF_DMA ?

Код:

Вот часть кода, в которой происходит повреждение.
Полный код драйвера взят из основной строки ядра Linux 4.19, и его можно найти здесь . Я вставил здесь только часть между строками 3451-3474.
Кто-нибудь находит здесь неправильное поведение в отношении использования DMA-API?

 skb = netdev_alloc_skb_ip_align(priv->dev,
                frame_len);
if (unlikely(!skb)) {
    if (net_ratelimit())
        dev_warn(priv->device,
             "packet droppedn");
    priv->dev->stats.rx_dropped  ;
    continue;
}

dma_sync_single_for_cpu(priv->device,
            rx_q->rx_skbuff_dma
            [entry], frame_len,
            DMA_FROM_DEVICE);

// Here I check if data has been corrupted (the answer is ALWAYS NO).
debug_check_data_corruption();

skb_copy_to_linear_data(skb,
            rx_q->
            rx_skbuff[entry]->data,
            frame_len);

// Here I check again if data has been corrupted (the answer is SOMETIMES YES).
debug_check_data_corruption();

skb_put(skb, frame_len);
dma_sync_single_for_device(priv->device,
               rx_q->rx_skbuff_dma
               [entry], frame_len,
               DMA_FROM_DEVICE);
  

Некоторые последние замечания:

  • Я попытался запустить ядро с CONFIG_DMA_API_DEBUG включенным. Это не всегда срабатывает, но когда я сам обнаруживаю повреждение (с помощью функции отладки), иногда я вижу, что /sys/kernel/debug/dma-api/num_errors оно увеличилось, а иногда я также получаю этот журнал: DMA-API: device driver tries to sync DMA memory it has not allocated [device address=0x000000006879f902] [size=61 bytes]
  • Я также включил CONFIG_DEBUG_KMEMLEAK и сразу после того, как я обнаружил событие повреждения данных, я получаю этот журнал: kmemleak: 1 new suspected memory leaks (see /sys/kernel/debug/kmemleak) , но я все еще не понимаю, какие подсказки он выдает, хотя, похоже, он взят из той же части кода, которую я вставил сюда ( __netdev_alloc_skb был вызван из __netdev_alloc_skb_ip_align ). Это то, что /sys/kernel/debug/kmemleak отображает:
 unreferenced object 0xe9ea52c0 (size 192):     
  comm "softirq", pid 0, jiffies 6171209 (age 32709.360s)     
  hex dump (first 32 bytes):  
        00 00 00 00 00 00 00 00 00 00 00 00 40 4d 2d ea  ............@M-.  
        00 00 00 00 00 00 00 00 d4 83 7c b3 7a 87 7c b3  ..........|.z.|.     
  backtrace:  
        [<045ac811>] __netdev_alloc_skb 0x9f/0xdc  
        [<4f2b009a>] stmmac_napi_poll 0x89b/0xfc4  
        [<1dd85c70>] net_rx_action 0xd3/0x28c  
        [<1c60fabb>] __do_softirq 0xd5/0x27c  
        [<9e007b1d>] irq_exit 0x8f/0xc0  
        [<beb36a07>] __handle_domain_irq 0x49/0x84  
        [<67c17c88>] gic_handle_irq 0x39/0x68  
        [<e8f5dc30>] __irq_svc 0x65/0x94  
        [<075bc7c7>] down_read 0x8/0x3c  
        [<075bc7c7>] down_read 0x8/0x3c  
        [<790c6556>] get_user_pages_unlocked 0x49/0x13c  
        [<544d56e3>] get_futex_key 0x77/0x2e0  
        [<1fd5d0e9>] futex_wait_setup 0x3f/0x144  
        [<8bc86dff>] futex_wait 0xa1/0x198  
        [<b362fbc0>] do_futex 0xd3/0x9a8  
        [<46f336be>] sys_futex 0xcd/0x138
  

Комментарии:

1. Вы упомянули 4.9, но ссылались на 4.19. Это опечатка?

2. Моя ошибка, я имел в виду 4.19. Спасибо

3. Какая архитектура? Может ли это быть вмешательство в кэш процессора? См. elixir.bootlin.com/linux/v4.19.147/source/Documentation /…

4. Allwinner A20 (ARM Cortex-A7). Есть ли способ проверить, не связано ли это с помехами в кэше? Возможно, когда драйвер вызывает <dma_sync_single_for_cpu> , его следует вызывать не с размером <frame_len>, а с размером <dma_buf_sz> , который соответствует тому же размеру, который мы использовали в <dma_map_single> (из документации я не вижу, что нам нужно это делать).

5. Вы можете попробовать отправлять пакеты, полные известного шаблона. Затем посмотрите на поврежденные данные, чтобы увидеть, есть ли в них этот известный шаблон. Это скажет вам, что вы получаете DMAed-данные там, где их не должно быть. Проверьте адреса в первом вызове синхронизации, всегда ли они такими, какими должны быть? То же самое с длиной?