Copy_from_user always fail

I’m currently using a PCIe driver on a Jetson Nano for DMA operations. However, when using copy_from_user() to copy a struct, the function always fails, returning a value equal to the full byte size of the struct.

I monitored the kernel logs with dmesg -w, but aside from my printk messages, nothing unusual appears.

My Environment:
hardware version: Jetson nano b01 4gb
system version : ubuntu 18.04
kernel verison : 4.9.140-tegra

Here’s my code:

in main.c:
typedef struct DMA_DATA

{

unsigned char read_buf[4096];

unsigned char write_buf[4096];

} DMA_DATA;

typedef struct DMA_OPERATION

{

unsigned int current_len;

unsigned int offset_addr;

unsigned int cmd;

DMA_DATA data;

} DMA_OPERATION;

DMA_OPERATION dma_operation;
DMA_OPERATION *dma_oper = &dma_operation;// ← the struct that I want to copy
int fd;
int main(int argc, char *argv)
{

pthread_t t1, t2;
pthread_attr_t attr1, attr2;
struct sched_param sch1, sch2;

fd = open_pci_driver();
if (fd < 0)
{
	return -1;
}

pthread_attr_init(&attr2);

sch2.sched_priority = 90;

pthread_attr_setinheritsched(&attr2, PTHREAD_EXPLICIT_SCHED);

pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);

pthread_attr_setschedparam(&attr2, &sch2);

    pthread_create(&t2, &attr2, thread_func2, NULL);

}
void *thread_func2(void *ptr)
{
int len;
int i;
struct timeval starttime, endtime;
long timeuse = 0;
long recv_write_time = 0;
long recv_read_time = 0;
int k;

int r;

printf("dma number test start\n");

printf("fd=%d\n", fd);
attach_cpu(0);
printf("DMA_OPERATION sizeof = %ld\n",sizeof(DMA_OPERATION));
r = ioctl(fd, PCI_MAP_ADDR_CMD, dma_oper); //<----fails here
    ......

}

in the driver file
pci_driver.h:

typedef struct DMA_DATA

{

unsigned char read_buf[4096];

unsigned char write_buf[4096];

}DMA_DATA;

typedef struct DMA_OPERATION

{

unsigned int current_len;

unsigned int offset_addr;

unsigned int cmd;

DMA_DATA data;

}DMA_OPERATION;

DMA_OPERATION dma_operation;

in pci_driver.c:

long cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) // <---- User-implemented ioctl
{
struct PciPango *pci_pango = &pci_info;
unsigned int value = 0;
unsigned int current_len = 0;

switch (cmd)
{

        .......

case PCI_MAP_ADDR_CMD:
{
	int ret;

	spin_lock(&dma_info.addr_r.lock);
	if (!access_ok(VERIFY_READ, (DMA_OPERATION *)arg, sizeof(DMA_OPERATION)))
	{
		printk(KERN_ERR "EFAULT-access_ok failed!\n");
		return -EFAULT;
	}                                                                                //<--- access_ok check didn't fail
	ret = copy_from_user(&dma_operation, (DMA_OPERATION *)arg, sizeof(DMA_OPERATION));        // <---- this function failed to copy the struct
	if (ret != 0)
	{
		printk(KERN_ERR "EFAULT-copy_from_user Error,ret=%d\n", ret); // <----- ret always equal sizeof(DMA_OPERATION)
		spin_unlock(&dma_info.addr_r.lock);
		return -EFAULT;
	}

	if (dma_operation.current_len <= 0 || dma_operation.current_len > DMA_MAX_PACKET_SIZE)
	{
		printk(KERN_ERR "EINVAL-current_len illegal\n");
		spin_unlock(&dma_info.addr_r.lock);
		return -EINVAL;
	}

	dma_info.addr_r.data_buf = pci_alloc_consistent(op_dev,
													dma_operation.current_len,
													&dma_info.addr_r.addr);
	if (!dma_info.addr_r.data_buf)
	{
		printk(KERN_ERR "ENOMEM-DMA read buffer is not allocated\n");
		spin_unlock(&dma_info.addr_r.lock);
		return -ENOMEM;
	}
	dma_info.addr_w.data_buf = pci_alloc_consistent(op_dev,
													dma_operation.current_len,
													&dma_info.addr_w.addr);
	if (!dma_info.addr_w.data_buf)
	{
		printk(KERN_ERR "ENOMEM-DMA write buffer is not allocated\n");
		pci_free_consistent(op_dev, dma_operation.current_len,
							dma_info.addr_r.data_buf, dma_info.addr_r.addr);
		spin_unlock(&dma_info.addr_r.lock);
		return -ENOMEM;
	}

	dma_info.addr_r.addr_size = ((dma_info.addr_r.addr >> 32) > 0) ? 1 : 0;
	dma_info.addr_w.addr_size = ((dma_info.addr_w.addr >> 32) > 0) ? 1 : 0;

	if (dma_operation.current_len < 4)
	{
		printk(KERN_ERR "EINVAL-current_len illegal\n");
		pci_free_consistent(op_dev, dma_operation.current_len,
							dma_info.addr_w.data_buf, dma_info.addr_w.addr);
		pci_free_consistent(op_dev, dma_operation.current_len,
							dma_info.addr_r.data_buf, dma_info.addr_r.addr);
		spin_unlock(&dma_info.addr_r.lock);
		return -EINVAL;
	}

	dma_info.cmd.data.length = (dma_operation.current_len >> 2) - 1;
	dma_info.cmd.data.addr_type = dma_info.addr_r.addr_size;

	spin_unlock(&dma_info.addr_r.lock);
	break;
}

    ......

}
return 0;

}

related dmesg log:
[ 56.892131] init cdev class result : 0 // <— begin of program
[ 66.925193] EFAULT-copy_from_user Error,ret=8208 // <— printk in ioctl
[ 66.930349] DMA write buffer is not allocated // <— printk in ioctl
[ 67.935897] pango_cdev_release. // <— end of program

I wonder if its a problem related to the kernel version(4.9.140-tegra) or PAN/UAO mechanism of ARMv8 . I tried to build the kernel with PAN/UAO compile option off , but that didn’t solve my problem either.

Sorry for the late response.
Is this still an issue to support? Any result can be shared?

yes. I’m still confused by this weird problem.
Currently no further message other than those in my first post.The function copy_from_user simply returns sizeof(DMA_OPERATION) . DMA_OPERATION is the struct I needed to copy.