copy_to_user returns 字符设备读取函数出错

copy_to_user returns an error in a char device read function

我已经为我的内核模块实现了一个字符设备,并为它实现了一个读取函数。读取函数调用copy_to_user到return数据给调用者。我最初以阻塞方式(使用 wait_event_interruptible)实现读取功能,但即使我以非阻塞方式实现读取,问题也会重现。我的代码是 运行 在 MIPS 处理器上。

用户space程序打开字符设备并读入分配在堆栈上的缓冲区。

我发现 copy_to_user 偶尔会无法复制任何字节。此外,即使我将 copy_to_user 替换为对 memcpy 的调用(仅用于检查目的......我知道这不是正确的做法),并立即打印出目标缓冲区之后,我看到 memcpy 未能复制任何字节。

我不太确定如何进一步调试 - 我如何确定为什么内存没有被复制?有没有可能是进程上下文错误?

编辑:这是一些伪代码,概述了代码当前的样子:

用户模式(重复运行):

char buf[BUF_LEN];
FILE *f = fopen(char_device_file, "rb");
fread(buf, 1, BUF_LEN, f);
fclose(f);

内核模式:

char_device = 
    create_char_device(char_device_name,
        NULL,
        read_func,
        NULL,
        NULL);

int read_func(char *output_buffer, int output_buffer_length, loff_t *offset)
{
    int rc;
    if (*offset == 0)
    {
        spin_lock_irqsave(&lock, flags);

        while (get_available_bytes_to_read() == 0)
        {
            spin_unlock_irqrestore(&lock, flags);
            if (wait_event_interruptible(self->wait_queue, get_available_bytes_to_read() != 0))
            {
                // Got a signal; retry the read
                return -ERESTARTSYS;
            }

            spin_lock_irqsave(&lock, flags);
        }

        rc = copy_to_user(output_buffer, internal_buffer, bytes_to_copy);

        spin_unlock_irqrestore(&lock, flags);
    } 
    else rc = 0;

    return rc;
}

调试了很多,但最终 Tsyvarev 的提示(关于不调用 copy_to_user 并使用自旋锁的注释)似乎是原因。

我们的进程有一个后台线程,偶尔会启动一个新进程 (fork + exec)。当我们禁用该线程时,一切正常。我们拥有的最好的理论是,fork 使我们所有的内存页都是写时复制,因此当我们试图复制到它们时,内核必须做一些使用自旋锁无法完成的工作。希望它至少有一定的意义(尽管我猜这只适用于子进程,而父进程的页面将只是保持可写,但谁知道...)。

我们重写了无锁代码,问题消失了。

现在我们只需要验证我们的无锁代码在不同的架构上确实是安全的。简单易行。