devtmpfs
기존 커널이 사용했던 devfs를 대신하여 커널 2.6에 도입된 udev 데몬을 통해 디바이스 드라이버의 로딩이 이루어진다. udev 데몬이 유저 프로세스에서 동작하므로 커널 부팅 후 루트 파일시스템을 마운트한 후에야 디바이스 드라이버의 로딩이 가능해진다. 대부분의 디바이스 드라이버들은 커널 부트업 과정에서 로딩이 이루어지므로 루트 파일 시스템을 로딩하기 전에 initrd/initramfs 등에 존재하는 디바이스 드라이버를 임시 파일 시스템인 devtmpfs에 로딩시켜 부팅 시간을 단축할 목적으로 사용한다.
다음 그림과 같이 루트 파일 시스템을 마운트하기 전에 디바이스 드라이버들이 devtmpfs에 로딩되어 부팅 시간을 단축시키는 과정을 보여준다.
devtmpfs_init()
drivers/base/devtmpfs.c
/* * Create devtmpfs instance, driver-core devices will add their device * nodes here. */ int __init devtmpfs_init(void) { int err = register_filesystem(&dev_fs_type); if (err) { printk(KERN_ERR "devtmpfs: unable to register devtmpfs " "type %i\n", err); return err; } thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); if (!IS_ERR(thread)) { wait_for_completion(&setup_done); } else { err = PTR_ERR(thread); thread = NULL; } if (err) { printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); unregister_filesystem(&dev_fs_type); return err; } printk(KERN_INFO "devtmpfs: initialized\n"); return 0; }
다음 devtmpfs라는 이름을 가진 파일 시스템을 등록한다. 그런 후 “kdevtmpfs” 스레드를 동작시킨다.
static struct file_system_type dev_fs_type = { .name = "devtmpfs", .mount = dev_mount, .kill_sb = kill_litter_super, };
kdevtmpfs 스레드
다음 그림은 디바이스 드라이버들의 로딩/언로딩 요청에 대해 devtmpfs에 디바이스 노드를 생성 및 삭제하는 과정을 보여준다.
devtmpfsd()
drivers/base/devtmpfs.c
static int devtmpfsd(void *p) { char options[] = "mode=0755"; int *err = p; *err = sys_unshare(CLONE_NEWNS); if (*err) goto out; *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options); if (*err) goto out; sys_chdir("/.."); /* will traverse into overmounted root */ sys_chroot("."); complete(&setup_done); while (1) { spin_lock(&req_lock); while (requests) { struct req *req = requests; requests = NULL; spin_unlock(&req_lock); while (req) { struct req *next = req->next; req->err = handle(req->name, req->mode, req->uid, req->gid, req->dev); complete(&req->done); req = next; } spin_lock(&req_lock); } __set_current_state(TASK_INTERRUPTIBLE); spin_unlock(&req_lock); schedule(); } return 0; out: complete(&setup_done); return *err; }
kdevtmpfs 스레드가 처음 동작하고 무한 루프를 돌며 디바이스 노드 생성 및 삭제 요청을 처리하는 함수이다.
- 코드 라인 5~7에서 현재 동작중인 파일 시스템 정보를 복사하여 새 스레드에서 사용할 수 있게 한다.
- 코드 라인 8~10에서 “devtmpfs” 파일 시스템을 마운트한다.
- 코드 라인 11~13에서 루트 디렉토리로 이동시킨 후 setup_done 변수에 complete 신호를 보내 싱크 대기중인 부모 태스크를 동작하게 한다.
- 코드 라인 14~33에서 반복 루프를 돌며 요청이 있는 경우 요청에 대한 처리를 수행한다.
handle()
base/devtmpfs.c
static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) { if (mode) return handle_create(name, mode, uid, gid, dev); else return handle_remove(name, dev); }
mode에 따라 핸들 생성 및 삭제 함수를 호출한다.
handle_create()
drivers/base/devtmpfs.c
static int handle_create(const char *nodename, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) { struct dentry *dentry; struct path path; int err; dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); if (dentry == ERR_PTR(-ENOENT)) { create_path(nodename); dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); } if (IS_ERR(dentry)) return PTR_ERR(dentry); err = vfs_mknod(d_inode(path.dentry), dentry, mode, dev->devt); if (!err) { struct iattr newattrs; newattrs.ia_mode = mode; newattrs.ia_uid = uid; newattrs.ia_gid = gid; newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; inode_lock(d_inode(dentry)); notify_change(dentry, &newattrs, NULL); inode_unlock(d_inode(dentry)); /* mark as kernel-created inode */ d_inode(dentry)->i_private = &thread; } done_path_create(&path, dentry); return err; }
디바이스 파일을 생성하고 디바이스 노드를 생성한다.
handle_remove()
drivers/base/devtmpfs.c
static int handle_remove(const char *nodename, struct device *dev) { struct path parent; struct dentry *dentry; int deleted = 0; int err; dentry = kern_path_locked(nodename, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (d_really_is_positive(dentry)) { struct kstat stat; struct path p = {.mnt = parent.mnt, .dentry = dentry}; err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE, AT_STATX_SYNC_AS_STAT); if (!err && dev_mynode(dev, d_inode(dentry), &stat)) { struct iattr newattrs; /* * before unlinking this node, reset permissions * of possible references like hardlinks */ newattrs.ia_uid = GLOBAL_ROOT_UID; newattrs.ia_gid = GLOBAL_ROOT_GID; newattrs.ia_mode = stat.mode & ~0777; newattrs.ia_valid = ATTR_UID|ATTR_GID|ATTR_MODE; inode_lock(d_inode(dentry)); notify_change(dentry, &newattrs, NULL); inode_unlock(d_inode(dentry)); err = vfs_unlink(d_inode(parent.dentry), dentry, NULL); if (!err || err == -ENOENT) deleted = 1; } } else { err = -ENOENT; } dput(dentry); inode_unlock(d_inode(parent.dentry)); path_put(&parent); if (deleted && strchr(nodename, '/')) delete_path(nodename); return err; }
디바이스 노드를 삭제하고, 디바이스 파일도 삭제한다.
참고
- Device & Driver -1- (Device Driver Model) | 문c
- Device & Driver -2- (Bus & Class) | 문c
- Device & Driver -3- (Platform device) | 문c
- Sysfs (kobject & kset) | 문c
- driver_init() | 문c
- devtmpfs & kdevtmpfs 스레드 | 문c – 현재글
- do_initcalls() | 문c
- Linux Device Model Part 1 | Sarah Diesburg – 다운로드 pdf
- Linux Device Model Part 2 | Sarah Diesburg – 다운로드 pdf
- Linux Device Drivers | OPERSYS – 다운로드 pdf
- Embedded data structures and lifetime management | Shuah Khan, Samsung Open Source Group – 다운로드 pdf
- The Sysfs Virtual Filesystem Exploring the Linux Device Model | Bill Gatliff – 다운로드 pdf
- Linux Device Drivers | Jie Liao – 다운로드 pdf
- The zen of kobjects | LWN.net
- Linux Device Drivers, Third Edition | LWN.net
- kobject/kset/ktype documentation and example code updated | LWN.net
- Documentation/driver-model/ | kernel.org
- Brief sysfs Tutorial | Ben Marks – 다운로드 pdf
- Rules on how to access information in sysfs | kernel.org