Hi all, I'm writing a char device driver and am having trouble getting the file to appear under /dev. This isn't my first rodeo: in fact, I've written a few other drivers in the past and they have all worked as expected. This driver is based on the source code of those other drivers, so I'm fairly confident I'm doing everything correctly. So I'm stumped and looking for help. Here is what I have in my init function: #define DRIVER_NAME "foo" static int __init mod_init(void) { dev_t devno; my_class = class_create(THIS_MODULE, DRIVER_NAME); ret = alloc_chrdev_region(&devno, 0, MAX_DEVICES, DRIVER_NAME); my_major = MAJOR(devno); ... } (Note that for brevity I'm omitting a lot of boilerplate/error handling, etc. But you can assume it's all there). My driver is also a network driver; in fact, it's *primarily* a network driver and it provides a char device file that is used to configure the hardware. I am creating the char device whenever the network device is first opened (e.g. 'netdev_open' below is the 'ndo_open' field of the 'struct netdev_ops'): struct private_data { struct cdev cdev; ... } static int netdev_open(struct net_device *dev) { struct private_data *priv = netdev_priv(dev); dev_t devno; int minor; struct device *d; minor = ida_alloc_max(&ida, MAX_DEVICES, GFP_KERNEL); devno = MKDEV(my_major, minor); cdev_init(&priv->cdev, &my_fops); cdev_add(&priv->cdev, devno, 1); /* This should create a device node with the same name as the * network interface, e.g. foo0 */ d = device_create(my_class, NULL, devno, priv, dev->name); if (IS_ERR(d)) { ... } ... } Again, I'm omitting the error checking for the sake of brevity, but it is there in the actual code. This function runs successfully and the network device is successfully opened. The 'device_create' function does not return an error, but there is nothing beneath /dev as I would expect. I'm really stumped here because everything I've been able to find online says that 'device_create' ought to create that device file. I can see my class under /sys/class/ and that directory contains a directory with the name of the device: $ ls -1 /sys/class/my_class/ foo0 so it looks like the char device *is* being created, there's just no corresponding entry under /dev. Anyway, I know that's a lot of info. If you've made it this far, thanks for reading. If you have any insight/advice/suggestions for me I'd greatly appreciate it! Thanks, Greg
On Wed, Mar 17, 2021 at 09:52:32AM -0600, Gregory Anders wrote:
Hi all,
I'm writing a char device driver and am having trouble getting the file to appear under /dev.
This isn't my first rodeo: in fact, I've written a few other drivers in the past and they have all worked as expected. This driver is based on the source code of those other drivers, so I'm fairly confident I'm doing everything correctly. So I'm stumped and looking for help.
Here is what I have in my init function:
#define DRIVER_NAME "foo"
static int __init mod_init(void) { dev_t devno; my_class = class_create(THIS_MODULE, DRIVER_NAME); ret = alloc_chrdev_region(&devno, 0, MAX_DEVICES, DRIVER_NAME);
my_major = MAJOR(devno);
... }
(Note that for brevity I'm omitting a lot of boilerplate/error handling, etc. But you can assume it's all there).
My driver is also a network driver; in fact, it's *primarily* a network driver and it provides a char device file that is used to configure the hardware. I am creating the char device whenever the network device is first opened (e.g. 'netdev_open' below is the 'ndo_open' field of the 'struct netdev_ops'):
struct private_data { struct cdev cdev; ... }
static int netdev_open(struct net_device *dev) { struct private_data *priv = netdev_priv(dev); dev_t devno; int minor; struct device *d;
minor = ida_alloc_max(&ida, MAX_DEVICES, GFP_KERNEL);
devno = MKDEV(my_major, minor); cdev_init(&priv->cdev, &my_fops); cdev_add(&priv->cdev, devno, 1);
/* This should create a device node with the same name as the * network interface, e.g. foo0 */ d = device_create(my_class, NULL, devno, priv, dev->name); if (IS_ERR(d)) { ... }
... }
Again, I'm omitting the error checking for the sake of brevity, but it is there in the actual code. This function runs successfully and the network device is successfully opened. The 'device_create' function does not return an error, but there is nothing beneath /dev as I would expect.
I'm really stumped here because everything I've been able to find online says that 'device_create' ought to create that device file. I can see my class under /sys/class/ and that directory contains a directory with the name of the device:
$ ls -1 /sys/class/my_class/ foo0
so it looks like the char device *is* being created, there's just no corresponding entry under /dev.
Is your char device listed in /sys/dev/char/ ? If not, then you have something wrong with your call to device_create(). If so, then you need to look at whatever tool is creating your device nodes in /dev. Usually the kernel handles this with devtmpfs, but I do not know what your system uses for this. Do you have a pointer to the real source code anywhere to be able to see it better to see if you are doing something wrong with the call to device_create()? Also, why a full major? Why not just use the misc_dev api instead that does all of the above "housekeeping" for you automagically? thanks, greg k-h
On Wed, 17 Mar 2021 16:59:08 +0100, Greg KH <greg@kroah.com> wrote:
Is your char device listed in /sys/dev/char/ ?
If not, then you have something wrong with your call to device_create().
If so, then you need to look at whatever tool is creating your device nodes in /dev. Usually the kernel handles this with devtmpfs, but I do not know what your system uses for this.
Indeed it is. I am working with a fairly minimal system on a Xilinx Microblaze soft-core CPU. I have udev installed, but I'm not sure about devtmpfs. I'll start digging in that direction and see what I find.
Also, why a full major? Why not just use the misc_dev api instead that does all of the above "housekeeping" for you automagically?
Simply because I wasn't aware of the misc_dev API :) But that does look like a nice abstraction that will save me some boilerplate, so thanks for pointing that out. Thanks, Greg
On Wed, Mar 17, 2021 at 10:13:22AM -0600, Gregory Anders wrote:
On Wed, 17 Mar 2021 16:59:08 +0100, Greg KH <greg@kroah.com> wrote:
Is your char device listed in /sys/dev/char/ ?
If not, then you have something wrong with your call to device_create().
If so, then you need to look at whatever tool is creating your device nodes in /dev. Usually the kernel handles this with devtmpfs, but I do not know what your system uses for this.
Indeed it is. I am working with a fairly minimal system on a Xilinx Microblaze soft-core CPU. I have udev installed, but I'm not sure about devtmpfs. I'll start digging in that direction and see what I find.
udev does not do device node creation anymore (as of a decade or so), you should make sure devtmpfs is mounted at /dev/ in order for this to work properly. good luck! greg k-h
On Wed, 17 Mar 2021 17:16:04 +0100, Greg KH <greg@kroah.com> wrote:
udev does not do device node creation anymore (as of a decade or so), you should make sure devtmpfs is mounted at /dev/ in order for this to work properly.
good luck!
greg k-h
I switched over to using the miscdevice API and the situation is the same. Nothing under /dev, but I can see my device under /sys/dev/char (it now also exists under /sys/devices/virtual/misc/, as expected). I verified that devtmpfs is enabled in the kernel config (CONFIG_DEVTMPFS=y and CONFIG_DEVTMPFS_MOUNT=y) and also see that it is mounted on /dev: # cat /proc/mounts root@xilinx-kcu105-2020_2:~# cat /proc/mounts 192.168.0.116:/data/nfs/root / nfs rw,relatime,vers=3,rsize=4096,wsize=4096,namlen=255,hard,nolock,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.0.116,mountvers=3,mountproto=tcp,local_lock=all,addr=192.168.0.116 0 0 devtmpfs /dev devtmpfs rw,relatime,size=1026312k,nr_inodes=185478,mode=755 0 0 proc /proc proc rw,relatime 0 0 sysfs /sys sysfs rw,relatime 0 0 tmpfs /run tmpfs rw,nosuid,nodev,mode=755 0 0 tmpfs /var/volatile tmpfs rw,relatime 0 0 tmpfs /dev tmpfs rw,relatime,size=64k,mode=755 0 0 devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0 I notice that it lists /dev as both devtmpfs (line 2) as well as tmpfs (the second line from the bottom). Could that be an issue? I'm not sure how or why /dev is being mounted as tmpfs in addition to devtmpfs. Thanks, Greg
On Wed, 17 Mar 2021 10:56:34 -0600, Gregory Anders <greg@gpanders.com> wrote:
I switched over to using the miscdevice API and the situation is the same. Nothing under /dev, but I can see my device under /sys/dev/char (it now also exists under /sys/devices/virtual/misc/, as expected).
I verified that devtmpfs is enabled in the kernel config (CONFIG_DEVTMPFS=y and CONFIG_DEVTMPFS_MOUNT=y) and also see that it is mounted on /dev:
# cat /proc/mounts root@xilinx-kcu105-2020_2:~# cat /proc/mounts 192.168.0.116:/data/nfs/root / nfs rw,relatime,vers=3,rsize=4096,wsize=4096,namlen=255,hard,nolock,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.0.116,mountvers=3,mountproto=tcp,local_lock=all,addr=192.168.0.116 0 0 devtmpfs /dev devtmpfs rw,relatime,size=1026312k,nr_inodes=185478,mode=755 0 0 proc /proc proc rw,relatime 0 0 sysfs /sys sysfs rw,relatime 0 0 tmpfs /run tmpfs rw,nosuid,nodev,mode=755 0 0 tmpfs /var/volatile tmpfs rw,relatime 0 0 tmpfs /dev tmpfs rw,relatime,size=64k,mode=755 0 0 devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
I notice that it lists /dev as both devtmpfs (line 2) as well as tmpfs (the second line from the bottom). Could that be an issue? I'm not sure how or why /dev is being mounted as tmpfs in addition to devtmpfs.
Thanks,
Greg
One other data point: there are 3 other misc devices on the system, and all of them are correctly listed under /dev: # ls -1 /sys/class/misc cpu_dma_latency loop-control foo0 vga_arbiter # ls -1 /dev/{cpu_dma_latency,loop-control,vga_arbiter} /dev/cpu_dma_latency /dev/loop-control /dev/vga_arbiter Greg
On Wed, Mar 17, 2021 at 10:56:34AM -0600, Gregory Anders wrote:
On Wed, 17 Mar 2021 17:16:04 +0100, Greg KH <greg@kroah.com> wrote:
udev does not do device node creation anymore (as of a decade or so), you should make sure devtmpfs is mounted at /dev/ in order for this to work properly.
good luck!
greg k-h
I switched over to using the miscdevice API and the situation is the same. Nothing under /dev, but I can see my device under /sys/dev/char (it now also exists under /sys/devices/virtual/misc/, as expected).
Then this is a userspace issue, not the kernel :)
I verified that devtmpfs is enabled in the kernel config (CONFIG_DEVTMPFS=y and CONFIG_DEVTMPFS_MOUNT=y) and also see that it is mounted on /dev:
# cat /proc/mounts root@xilinx-kcu105-2020_2:~# cat /proc/mounts 192.168.0.116:/data/nfs/root / nfs rw,relatime,vers=3,rsize=4096,wsize=4096,namlen=255,hard,nolock,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.0.116,mountvers=3,mountproto=tcp,local_lock=all,addr=192.168.0.116 0 0 devtmpfs /dev devtmpfs rw,relatime,size=1026312k,nr_inodes=185478,mode=755 0 0 proc /proc proc rw,relatime 0 0 sysfs /sys sysfs rw,relatime 0 0 tmpfs /run tmpfs rw,nosuid,nodev,mode=755 0 0 tmpfs /var/volatile tmpfs rw,relatime 0 0 tmpfs /dev tmpfs rw,relatime,size=64k,mode=755 0 0 devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
I notice that it lists /dev as both devtmpfs (line 2) as well as tmpfs (the second line from the bottom). Could that be an issue? I'm not sure how or why /dev is being mounted as tmpfs in addition to devtmpfs.
Looks like you have mounted tmpfs on top of devtmpfs, which seems very odd. Try unmounting the tmpfs instance and see if the node really is there in devtmpfs. Or better yet, mount devtmpfs somewhere else right now to see if the node is there. Good luck with your userspace mount maze! greg k-h
On Wed, 17 Mar 2021 18:15:27 +0100, Greg KH <greg@kroah.com> wrote:
Looks like you have mounted tmpfs on top of devtmpfs, which seems very odd. Try unmounting the tmpfs instance and see if the node really is there in devtmpfs.
Or better yet, mount devtmpfs somewhere else right now to see if the node is there.
Good luck with your userspace mount maze!
greg k-h
Ok I finally tracked down what was going on. The embedded distribution I am using uses mdev. mdev has an init script that mounts tmpfs on /dev, which is why devtmpfs wasn't working as expected. mdev requires the kernel option CONFIG_UEVENT_HELPER=y which was not enabled in my configuration (it even has a scary help description, something along the lines of "this should not be used", so I didn't think to enable it until I saw it mentioned somewhere else). After enabling that, the device file appears under /dev. Thanks for your help Greg!
participants (2)
-
Greg KH -
Gregory Anders