Hi Arnaud, Thanks for the reply.
On Fri, Jun 20, 2025 at 09:52:03AM +0200, Arnaud POULIQUEN wrote: > > > On 6/19/25 16:43, Dawei Li wrote: > > Hi Arnaud, > > Thanks for review. > > > > On Wed, Jun 18, 2025 at 03:07:36PM +0200, Arnaud POULIQUEN wrote: > >> Hello Dawei, > >> > >> > >> Please find a few comments below. It is not clear to me which parts of your > >> implementation are mandatory and which are optional "nice-to-have" > >> optimizations. > > > > It's more like an improvement. > > > >> > >> Based on (potentially erroneous) hypothesis, you will find a suggestion > >> for an > >> alternative to the anonymous inode approach, which does not seem to be a > >> common > >> interface. > > > > AFAIC, annoymous inode is a common interface and used extensivly in kernel > > development. > > Some examples below. > > > >> > >> > >> On 6/9/25 17:15, Dawei Li wrote: > >>> Hi, > >>> > >>> This is V4 of series which introduce new uAPI(RPMSG_CREATE_EPT_FD_IOCTL) > >>> for rpmsg subsystem. > >>> > >>> Current uAPI implementation for rpmsg ctrl & char device manipulation is > >>> abstracted in procedures below: > >>> - fd = open("/dev/rpmsg_ctrlX") > >>> - ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &info); /dev/rpmsgY devnode is > >>> generated. > >>> - fd_ep = open("/dev/rpmsgY", O_RDWR) > >>> - operations on fd_ep(write, read, poll ioctl) > >>> - ioctl(fd_ep, RPMSG_DESTROY_EPT_IOCTL) > >>> - close(fd_ep) > >>> - close(fd) > >>> > >>> This /dev/rpmsgY abstraction is less favorable for: > >>> - Performance issue: It's time consuming for some operations are > >>> invovled: > >>> - Device node creation. > >>> Depends on specific config, especially CONFIG_DEVTMPFS, the overall > >>> overhead is based on coordination between DEVTMPFS and userspace > >>> tools such as udev and mdev. > >>> > >>> - Extra kernel-space switch cost. > >>> > >>> - Other major costs brought by heavy-weight logic like device_add(). > >> > >> Is this a blocker of just optimization? > > > > Yep, performance is one of motivations of this change. > > > >> > >>> > >>> - /dev/rpmsgY node can be opened only once. It doesn't make much sense > >>> that a dynamically created device node can be opened only once. > >> > >> > >> I assume this is blocker with the fact that you need to open the > >> /dev/rpmsg<x> > >> to create the endpoint. > > > > Yes. You have to open /dev/rpmsgX which is generated by legacy ioctl to > > instantiate a new endpoint. > > > >> > >> > >>> > >>> - For some container application such as docker, a client can't access > >>> host's dev unless specified explicitly. But in case of /dev/rpmsgY, > >>> which > >>> is generated dynamically and whose existence is unknown for clients in > >>> advance, this uAPI based on device node doesn't fit well. > >> > >> does this could be solve in userspace parsing /sys/class/rpmsg/ directory > >> to > >> retreive the device? > > > > Hardly, because client still can't access /dev/rpmsgX which is generated > > by host _after_ client is launched. > > > This part is not clear to me; could you provide more details? > I cannot figure out why a client can access /dev/rpmsg_ctrlX but not > /dev/rpmsgX. Well, let's take docker as example: For docker, when a client is launched and it wants to access host's device, it must make explicit request when it's launched: docker run --device=/dev/xxx Let's presume that xxx is /dev/rpmsgX generated dynamically by _host_. Docker command above knows nothing about these rpmsg nodes which are generated by host _after_ client is launched. And yes, parsing /sys/class/rpmsg may acquire info about rpmsg devices, but client still can't access /dev/rpmsgX. > > > > > >> > >> You could face same kind of random instantiation for serial peripherals ( > >> UART; > >> USb, I2C,...) based on a device tree enumeration. I suppose that user space > >> use to solve this. > >> > >>> > >>> An anonymous inode based approach is introduced to address the issues > >>> above. > >>> Rather than generating device node and opening it, rpmsg code just creates > >>> an anonymous inode representing eptdev and return the fd to userspace. > >> > >> A drawback is that you need to share fb passed between processes. > > > > Fd is the abstraction of an unique endpoint device, it holds true for > > both legacy and new approach. > > > > So I guess what you mean is that /dev/rpmsgX is global to all so other > > process > > can access it? > > > > But /dev/rpmsgX is designed to be opened only once, it's implemented as > > singleton pattern. > > > > static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) > > { > > ... > > if (eptdev->ept) { > > mutex_unlock(&eptdev->ept_lock); > > return -EBUSY; > > } > > ... > > eptdev->ept = ept; > > ... > > } > > > > [...] > > > >>> printf("loop[%d]\n", loop); > >>> > >>> gettimeofday(&start, NULL); > >>> > >>> while (loop--) { > >> > >> Do you need to create /close Endpoint sevral times in your real use case > >> with > >> high timing > >> constraint? > > > > No, it's just a silly benchmark demo, large sample reduces noise > > statistically. > > > >> > >>> fd_info.fd = -1; > >>> fd_info.flags = O_RDWR | O_CLOEXEC | O_NONBLOCK; > >>> ret = ioctl(fd, RPMSG_CREATE_EPT_FD_IOCTL, &fd_info); > >>> if (ret < 0 || fd_info.fd < 0) { > >>> printf("ioctl[RPMSG_CREATE_EPT_FD_IOCTL] failed, > >>> ret[%d]\n", ret); > >>> } > >>> > >> > >> > >>> ret = ioctl(fd_info.fd, RPMSG_DESTROY_EPT_IOCTL, &info); > >>> if (ret < 0) { > >>> printf("new ioctl[RPMSG_DESTROY_EPT_IOCTL] failed, > >>> ret[%d]\n", ret); > >>> } > >>> > >>> close(fd_info.fd); > >> > >> It seems strange to me to use ioctl() for opening and close() for closing, > >> from > >> a symmetry point of view. > > > > Sorry to hear that. But no, it's a pretty normal skill in kernel codebase > > , I had to copy some examples from reply to other reviewer[1]. > > I missed this one, apologize for the duplication. > > > > > anon_inode_get_{fd,file} are used extensively in kernel for returning a new > > fd to userspace which is associated with an unique data structure in kernel > > space, in different ways: > > > > - via ioctl(), some examples are: > > > > - KVM ioctl(s) > > - KVM_CREATE_VCPU -> kvm_vm_ioctl_create_vcpu > > - KVM_GET_STATS_FD -> kvm_vcpu_ioctl_get_stats_fd > > - KVM_CREATE_DEVICE -> kvm_ioctl_create_device > > - KVM_CREATE_VM -> kvm_dev_ioctl_create_vm > > > > - DMA buf/fence/sync ioctls > > - DMA_BUF_IOCTL_EXPORT_SYNC_FILE -> dma_buf_export_sync_file > > - SW_SYNC_IOC_CREATE_FENCE -> sw_sync_ioctl_create_fence > > - Couples of driver implement DMA buf by using anon file _implicitly_: > > - UDMABUF_CREATE -> udmabuf_ioctl_create > > - DMA_HEAP_IOCTL_ALLOC -> dma_heap_ioctl_allocate > > > > - gpiolib ioctls: > > - GPIO_GET_LINEHANDLE_IOCTL -> linehandle_create > > - GPIO_V2_GET_LINE_IOCTL > > > > - IOMMUFD ioctls: > > > > - VFIO Ioctls: > > > > - .... > > > > > > - via other specific syscalls: > > - epoll_create1 > > - bpf > > - perf_event_open > > - inotify_init > > - ... > > If we put the optimization aspect aside, what seems strange to me is that the > purpose of rpmsg_char was to expose a FS character device to user space. If we > need tobypass the use of /dev/rpmsgX, does it make sense to support an > anonymous > inode in this driver? I am clearly not legitimate to answer this question... You have every right to do so, after all, it's purely a technical discussion :). I admit it's bit confusing to add annoymous inode logic to a file named rpmsg_char.c which implies 'character' device. That's why I rename API following Mathieu's comment: - __rpmsg_chrdev_eptdev_alloc -> rpmsg_eptdev_alloc - __rpmsg_chrdev_eptdev_add -> rpmsg_eptdev_add As to topic how these two uAPI(s) co-exist and affect each other. This change is based on rules: 1. Never break existing uAPI. 2. Try best to reuse existing codebase. 3. Userspace can choose whatever approach they want to. Thanks, Dawei > > > Thanks, > Arnaud > > > > > [1] https://lore.kernel.org/all/20250530125008.GA5355@wendao-VirtualBox/ > > > >> > >> Regarding your implementation, I wonder if we could keep the /dev/rpmsg<x> > >> device with specific open() and close() file operations associated with > >> your new > >> ioctl. > >> > >> - The ioctl would create the endpoint. > >> - The open() and close() operations would simply manage the file > >> descriptor and > >> increment/decrement a counter to prevent premature endpoint destruction. > >> > >> > >> Regards, > >> Arnaud > >> > > > > [...] > > > > Thanks, > > > > Dawei