如何在读写字符设备时避免CPU使用率过高?

法齐熊

我需要为带有SRAM的PCIe设备编写一个Linux内核驱动程序。

第一次尝试,我编写了一个驱动程序,用于使用字符设备从PCIe访问SRAM。

一切都按预期工作,但是有一个问题。SRAM慢1MB读取/写入大约需要2秒钟,这是硬件限制。读取/写入时,CPU 100%繁忙。女巫是个问题。我不需要速度,读取/写入可能会很慢,但是为什么要占用这么多CPU?

缓冲区初始化为pci_iomap

  g_mmio_buffer[0] = pci_iomap(pdev, SRAM_BAR_H, g_mmio_length);

读/写功能如下:

static ssize_t dev_read(struct file *fp, char *buf, size_t len, loff_t *off) {
  unsigned long rval;
  size_t copied;

  rval = copy_to_user(buf, g_mmio_buffer[SRAM_BAR] + *off, len);

  if (rval < 0) return -EFAULT;

  copied = len - rval;
  *off += copied;

  return copied;
}

static ssize_t dev_write(struct file *fp, const char *buf, size_t len, loff_t *off) {
  unsigned long rval;
  size_t copied;

  rval = copy_from_user(g_mmio_buffer[SRAM_BAR] + *off, buf, len);

  if (rval < 0) return -EFAULT;

  copied = len - rval;
  *off += copied;

  return copied;
}

问题是高CPU使用率该怎么办?

我应该重写驱动程序以使用块设备而不是字符吗?

在读取/保存数据时允许CPU在另一个进程上工作吗?

伊恩·雅培(Ian Abbott)

正如@ 0andriy指出的那样,您不应直接访问iomem。有诸如memcpy_toio()和的功能memcpy_fromio()可以在iomem和普通内存之间复制,但是它们仅适用于内核虚拟地址。

为了在不使用中间数据缓冲区的情况下从用户空间地址复制到iomem,需要将用户空间内存页面“固定”到物理内存中。可以使用来完成get_user_pages_fast()但是,固定页面可能位于内核中永久映射的内存之外的“高内存”(highmem)中。此类页面需要使用短期内临时映射到内核虚拟地址空间kmap_atomic()(存在使用的规则kmap_atomic(),还有一些用于highmem长期映射的功能。有关详细信息,请查看highmem文档。)

一旦一个用户空间页面有BEEM映射到内核的虚拟地址空间,memcpy_toio()并且memcpy_fromio()可以使用页面和IOMEM之间进行复制。

由暂时映射的页面kmap_atomic()需要由取消映射kunmap_atomic()

通过固定用户内存页get_user_pages_fast()需要通过调用单独未锁定的put_page(),但如果页面内存已经被写入(例如memcpy_fromio(),它必须首先被标记为“脏”set_page_dirty_lock()之前调用put_page()

将所有这些放在一起,可以使用以下功能在用户内存和iomem之间进行复制:

#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/io.h>

/**
 * my_copy_to_user_from_iomem - copy to user memory from MMIO
 * @to:     destination in user memory
 * @from:   source in remapped MMIO
 * @n:      number of bytes to copy
 * Context: process
 *
 * Returns number of uncopied bytes.
 */
long my_copy_to_user_from_iomem(void __user *to, const void __iomem *from,
                unsigned long n)
{
    might_fault();
    if (!access_ok(to, n))
        return n;
    while (n) {
        enum { PAGE_LIST_LEN = 32 };
        struct page *page_list[PAGE_LIST_LEN];
        unsigned long start;
        unsigned int p_off;
        unsigned int part_len;
        int nr_pages;
        int i;

        /* Determine pages to do this iteration. */
        p_off = offset_in_page(to);
        start = (unsigned long)to - p_off;
        nr_pages = min_t(int, PAGE_ALIGN(p_off + n) >> PAGE_SHIFT,
                 PAGE_LIST_LEN);
        /* Lock down (for write) user pages. */
        nr_pages = get_user_pages_fast(start, nr_pages, 1, page_list);
        if (nr_pages <= 0)
            break;

        /* Limit number of bytes to end of locked-down pages. */
        part_len =
            min(n, ((unsigned long)nr_pages << PAGE_SHIFT) - p_off);

        /* Copy from iomem to locked-down user memory pages. */
        for (i = 0; i < nr_pages; i++) {
            struct page *page = page_list[i];
            unsigned char *p_va;
            unsigned int plen;

            plen = min((unsigned int)PAGE_SIZE - p_off, part_len);
            p_va = kmap_atomic(page);
            memcpy_fromio(p_va + p_off, from, plen);
            kunmap_atomic(p_va);
            set_page_dirty_lock(page);
            put_page(page);
            to = (char __user *)to + plen;
            from = (const char __iomem *)from + plen;
            n -= plen;
            part_len -= plen;
            p_off = 0;
        }
    }
    return n;
}

/**
 * my_copy_from_user_to_iomem - copy from user memory to MMIO
 * @to:     destination in remapped MMIO
 * @from:   source in user memory
 * @n:      number of bytes to copy
 * Context: process
 *
 * Returns number of uncopied bytes.
 */
long my_copy_from_user_to_iomem(void __iomem *to, const void __user *from,
                unsigned long n)
{
    might_fault();
    if (!access_ok(from, n))
        return n;
    while (n) {
        enum { PAGE_LIST_LEN = 32 };
        struct page *page_list[PAGE_LIST_LEN];
        unsigned long start;
        unsigned int p_off;
        unsigned int part_len;
        int nr_pages;
        int i;

        /* Determine pages to do this iteration. */
        p_off = offset_in_page(from);
        start = (unsigned long)from - p_off;
        nr_pages = min_t(int, PAGE_ALIGN(p_off + n) >> PAGE_SHIFT,
                 PAGE_LIST_LEN);
        /* Lock down (for read) user pages. */
        nr_pages = get_user_pages_fast(start, nr_pages, 0, page_list);
        if (nr_pages <= 0)
            break;

        /* Limit number of bytes to end of locked-down pages. */
        part_len =
            min(n, ((unsigned long)nr_pages << PAGE_SHIFT) - p_off);

        /* Copy from locked-down user memory pages to iomem. */
        for (i = 0; i < nr_pages; i++) {
            struct page *page = page_list[i];
            unsigned char *p_va;
            unsigned int plen;

            plen = min((unsigned int)PAGE_SIZE - p_off, part_len);
            p_va = kmap_atomic(page);
            memcpy_toio(to, p_va + p_off, plen);
            kunmap_atomic(p_va);
            put_page(page);
            to = (char __iomem *)to + plen;
            from = (const char __user *)from + plen;
            n -= plen;
            part_len -= plen;
            p_off = 0;
        }
    }
    return n;
}

其次,你可能能够通过映射IOMEM为“写联合”通过更换加快内存访问pci_iomap()使用pci_iomap_wc()

第三,访问慢速内存时避免等待状态的唯一真实方法是不使用CPU,而是使用DMA传输。细节很大程度上取决于您的PCIe设备的总线主控DMA功能(如果有的话)。get_user_pages_fast()在DMA传输期间,用户存储页面仍需要固定(例如通过),但不需要通过临时映射kmap_atomic()

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何获得CPU使用率

Golang stdin循环上的CPU使用率过高

Java线程:CPU使用率过高

观看'./**/*.js'会导致CPU使用率过高

如何减少ffmpeg的cpu使用率?

docker容器内的webpack-dev-server轮询-CPU使用率过高

如何停止Facebook搜寻器导致CPU使用率过高

OpenCV Polylines CPU使用率过高

使用Angular5 + cytoscapeJS时的CPU使用率过高

会尝试捕获-导致CPU使用率过高吗?

使用蝗虫时的CPU使用率

如何解决CPU使用率过高的问题?

为什么USB 3.0端口会导致CPU使用率过高?

Dropbox导致Mac OS X 10.8.2(Mountain Lion)上的CPU使用率过高

如何在通知中包括%CPU使用率?

“系统”和“系统中断”导致的CPU使用率过高(由ACPI.sys引起)

由于频繁重绘C#Windows表单自定义控件,如何减少CPU使用率过高?

死锁会导致CPU使用率过高吗?

锁定游戏更新速度会导致CPU使用率过高。不执行任何操作时如何使线程休眠?

Java:游戏循环-CPU使用率过高

如何获得CPU图形使用率?

当CPU /内存使用率过高时,Bash脚本会自动终止进程

当CPU使用率超过___%时如何防止睡眠模式

如何限制Skype CPU使用率

向下滚动Facebook网页会导致CPU使用率过高正常吗?

没有活动查询时如何避免CPU使用率过高

如何限制Fortify CPU使用率?

PyQt5 - 如何在使用 QThread 时减少 CPU 使用率(低于 50%)?

在 Qlabel 中使用 QTime 和 Qtimer 作为时钟导致 CPU 使用率过高