Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * dir.c - Operations for sysfs directories. | |
3 | */ | |
4 | ||
5 | #undef DEBUG | |
6 | ||
7 | #include <linux/fs.h> | |
8 | #include <linux/mount.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/kobject.h> | |
5f45f1a7 | 11 | #include <linux/namei.h> |
2b611bb7 | 12 | #include <linux/idr.h> |
94bebf4d | 13 | #include <asm/semaphore.h> |
1da177e4 LT |
14 | #include "sysfs.h" |
15 | ||
16 | DECLARE_RWSEM(sysfs_rename_sem); | |
dd14cbc9 | 17 | spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; |
1da177e4 | 18 | |
2b611bb7 TH |
19 | static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; |
20 | static DEFINE_IDA(sysfs_ino_ida); | |
21 | ||
22 | int sysfs_alloc_ino(ino_t *pino) | |
23 | { | |
24 | int ino, rc; | |
25 | ||
26 | retry: | |
27 | spin_lock(&sysfs_ino_lock); | |
28 | rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino); | |
29 | spin_unlock(&sysfs_ino_lock); | |
30 | ||
31 | if (rc == -EAGAIN) { | |
32 | if (ida_pre_get(&sysfs_ino_ida, GFP_KERNEL)) | |
33 | goto retry; | |
34 | rc = -ENOMEM; | |
35 | } | |
36 | ||
37 | *pino = ino; | |
38 | return rc; | |
39 | } | |
40 | ||
41 | static void sysfs_free_ino(ino_t ino) | |
42 | { | |
43 | spin_lock(&sysfs_ino_lock); | |
44 | ida_remove(&sysfs_ino_ida, ino); | |
45 | spin_unlock(&sysfs_ino_lock); | |
46 | } | |
47 | ||
fa7f912a TH |
48 | void release_sysfs_dirent(struct sysfs_dirent * sd) |
49 | { | |
13b3086d TH |
50 | struct sysfs_dirent *parent_sd; |
51 | ||
52 | repeat: | |
53 | parent_sd = sd->s_parent; | |
54 | ||
fa7f912a TH |
55 | if (sd->s_type & SYSFS_KOBJ_LINK) { |
56 | struct sysfs_symlink * sl = sd->s_element; | |
57 | kfree(sl->link_name); | |
58 | kobject_put(sl->target_kobj); | |
59 | kfree(sl); | |
60 | } | |
61 | kfree(sd->s_iattr); | |
2b611bb7 | 62 | sysfs_free_ino(sd->s_ino); |
fa7f912a | 63 | kmem_cache_free(sysfs_dir_cachep, sd); |
13b3086d TH |
64 | |
65 | sd = parent_sd; | |
66 | if (sd && atomic_dec_and_test(&sd->s_count)) | |
67 | goto repeat; | |
fa7f912a TH |
68 | } |
69 | ||
1da177e4 LT |
70 | static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) |
71 | { | |
72 | struct sysfs_dirent * sd = dentry->d_fsdata; | |
73 | ||
74 | if (sd) { | |
dd14cbc9 TH |
75 | /* sd->s_dentry is protected with sysfs_lock. This |
76 | * allows sysfs_drop_dentry() to dereference it. | |
77 | */ | |
78 | spin_lock(&sysfs_lock); | |
79 | ||
80 | /* The dentry might have been deleted or another | |
81 | * lookup could have happened updating sd->s_dentry to | |
82 | * point the new dentry. Ignore if it isn't pointing | |
83 | * to this dentry. | |
84 | */ | |
85 | if (sd->s_dentry == dentry) | |
86 | sd->s_dentry = NULL; | |
87 | spin_unlock(&sysfs_lock); | |
1da177e4 LT |
88 | sysfs_put(sd); |
89 | } | |
90 | iput(inode); | |
91 | } | |
92 | ||
93 | static struct dentry_operations sysfs_dentry_ops = { | |
94 | .d_iput = sysfs_d_iput, | |
95 | }; | |
96 | ||
a26cd722 | 97 | struct sysfs_dirent *sysfs_new_dirent(void *element, umode_t mode, int type) |
1da177e4 LT |
98 | { |
99 | struct sysfs_dirent * sd; | |
100 | ||
c3762229 | 101 | sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); |
1da177e4 LT |
102 | if (!sd) |
103 | return NULL; | |
104 | ||
2b611bb7 TH |
105 | if (sysfs_alloc_ino(&sd->s_ino)) { |
106 | kmem_cache_free(sysfs_dir_cachep, sd); | |
107 | return NULL; | |
108 | } | |
109 | ||
1da177e4 | 110 | atomic_set(&sd->s_count, 1); |