diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2014-01-03 08:09:47 -0500 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2014-03-16 07:17:03 -0400 |
commit | 31bbe16f6d88622d6731fa2cb4ab38d57d844ac1 (patch) | |
tree | 2a123ae13d3e3a9de36f82304b74b8052545cb87 | |
parent | 786a7828bc74b9b1466e83abb200b75f80f94121 (diff) |
drm: add pseudo filesystem for shared inodes
Our current DRM design uses a single address_space for all users of the
same DRM device. However, there is no way to create an anonymous
address_space without an underlying inode. Therefore, we wait for the
first ->open() callback on a registered char-dev and take-over the inode
of the char-dev. This worked well so far, but has several drawbacks:
- We screw with FS internals and rely on some non-obvious invariants like
inode->i_mapping being the same as inode->i_data for char-devs.
- We don't have any address_space prior to the first ->open() from
user-space. This leads to ugly fallback code and we cannot allocate
global objects early.
As pointed out by Al-Viro, fs/anon_inode.c is *not* supposed to be used by
drivers for anonymous inode-allocation. Therefore, this patch follows the
proposed alternative solution and adds a pseudo filesystem mount-point to
DRM. We can then allocate private inodes including a private address_space
for each DRM device at initialization time.
Note that we could use:
sysfs_get_inode(sysfs_mnt->mnt_sb, drm_device->dev->kobj.sd);
to get access to the underlying sysfs-inode of a "struct device" object.
However, most of this information is currently hidden and it's not clear
whether this address_space is suitable for driver access. Thus, unless
linux allows anonymous address_space objects or driver-core provides a
public inode per device, we're left with our own private internal mount
point.
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r-- | drivers/gpu/drm/drm_stub.c | 74 | ||||
-rw-r--r-- | fs/dcache.c | 1 |
2 files changed, 75 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 98a33c580ca1..f2903d7e3d8e 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c | |||
@@ -31,8 +31,10 @@ | |||
31 | * DEALINGS IN THE SOFTWARE. | 31 | * DEALINGS IN THE SOFTWARE. |
32 | */ | 32 | */ |
33 | 33 | ||
34 | #include <linux/fs.h> | ||
34 | #include <linux/module.h> | 35 | #include <linux/module.h> |
35 | #include <linux/moduleparam.h> | 36 | #include <linux/moduleparam.h> |
37 | #include <linux/mount.h> | ||
36 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
37 | #include <drm/drmP.h> | 39 | #include <drm/drmP.h> |
38 | #include <drm/drm_core.h> | 40 | #include <drm/drm_core.h> |
@@ -416,6 +418,78 @@ void drm_unplug_dev(struct drm_device *dev) | |||
416 | } | 418 | } |
417 | EXPORT_SYMBOL(drm_unplug_dev); | 419 | EXPORT_SYMBOL(drm_unplug_dev); |
418 | 420 | ||
421 | /* | ||
422 | * DRM internal mount | ||
423 | * We want to be able to allocate our own "struct address_space" to control | ||
424 | * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow | ||
425 | * stand-alone address_space objects, so we need an underlying inode. As there | ||
426 | * is no way to allocate an independent inode easily, we need a fake internal | ||
427 | * VFS mount-point. | ||
428 | * | ||
429 | * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free() | ||
430 | * frees it again. You are allowed to use iget() and iput() to get references to | ||
431 | * the inode. But each drm_fs_inode_new() call must be paired with exactly one | ||
432 | * drm_fs_inode_free() call (which does not have to be the last iput()). | ||
433 | * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it | ||
434 | * between multiple inode-users. You could, technically, call | ||
435 | * iget() + drm_fs_inode_free() directly after alloc and sometime later do an | ||
436 | * iput(), but this way you'd end up with a new vfsmount for each inode. | ||
437 | */ | ||
438 | |||
439 | static int drm_fs_cnt; | ||
440 | static struct vfsmount *drm_fs_mnt; | ||
441 | |||
442 | static const struct dentry_operations drm_fs_dops = { | ||
443 | .d_dname = simple_dname, | ||
444 | }; | ||
445 | |||
446 | static const struct super_operations drm_fs_sops = { | ||
447 | .statfs = simple_statfs, | ||
448 | }; | ||
449 | |||
450 | static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags, | ||
451 | const char *dev_name, void *data) | ||
452 | { | ||
453 | return mount_pseudo(fs_type, | ||
454 | "drm:", | ||
455 | &drm_fs_sops, | ||
456 | &drm_fs_dops, | ||
457 | 0x010203ff); | ||
458 | } | ||
459 | |||
460 | static struct file_system_type drm_fs_type = { | ||
461 | .name = "drm", | ||
462 | .owner = THIS_MODULE, | ||
463 | .mount = drm_fs_mount, | ||
464 | .kill_sb = kill_anon_super, | ||
465 | }; | ||
466 | |||
467 | static struct inode *drm_fs_inode_new(void) | ||
468 | { | ||
469 | struct inode *inode; | ||
470 | int r; | ||
471 | |||
472 | r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt); | ||
473 | if (r < 0) { | ||
474 | DRM_ERROR("Cannot mount pseudo fs: %d\n", r); | ||
475 | return ERR_PTR(r); | ||
476 | } | ||
477 | |||
478 | inode = alloc_anon_inode(drm_fs_mnt->mnt_sb); | ||
479 | if (IS_ERR(inode)) | ||
480 | simple_release_fs(&drm_fs_mnt, &drm_fs_cnt); | ||
481 | |||
482 | return inode; | ||
483 | } | ||
484 | |||
485 | static void drm_fs_inode_free(struct inode *inode) | ||
486 | { | ||
487 | if (inode) { | ||
488 | iput(inode); | ||
489 | simple_release_fs(&drm_fs_mnt, &drm_fs_cnt); | ||
490 | } | ||
491 | } | ||
492 | |||
419 | /** | 493 | /** |
420 | * drm_dev_alloc - Allocate new drm device | 494 | * drm_dev_alloc - Allocate new drm device |
421 | * @driver: DRM driver to allocate device for | 495 | * @driver: DRM driver to allocate device for |
diff --git a/fs/dcache.c b/fs/dcache.c index 265e0ce9769c..66dc62cb766d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -3112,6 +3112,7 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen) | |||
3112 | end = ERR_PTR(-ENAMETOOLONG); | 3112 | end = ERR_PTR(-ENAMETOOLONG); |
3113 | return end; | 3113 | return end; |
3114 | } | 3114 | } |
3115 | EXPORT_SYMBOL(simple_dname); | ||
3115 | 3116 | ||
3116 | /* | 3117 | /* |
3117 | * Write full pathname from the root of the filesystem into the buffer. | 3118 | * Write full pathname from the root of the filesystem into the buffer. |