комментирование инструкции printk приводит к сбою в тесте драйвера устройства Linux

#linux-kernel #linux-device-driver #arm64

Вопрос:

Я вижу странный случай в простом тесте драйверов Linux(arm64).
Пользовательская программа вызывает ioctl драйвера устройства и передает массив ‘arg’ uint64_t в качестве аргумента. Кстати, arg[2] содержит указатель на переменную в приложении. Ниже приведен фрагмент кода.

     case SetRunParameters:
        copy_from_user(args, (void __user *)arg, 8*3);
        offs = args[2] % PAGE_SIZE;
        down_read(amp;current->mm->mmap_sem);
        res = get_user_pages( (unsigned long)args[2], 1, 1, amp;pages, NULL);
        if (res) {
            kv_page_addr = kmap(pages);
            kv_addr = ((unsigned long long int)(kv_page_addr) offs);
            args[2] = page_to_phys(pages)   offset; // args[2] changed to physical
        }
        else {
            printk("get_user_pages failed!n");
        }
        up_read(amp;current->mm->mmap_sem);
        *(vaddr   REG_IOCTL_ARG/4) = virt_to_phys(args);  // from axpu_regs.h
        printk("ldd:writing %x at %pxn",cmdx,vaddr   REG_IOCTL_CMD/4); // <== line 248. not ok w/o this printk line why?..
        *(vaddr   REG_IOCTL_CMD/4) = cmdx;  // this command is different from ioctl cmd!
        put_page(pages); //page_cache_release(page);
        break;
    case ...
 

Я отметил строку 248 в приведенном выше коде. Если я закомментирую там printk, произойдет ловушка, и виртуальная машина рухнет(я делаю это на виртуальной машине qemu). Это cmdx целочисленное значение, заданное в соответствии с командой ioctl из приложения, и vaddr является виртуальным адресом устройства (полученным из ioremap). Если я сохраню printk, он будет работать так, как я ожидаю. В каком случае это может произойти? (кэш или tlb?)

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

1. Можете ли вы также опубликовать отчеты о сбоях? Если он что-то сообщит

2. поскольку сама виртуальная машина разбилась, я не смог увидеть отчет ядра на виртуальной машине. Я вижу только это : ./runit20a: строка 10: 16669 Ошибка сегментации (сброшено ядро) qemu-система-aarch64-nographic-машина ab21q,gic-версия=max,iommu=smmuv3-m 1G-процессор max-smp 4-пользователь netdev,идентификатор=vnet,hostfwd=:127.0.0.1:0-:22-виртуальное устройство-net-pci,netdev=vnet-файл диска=ab21q-ubuntu-20.04-изображение.img,если=нет,идентификатор=привод0,кэш=обратная запись-виртуальное устройство-blk,привод=привод0,загрузочный индекс=0-привод file=ubuntu-20.04-mini.iso,если=нет,идентификатор=привод1,кэш=обратная запись

3. -виртуальное устройство-blk,привод=привод1,загрузочный индекс=1 -файл на диске=flash0.img,формат=raw,если=pflash-файл на диске=flash1.img,формат=raw,если=pflash. Но это из-за аварии qemu.

4. Иногда без оператора printk он не аварийно завершается, но значение из области пользователя не изменяется (предполагается, что устройство изменит значение). Когда все в порядке(с печатью), значение было изменено устройством(модель qemu).

5. Я предполагаю printk , что звонок действует как барьер. Вы должны использовать writel для записи регистров.

Ответ №1:

Доступ к отображенным в памяти регистрам с помощью простых конструкций языка Си, таких как *(vaddr REG_IOCTL_ARG/4) плохая идея. На некоторых платформах это может сойти вам с рук, если доступ volatile квалифицирован, но на некоторых платформах он не будет работать надежно или вообще не будет работать. Правильный способ доступа к регистрам, отображенным в памяти,-это использование функций, объявленных #include <asm/io.h> or #include <linux/io.h> . Они позаботятся о любых требованиях, специфичных для arch, чтобы гарантировать, что записи правильно упорядочены в отношении центрального процессора 1.

Функции доступа к регистру с отображением в памяти описаны в документации ядра Linux в разделе Доступ к устройствам, не зависящим от шины.

Этот код:

         *(vaddr   REG_IOCTL_ARG/4) = virt_to_phys(args);
        *(vaddr   REG_IOCTL_CMD/4) = cmdx;
 

может быть переписан как:

         writel(virt_to_phys(args), vaddr   REG_IOCTL_ARG/4);
        writel(cmdx, vaddr   REG_IOCTL_CMD/4);
 

1 Порядок записи для определенных типов шин, таких как PCI, может потребоваться дополнительный код для чтения регистра между записями в разные регистры, если порядок записи в регистр важен. Это связано с тем, что записи «отправляются» асинхронно на шину PCI, и устройство PCI может обрабатывать записи в разные регистры не по порядку. Считывание промежуточного регистра не будет обрабатываться устройством до тех пор, пока не будут обработаны все предыдущие записи, поэтому его можно использовать для обеспечения упорядочения опубликованных записей.

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

1. Большое спасибо за ясное объяснение. Изменение его на writel заставило его работать с принтером или без него.