Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+75
+74
drivers/gpu/drm/drm_stub.c
··· 31 31 * DEALINGS IN THE SOFTWARE. 32 32 */ 33 33 34 + #include <linux/fs.h> 34 35 #include <linux/module.h> 35 36 #include <linux/moduleparam.h> 37 + #include <linux/mount.h> 36 38 #include <linux/slab.h> 37 39 #include <drm/drmP.h> 38 40 #include <drm/drm_core.h> ··· 417 415 mutex_unlock(&drm_global_mutex); 418 416 } 419 417 EXPORT_SYMBOL(drm_unplug_dev); 418 + 419 + /* 420 + * DRM internal mount 421 + * We want to be able to allocate our own "struct address_space" to control 422 + * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow 423 + * stand-alone address_space objects, so we need an underlying inode. As there 424 + * is no way to allocate an independent inode easily, we need a fake internal 425 + * VFS mount-point. 426 + * 427 + * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free() 428 + * frees it again. You are allowed to use iget() and iput() to get references to 429 + * the inode. But each drm_fs_inode_new() call must be paired with exactly one 430 + * drm_fs_inode_free() call (which does not have to be the last iput()). 431 + * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it 432 + * between multiple inode-users. You could, technically, call 433 + * iget() + drm_fs_inode_free() directly after alloc and sometime later do an 434 + * iput(), but this way you'd end up with a new vfsmount for each inode. 435 + */ 436 + 437 + static int drm_fs_cnt; 438 + static struct vfsmount *drm_fs_mnt; 439 + 440 + static const struct dentry_operations drm_fs_dops = { 441 + .d_dname = simple_dname, 442 + }; 443 + 444 + static const struct super_operations drm_fs_sops = { 445 + .statfs = simple_statfs, 446 + }; 447 + 448 + static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags, 449 + const char *dev_name, void *data) 450 + { 451 + return mount_pseudo(fs_type, 452 + "drm:", 453 + &drm_fs_sops, 454 + &drm_fs_dops, 455 + 0x010203ff); 456 + } 457 + 458 + static struct file_system_type drm_fs_type = { 459 + .name = "drm", 460 + .owner = THIS_MODULE, 461 + .mount = drm_fs_mount, 462 + .kill_sb = kill_anon_super, 463 + }; 464 + 465 + static struct inode *drm_fs_inode_new(void) 466 + { 467 + struct inode *inode; 468 + int r; 469 + 470 + r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt); 471 + if (r < 0) { 472 + DRM_ERROR("Cannot mount pseudo fs: %d\n", r); 473 + return ERR_PTR(r); 474 + } 475 + 476 + inode = alloc_anon_inode(drm_fs_mnt->mnt_sb); 477 + if (IS_ERR(inode)) 478 + simple_release_fs(&drm_fs_mnt, &drm_fs_cnt); 479 + 480 + return inode; 481 + } 482 + 483 + static void drm_fs_inode_free(struct inode *inode) 484 + { 485 + if (inode) { 486 + iput(inode); 487 + simple_release_fs(&drm_fs_mnt, &drm_fs_cnt); 488 + } 489 + } 420 490 421 491 /** 422 492 * drm_dev_alloc - Allocate new drm device
+1
fs/dcache.c
··· 3112 3112 end = ERR_PTR(-ENAMETOOLONG); 3113 3113 return end; 3114 3114 } 3115 + EXPORT_SYMBOL(simple_dname); 3115 3116 3116 3117 /* 3117 3118 * Write full pathname from the root of the filesystem into the buffer.