How Does Copy_From_User from the Linux Kernel Work Internally

How does copy_from_user from the Linux kernel work internally?

The implementation of copy_from_user() is highly dependent on the architecture.

On x86 and x86-64, it simply does a direct read from the userspace address and write to the kernelspace address, while temporarily disabling SMAP (Supervisor Mode Access Prevention) if it is configured. The tricky part of it is that the copy_from_user() code is placed into a special region so that the page fault handler can recognise when a fault occurs within it. A memory protection fault that occurs in copy_from_user() doesn't kill the process like it would if it is triggered by any other process-context code, or panic the kernel like it would if it occured in interrupt context - it simply resumes execution in a code path which returns -EFAULT to the caller.

copy_from_user - difficulty in copying double pointer from user space

copy_from_user copies one block of consecutive bytes from user space to kernel space. You'll need to first copy the struct ioctl_node, then copy the pointers to individual nodes, then copy the struct node's in a loop. Be sure to allocate the right amount of memory. Since you know the number of nodes in advance, you can allocate an array of them.

struct node_data {
size_t node_count;
struct nodes *nodes;
};
struct node_data *read_nodes(struct ioctl_node_user_pointer *arg)
{
struct node_data *nd = NULL;
struct ioctl_node kin = {0};
struct node *node_user_pointers = NULL;
int i;
int err;
/* Step 1: retrieve the root node */
nd = kmalloc(sizeof(*nd), GFP_KERNEL);
if (!nd) {
err = -ENOMEM;
goto error;
}
if (copy_from_user(ioctl_node_user_pointer, &kin, sizeof(kin))) {
err = -EFAULT;
goto error;
}
if (kin->count < 0 || kin->count > ((size_t)-1)/sizeof(*nodes)) {
err = -EINVAL;
goto error;
}
nd->node_count = kin.count;
/* Step 2: retrieve the pointers to individual nodes */
node_user_pointers = kmalloc(sizeof(*node_user_pointers) * nd->node_count, GFP_KERNEL);
if (node_user_pointers) {
err = -ENOMEM;
goto error;
}
if (copy_from_user(kin->nodes, node_user_pointers, sizeof(*node_user_pointers) * nd->node_count)) {
err = -EFAULT;
goto error;
}
/* Step 3: retrieve the nodes themselves */
nd->nodes = kmalloc(sizeof(*nodes) * nd->node_count, GFP_KERNEL);
if (!nd->nodes) {
err = -ENOMEM;
goto error;
}
for (i = 0; i < head.count; i++) {
if (copy_from_user(node_user_pointers[i], nd->nodes + i, sizeof(nd->nodes[0]))) {
err = -EFAULT;
goto error;
}
}
kfree(node_user_pointers);
return nd;
error:
if (nd) {
kfree(nd->nodes);
kfree(nd);
}
kfree(node_user_pointers);
return ERR_PTR(err);
}

How to use copy_to_user in workqueue (linux, kernel)?

copy_to_user intented to be called from user context only. It is bad idea to call it from workqueue context.

You can allocate memory block (kmalloc), submit it to workqueue, wait until wokqueue finished it's work in user context (in functions like read, write, ioctl) and copy workqueue result to userspace.

As an another approach you can allocate kernel memory and mmap it to userspace so the memory could be directly accessed by kernel and userspace at same time. Each time workqueue update kernel memory the data became available to userspace. You have to implement some sort of synchronization between userspace and kernel space workqueues to make this approach workable.

See uvc_queue.c for example of mmap implementation.

Why do you have to use copy_to_user()/copy_from_user() to access user space from the kernel?

These functions do a few things:

  • They check if the supplied userspace block is entirely within the user portion of the address space (access_ok()) - this prevents userspace applications from asking the kernel to read/write kernel addresses;
  • They return an error if any of the addresses are inaccessible, allowing the error to be returned to userspace (EFAULT) instead of crashing the kernel (this is implemented by special co-operation with the page fault handler, which specifically can detect when a fault occurs in one of the user memory access functions);
  • They allow architecture-specific magic, for example to ensure consistency on architectures with virtually-tagged caches, to disable protections like SMAP or to switch address spaces on architectures with separate user/kernel address spaces like S/390.

kernel driver reading ok from user space, but writing back is always 0

Your read function always returns 0 because you are returning retval, and not the count of bytes read. As long as the copy_to_user() call always succeeds, retval will always be 0. Instead, as long as copy_to_user() succeeds, you should return the number of bytes actually written to user space. This documentation states that copy_to_user() returns the total number of bytes that it was unable to copy.

As an aside, you are ignoring the value of count. It is very possible that the user is requesting less data than you have available in your buffer. You should never ignore count.

Now you have the problem where your function never returns a 0. Returning a 0 is important because is tells the user application that there is no more data available for reading and the user application should close the device file.

You need to keep track in your driver how many bytes have been read vs. how many bytes have been written. This may be implemented using your actual_rx_size.

Try this:

//Read function
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) {

ssize_t bytes;

if (actual_rx_size < count)
bytes = actual_rx_size;
else
bytes = count;

printk("user requesting data, our buffer has (%d) \n", actual_rx_size);

/* Check to see if there is data to transfer */
if (bytes == 0)
return 0;

/* Transfering data to user space */
int retval = copy_to_user(buf,rx_buffer,bytes);

if (retval) {
printk("copy_to_user() could not copy %d bytes.\n", retval);
return -EFAULT;
} else {
printk("copy_to_user() succeeded!\n");
actual_rx_size -= bytes;
return bytes;
}
}


Related Topics



Leave a reply



Submit