~ubuntu-branches/ubuntu/maverick/linux-fsl-imx51/maverick

« back to all changes in this revision

Viewing changes to drivers/base/devtmpfs.c

  • Committer: Bazaar Package Importer
  • Author(s): Andy Whitcroft, Bryan Wu, Heiko Carstens, Kay Sievers, Upstream Kernel Changes
  • Date: 2010-01-27 10:30:45 UTC
  • Revision ID: james.westby@ubuntu.com-20100127103045-ocbuz94jsh104pgx
Tags: 2.6.31-603.5
[ Bryan Wu ]

* [Config] Enable devtmpfs for fsl-imx51 kernel
  - LP: #512321
* SAUCE: IMX51: only export NEON flag to userspace on Freescale iMX51
  rev3.x or later silicon
  - LP: #507416

[ Heiko Carstens ]

* (pre-stable) driver-core: fix devtmpfs crash on s390
  - LP: #512370

[ Kay Sievers ]

* (pre-stable) Driver-Core: devtmpfs - set root directory mode to 0755
  - LP: #512370

[ Upstream Kernel Changes ]

* ENGR00119069 V4L2 capture: Do not change current crop setting for tvin.
* ENGR00118830 Update NAND driver scan scheme to support new nand type
* ENGR00118946 imx23: enable wake up from USB 5V
* ENGR00119150 Bluetooth: Handsfree audio has noise
* ENGR00118610 MX23: Add double buffering for PXP
* ENGR00118872 Write bootstream to kernel by kobs with BCH ECC
* ENGR00119199: ipu: add clock nodes for pixel clocks
* ENGR00119202: Fix DVFS-PER related bugs.
* ENGR00119203 MX23: Correct VDDD value for CPU frequency 360 MHz
* ENGR00119004 v4l2 output: fix kernel dump
* ENGR00119242 Add wait timeout support to void dead loop in NAND driver
* ENGR00119263: MX51 : Fix pll_set_rate function
* ENGR00119075 fix iMX23 USB initialization cause wrong power status
* ENGR00118892 MX23: iMX233 disable lcd clock when LCD off
* ENGR00119063 MX23: fix system halt rather than reboot when watchdog
  timeout
* ENGR00119324 Put GPMI NAND flash scan scheme code to the common NAND
  directory
* ENGR00114151 800x600-16@60 video not play correctly
* ENGR00119275 ipuv3: dmfc size control
* ENGR00119443 [MX23_BSP] GPMI driver computes wrong block size for
  K9GAG08U0D
* ENGR00119136 ipuv3: support 720p for ipu lib
* ENGR00119274 TVE: HDTV can not work
* ENGR00119104 MX23 ALSA: Resolve the problem of record from line-in
* ENGR00119081 V4l2 capture:Support NV12 output pixel format for still
  capture
* ENGR00119179 TVE: fix system hang for tvout
* ENGR00119070 IPUv3 FB:Support DP local alpha in pixel
* ENGR00119432 MX35 system can't re-boot up when SD/MMC boot is used.
* ENGR00119296 Fix iMX23 display abnormal when change frequency
* ENGR00119484 Update NFC INT wait timeout value to 1s
* ENGR00119504 MX25: Change nand partition for bootloader to 3M byte
* ENGR00119532 MX35: Change nand partition for bootloader to 3M bytes
* ENGR00119305 imx23: fix ethernet standby issue
* ENGR00115370 ipuv3: add display control
* ENGR00119531 ipuv3: dmfc setting restore during resume
* Driver Core: devtmpfs - kernel-maintained tmpfs-based /dev
  - LP: #512321

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * devtmpfs - kernel-maintained tmpfs-based /dev
 
3
 *
 
4
 * Copyright (C) 2009, Kay Sievers <kay.sievers@vrfy.org>
 
5
 *
 
6
 * During bootup, before any driver core device is registered,
 
7
 * devtmpfs, a tmpfs-based filesystem is created. Every driver-core
 
8
 * device which requests a device node, will add a node in this
 
9
 * filesystem. The node is named after the the name of the device,
 
10
 * or the susbsytem can provide a custom name. All devices are
 
11
 * owned by root and have a mode of 0600.
 
12
 */
 
13
 
 
14
#include <linux/kernel.h>
 
15
#include <linux/syscalls.h>
 
16
#include <linux/mount.h>
 
17
#include <linux/device.h>
 
18
#include <linux/genhd.h>
 
19
#include <linux/namei.h>
 
20
#include <linux/fs.h>
 
21
#include <linux/shmem_fs.h>
 
22
#include <linux/cred.h>
 
23
#include <linux/init_task.h>
 
24
 
 
25
static struct vfsmount *dev_mnt;
 
26
 
 
27
#if defined CONFIG_DEVTMPFS_MOUNT
 
28
static int dev_mount = 1;
 
29
#else
 
30
static int dev_mount;
 
31
#endif
 
32
 
 
33
static int __init mount_param(char *str)
 
34
{
 
35
        dev_mount = simple_strtoul(str, NULL, 0);
 
36
        return 1;
 
37
}
 
38
__setup("devtmpfs.mount=", mount_param);
 
39
 
 
40
static int dev_get_sb(struct file_system_type *fs_type, int flags,
 
41
                      const char *dev_name, void *data, struct vfsmount *mnt)
 
42
{
 
43
        return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt);
 
44
}
 
45
 
 
46
static struct file_system_type dev_fs_type = {
 
47
        .name = "devtmpfs",
 
48
        .get_sb = dev_get_sb,
 
49
        .kill_sb = kill_litter_super,
 
50
};
 
51
 
 
52
#ifdef CONFIG_BLOCK
 
53
static inline int is_blockdev(struct device *dev)
 
54
{
 
55
        return dev->class == &block_class;
 
56
}
 
57
#else
 
58
static inline int is_blockdev(struct device *dev) { return 0; }
 
59
#endif
 
60
 
 
61
static int dev_mkdir(const char *name, mode_t mode)
 
62
{
 
63
        struct nameidata nd;
 
64
        struct dentry *dentry;
 
65
        int err;
 
66
 
 
67
        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
68
                              name, LOOKUP_PARENT, &nd);
 
69
        if (err)
 
70
                return err;
 
71
 
 
72
        dentry = lookup_create(&nd, 1);
 
73
        if (!IS_ERR(dentry)) {
 
74
                err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
 
75
                dput(dentry);
 
76
        } else {
 
77
                err = PTR_ERR(dentry);
 
78
        }
 
79
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 
80
 
 
81
        path_put(&nd.path);
 
82
        return err;
 
83
}
 
84
 
 
85
static int create_path(const char *nodepath)
 
86
{
 
87
        char *path;
 
88
        struct nameidata nd;
 
89
        int err = 0;
 
90
 
 
91
        path = kstrdup(nodepath, GFP_KERNEL);
 
92
        if (!path)
 
93
                return -ENOMEM;
 
94
 
 
95
        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
96
                              path, LOOKUP_PARENT, &nd);
 
97
        if (err == 0) {
 
98
                struct dentry *dentry;
 
99
 
 
100
                /* create directory right away */
 
101
                dentry = lookup_create(&nd, 1);
 
102
                if (!IS_ERR(dentry)) {
 
103
                        err = vfs_mkdir(nd.path.dentry->d_inode,
 
104
                                        dentry, 0755);
 
105
                        dput(dentry);
 
106
                }
 
107
                mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 
108
 
 
109
                path_put(&nd.path);
 
110
        } else if (err == -ENOENT) {
 
111
                char *s;
 
112
 
 
113
                /* parent directories do not exist, create them */
 
114
                s = path;
 
115
                while (1) {
 
116
                        s = strchr(s, '/');
 
117
                        if (!s)
 
118
                                break;
 
119
                        s[0] = '\0';
 
120
                        err = dev_mkdir(path, 0755);
 
121
                        if (err && err != -EEXIST)
 
122
                                break;
 
123
                        s[0] = '/';
 
124
                        s++;
 
125
                }
 
126
        }
 
127
 
 
128
        kfree(path);
 
129
        return err;
 
130
}
 
131
 
 
132
int devtmpfs_create_node(struct device *dev)
 
133
{
 
134
        const char *tmp = NULL;
 
135
        const char *nodename;
 
136
        const struct cred *curr_cred;
 
137
        mode_t mode;
 
138
        struct nameidata nd;
 
139
        struct dentry *dentry;
 
140
        int err;
 
141
 
 
142
        if (!dev_mnt)
 
143
                return 0;
 
144
 
 
145
        nodename = device_get_nodename(dev, &tmp);
 
146
        if (!nodename)
 
147
                return -ENOMEM;
 
148
 
 
149
        if (is_blockdev(dev))
 
150
                mode = S_IFBLK|0600;
 
151
        else
 
152
                mode = S_IFCHR|0600;
 
153
 
 
154
        curr_cred = override_creds(&init_cred);
 
155
        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
156
                              nodename, LOOKUP_PARENT, &nd);
 
157
        if (err == -ENOENT) {
 
158
                /* create missing parent directories */
 
159
                create_path(nodename);
 
160
                err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
161
                                      nodename, LOOKUP_PARENT, &nd);
 
162
                if (err)
 
163
                        goto out;
 
164
        }
 
165
 
 
166
        dentry = lookup_create(&nd, 0);
 
167
        if (!IS_ERR(dentry)) {
 
168
                err = vfs_mknod(nd.path.dentry->d_inode,
 
169
                                dentry, mode, dev->devt);
 
170
                /* mark as kernel created inode */
 
171
                if (!err)
 
172
                        dentry->d_inode->i_private = &dev_mnt;
 
173
                dput(dentry);
 
174
        } else {
 
175
                err = PTR_ERR(dentry);
 
176
        }
 
177
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 
178
 
 
179
        path_put(&nd.path);
 
180
out:
 
181
        kfree(tmp);
 
182
        revert_creds(curr_cred);
 
183
        return err;
 
184
}
 
185
 
 
186
static int dev_rmdir(const char *name)
 
187
{
 
188
        struct nameidata nd;
 
189
        struct dentry *dentry;
 
190
        int err;
 
191
 
 
192
        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
193
                              name, LOOKUP_PARENT, &nd);
 
194
        if (err)
 
195
                return err;
 
196
 
 
197
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 
198
        dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
 
199
        if (!IS_ERR(dentry)) {
 
200
                if (dentry->d_inode)
 
201
                        err = vfs_rmdir(nd.path.dentry->d_inode, dentry);
 
202
                else
 
203
                        err = -ENOENT;
 
204
                dput(dentry);
 
205
        } else {
 
206
                err = PTR_ERR(dentry);
 
207
        }
 
208
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 
209
 
 
210
        path_put(&nd.path);
 
211
        return err;
 
212
}
 
213
 
 
214
static int delete_path(const char *nodepath)
 
215
{
 
216
        const char *path;
 
217
        int err = 0;
 
218
 
 
219
        path = kstrdup(nodepath, GFP_KERNEL);
 
220
        if (!path)
 
221
                return -ENOMEM;
 
222
 
 
223
        while (1) {
 
224
                char *base;
 
225
 
 
226
                base = strrchr(path, '/');
 
227
                if (!base)
 
228
                        break;
 
229
                base[0] = '\0';
 
230
                err = dev_rmdir(path);
 
231
                if (err)
 
232
                        break;
 
233
        }
 
234
 
 
235
        kfree(path);
 
236
        return err;
 
237
}
 
238
 
 
239
static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
 
240
{
 
241
        /* did we create it */
 
242
        if (inode->i_private != &dev_mnt)
 
243
                return 0;
 
244
 
 
245
        /* does the dev_t match */
 
246
        if (is_blockdev(dev)) {
 
247
                if (!S_ISBLK(stat->mode))
 
248
                        return 0;
 
249
        } else {
 
250
                if (!S_ISCHR(stat->mode))
 
251
                        return 0;
 
252
        }
 
253
        if (stat->rdev != dev->devt)
 
254
                return 0;
 
255
 
 
256
        /* ours */
 
257
        return 1;
 
258
}
 
259
 
 
260
int devtmpfs_delete_node(struct device *dev)
 
261
{
 
262
        const char *tmp = NULL;
 
263
        const char *nodename;
 
264
        const struct cred *curr_cred;
 
265
        struct nameidata nd;
 
266
        struct dentry *dentry;
 
267
        struct kstat stat;
 
268
        int deleted = 1;
 
269
        int err;
 
270
 
 
271
        if (!dev_mnt)
 
272
                return 0;
 
273
 
 
274
        nodename = device_get_nodename(dev, &tmp);
 
275
        if (!nodename)
 
276
                return -ENOMEM;
 
277
 
 
278
        curr_cred = override_creds(&init_cred);
 
279
        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
 
280
                              nodename, LOOKUP_PARENT, &nd);
 
281
        if (err)
 
282
                goto out;
 
283
 
 
284
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 
285
        dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
 
286
        if (!IS_ERR(dentry)) {
 
287
                if (dentry->d_inode) {
 
288
                        err = vfs_getattr(nd.path.mnt, dentry, &stat);
 
289
                        if (!err && dev_mynode(dev, dentry->d_inode, &stat)) {
 
290
                                err = vfs_unlink(nd.path.dentry->d_inode,
 
291
                                                 dentry);
 
292
                                if (!err || err == -ENOENT)
 
293
                                        deleted = 1;
 
294
                        }
 
295
                } else {
 
296
                        err = -ENOENT;
 
297
                }
 
298
                dput(dentry);
 
299
        } else {
 
300
                err = PTR_ERR(dentry);
 
301
        }
 
302
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 
303
 
 
304
        path_put(&nd.path);
 
305
        if (deleted && strchr(nodename, '/'))
 
306
                delete_path(nodename);
 
307
out:
 
308
        kfree(tmp);
 
309
        revert_creds(curr_cred);
 
310
        return err;
 
311
}
 
312
 
 
313
/*
 
314
 * If configured, or requested by the commandline, devtmpfs will be
 
315
 * auto-mounted after the kernel mounted the root filesystem.
 
316
 */
 
317
int devtmpfs_mount(const char *mountpoint)
 
318
{
 
319
        struct path path;
 
320
        int err;
 
321
 
 
322
        if (!dev_mount)
 
323
                return 0;
 
324
 
 
325
        if (!dev_mnt)
 
326
                return 0;
 
327
 
 
328
        err = kern_path(mountpoint, LOOKUP_FOLLOW, &path);
 
329
        if (err)
 
330
                return err;
 
331
        err = do_add_mount(dev_mnt, &path, 0, NULL);
 
332
        if (err)
 
333
                printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
 
334
        else
 
335
                printk(KERN_INFO "devtmpfs: mounted\n");
 
336
        path_put(&path);
 
337
        return err;
 
338
}
 
339
 
 
340
/*
 
341
 * Create devtmpfs instance, driver-core devices will add their device
 
342
 * nodes here.
 
343
 */
 
344
int __init devtmpfs_init(void)
 
345
{
 
346
        int err;
 
347
        struct vfsmount *mnt;
 
348
        char options[] = "mode=0755";
 
349
 
 
350
        err = register_filesystem(&dev_fs_type);
 
351
        if (err) {
 
352
                printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
 
353
                       "type %i\n", err);
 
354
                return err;
 
355
        }
 
356
 
 
357
        mnt = kern_mount_data(&dev_fs_type, options);
 
358
        if (IS_ERR(mnt)) {
 
359
                err = PTR_ERR(mnt);
 
360
                printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
 
361
                unregister_filesystem(&dev_fs_type);
 
362
                return err;
 
363
        }
 
364
        dev_mnt = mnt;
 
365
 
 
366
        printk(KERN_INFO "devtmpfs: initialized\n");
 
367
        return 0;
 
368
}