Подтвердить что ты не робот

Mmap медленнее, чем ioremap

Я разрабатываю для устройства ARM под управлением Linux 2.6.37. Я пытаюсь переключить вывод IO как можно быстрее. Я сделал небольшой модуль ядра и приложение для пользовательского пространства. Я пробовал две вещи:

  • Манипулируйте управляющие регистры GPIO непосредственно из пространства ядра с помощью ioremap.
  • mmap() Контроллер GPIO регистрирует без кэширования и использует их из пользовательского пространства.

Оба метода работают, но второй примерно в 3 раза медленнее первого (наблюдается на осциллографе). Я думаю, что я отключил все механизмы кэширования.

Конечно, я хотел бы получить лучшее из двух миров: гибкость и простоту разработки из пользовательского пространства со скоростью пространства ядра.

Кто-нибудь знает, почему mmap() может быть медленнее, чем ioremap()?

Здесь мой код:

Код модуля ядра

static int ti81xx_usmap_mmap(struct file* pFile, struct vm_area_struct* pVma)
{
  pVma->vm_flags |= VM_RESERVED;
  pVma->vm_page_prot = pgprot_noncached(pVma->vm_page_prot);

  if (io_remap_pfn_range(pVma, pVma->vm_start, pVma->vm_pgoff,
                          pVma->vm_end - pVma->vm_start, pVma->vm_page_prot))
     return -EAGAIN;

  pVma->vm_ops = &ti81xx_usmap_vm_ops;
  return 0;
}

static void ti81xx_usmap_test_gpio(void)
{
  u32* pGpIoRegisters = ioremap_nocache(TI81XX_GPIO0_BASE, 0x400);
  const u32 pin = 1 << 24;
  int i;

  /* I should use IO read/write functions instead of pointer deferencing, 
   * but portability isn't the issue here */

  pGpIoRegisters[OMAP4_GPIO_OE >> 2] &= ~pin;    /* Set pin as output*/

  for (i = 0; i < 200000000; ++i)
  {
     pGpIoRegisters[OMAP4_GPIO_SETDATAOUT >> 2] = pin;
     pGpIoRegisters[OMAP4_GPIO_CLEARDATAOUT >> 2] = pin;
  }

  pGpIoRegisters[OMAP4_GPIO_OE >> 2] |= pin;    /* Set pin as input*/

  iounmap(pGpIoRegisters);
}

Код приложения для пользовательского пространства

int main(int argc, char** argv)
{
   int file, i;
   ulong* pGpIoRegisters = NULL;
   ulong pin = 1 << 24;

   file = open("/dev/ti81xx-usmap", O_RDWR | O_SYNC);

   if (file < 0)
   {
      printf("open failed (%d)\n", errno);
      return 1;
   }


   printf("Toggle from kernel space...");
   fflush(stdout);

   ioctl(file, TI81XX_USMAP_IOCTL_TEST_GPIO);

   printf(" done\n");    

   pGpIoRegisters = mmap(NULL, 0x400, PROT_READ | PROT_WRITE, MAP_SHARED, file, TI81XX_GPIO0_BASE);
   printf("Toggle from user space...");
   fflush(stdout);

   pGpIoRegisters[OMAP4_GPIO_OE >> 2] &= ~pin;

   for (i = 0; i < 30000000; ++i)
   {
      pGpIoRegisters[OMAP4_GPIO_SETDATAOUT >> 2] = pin;
      pGpIoRegisters[OMAP4_GPIO_CLEARDATAOUT >> 2] = pin;
   }

   pGpIoRegisters[OMAP4_GPIO_OE >> 2] |= pin;

   printf(" done\n");
   fflush(stdout);
   munmap(pGpIoRegisters, 0x400);    

   close(file);    
   return 0;
}
4b9b3361

Ответ 1

Это связано с тем, что ioremap_nocache() все еще разрешает буфер записи процессора в вашем отображении VM, тогда как pgprot_noncached() отключает как буферность, так и кешируемость.

Сравнение яблок с яблоками будет заключаться в использовании ioremap_strongly_ordered().

Ответ 2

Я предполагаю, что, поскольку mmap должен проверить, чтобы вы записывали в память, на которую вы можете писать, это будет медленнее, чем версия ядра (что я считаю/предполагаю, выполните такую ​​проверку - с модулем ядра, за который вы отвечаете за тестирование, пока не убедитесь, что не нарушаете вещи).

Попробуйте использовать do_mmap (я считаю, что тот) использовать mmap из пространства ядра и посмотреть, как это сравнивается. Если это сравнимо быстрее, то я прав. Если это не так, то что-то еще.