Blob Blame History Raw
From: Jeff Mahoney <jeffm@suse.com>
Subject: vfs: add super_operations->get_inode_dev
References: bsc#927455
Patch-mainline: Never, Upstream submissions have been met with a request to use per-subvolume superblocks, which is unworkable in practice

There are many places where a dev_t:ino_t pair are passed to userspace
to uniquely describe an inode.  Some file systems, like btrfs, have
multiple inode namespace internally and use a separate dev_t to make the
distinction between them.

This patch adds a super_operations->get_inode_dev operation to allow
the file system to export those dev_ts to callers within the kernel
for further export to userspace.

Without this patch, things like audit and some perf and trace events
will not distinguish between subvolumes within a single btrfs filesystem.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 arch/arc/kernel/troubleshoot.c             | 2 +-
 drivers/staging/lustre/lustre/llite/dir.c  | 2 +-
 drivers/staging/lustre/lustre/llite/file.c | 2 +-
 fs/locks.c                                 | 4 ++--
 fs/nfsd/nfs3xdr.c                          | 2 +-
 fs/nfsd/vfs.c                              | 6 +++---
 fs/notify/fdinfo.c                         | 4 ++--
 fs/proc/nommu.c                            | 2 +-
 fs/proc/task_mmu.c                         | 2 +-
 fs/proc/task_nommu.c                       | 2 +-
 fs/stat.c                                  | 2 +-
 include/linux/fs.h                         | 9 +++++++++
 include/trace/events/filelock.h            | 8 ++++----
 include/trace/events/filemap.h             | 2 +-
 include/trace/events/writeback.h           | 2 +-
 kernel/audit.c                             | 2 +-
 kernel/audit_fsnotify.c                    | 2 +-
 kernel/audit_watch.c                       | 6 +++---
 kernel/auditsc.c                           | 4 ++--
 kernel/events/core.c                       | 2 +-
 mm/memory-failure.c                        | 2 +-
 net/unix/diag.c                            | 2 +-
 security/tomoyo/condition.c                | 2 +-
 23 files changed, 41 insertions(+), 32 deletions(-)

diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c
index f9caf79186d4..d2cdc6dd7205 100644
--- a/arch/arc/kernel/troubleshoot.c
+++ b/arch/arc/kernel/troubleshoot.c
@@ -104,7 +104,7 @@ static void show_faulting_vma(unsigned long address, char *buf)
 		if (file) {
 			nm = file_path(file, buf, PAGE_SIZE - 1);
 			inode = file_inode(vma->vm_file);
-			dev = inode->i_sb->s_dev;
+			dev = inode_get_dev(inode);
 			ino = inode->i_ino;
 		}
 		pr_info("    @off 0x%lx in [%s]\n"
diff --git a/drivers/staging/lustre/lustre/llite/dir.c b/drivers/staging/lustre/lustre/llite/dir.c
index 13b35922a4ca..6a7f9e69855a 100644
--- a/drivers/staging/lustre/lustre/llite/dir.c
+++ b/drivers/staging/lustre/lustre/llite/dir.c
@@ -1362,7 +1362,7 @@ static long ll_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 			struct lov_user_mds_data __user *lmdp;
 			lstat_t st = { 0 };
 
-			st.st_dev     = inode->i_sb->s_dev;
+			st.st_dev     = inode_get_dev(inode);
 			st.st_mode    = body->mbo_mode;
 			st.st_nlink   = body->mbo_nlink;
 			st.st_uid     = body->mbo_uid;
diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c
index 67c4b9cc6e75..8c9fc8a75995 100644
--- a/drivers/staging/lustre/lustre/llite/file.c
+++ b/drivers/staging/lustre/lustre/llite/file.c
@@ -2964,7 +2964,7 @@ int ll_getattr(const struct path *path, struct kstat *stat,
 
 	OBD_FAIL_TIMEOUT(OBD_FAIL_GETATTR_DELAY, 30);
 
-	stat->dev = inode->i_sb->s_dev;
+	stat->dev = inode_get_dev(inode);
 	if (ll_need_32bit_api(sbi))
 		stat->ino = cl_fid_build_ino(&lli->lli_fid, 1);
 	else
diff --git a/fs/locks.c b/fs/locks.c
index af2031a1fcff..e71c2fdf80df 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -2682,8 +2682,8 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
 	if (inode) {
 		/* userspace relies on this representation of dev_t */
 		seq_printf(f, "%d %02x:%02x:%ld ", fl_pid,
-				MAJOR(inode->i_sb->s_dev),
-				MINOR(inode->i_sb->s_dev), inode->i_ino);
+				MAJOR(inode_get_dev(inode)),
+				MINOR(inode_get_dev(inode)), inode->i_ino);
 	} else {
 		seq_printf(f, "%d <none>:0 ", fl_pid);
 	}
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 12feac6ee2fd..5f59b0bb6984 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -146,7 +146,7 @@ static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp)
 	default:
 	case FSIDSOURCE_DEV:
 		p = xdr_encode_hyper(p, (u64)huge_encode_dev
-				     (fhp->fh_dentry->d_sb->s_dev));
+				     (inode_get_dev(d_inode(fhp->fh_dentry))));
 		break;
 	case FSIDSOURCE_FSID:
 		p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 2be32955d7f2..c3c8b4573edb 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -785,7 +785,7 @@ struct raparms *
 nfsd_init_raparms(struct file *file)
 {
 	struct inode *inode = file_inode(file);
-	dev_t dev = inode->i_sb->s_dev;
+	dev_t dev = inode_get_dev(inode);
 	ino_t ino = inode->i_ino;
 	struct raparms	*ra, **rap, **frap = NULL;
 	int depth = 0;
@@ -953,7 +953,7 @@ static int wait_for_concurrent_writes(struct file *file)
 	int err = 0;
 
 	if (atomic_read(&inode->i_writecount) > 1
-	    || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) {
+	    || (last_ino == inode->i_ino && last_dev == inode_get_dev(inode))) {
 		dprintk("nfsd: write defer %d\n", task_pid_nr(current));
 		msleep(10);
 		dprintk("nfsd: write resume %d\n", task_pid_nr(current));
@@ -964,7 +964,7 @@ static int wait_for_concurrent_writes(struct file *file)
 		err = vfs_fsync(file, 0);
 	}
 	last_ino = inode->i_ino;
-	last_dev = inode->i_sb->s_dev;
+	last_dev = inode_get_dev(inode);
 	return err;
 }
 
diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c
index dd63aa9a6f9a..82737e4ae0ae 100644
--- a/fs/notify/fdinfo.c
+++ b/fs/notify/fdinfo.c
@@ -90,7 +90,7 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
 		 */
 		u32 mask = mark->mask & IN_ALL_EVENTS;
 		seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:%x ",
-			   inode_mark->wd, inode->i_ino, inode->i_sb->s_dev,
+			   inode_mark->wd, inode->i_ino, inode_get_dev(inode),
 			   mask, mark->ignored_mask);
 		show_mark_fhandle(m, inode);
 		seq_putc(m, '\n');
@@ -120,7 +120,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
 		if (!inode)
 			return;
 		seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
-			   inode->i_ino, inode->i_sb->s_dev,
+			   inode->i_ino, inode_get_dev(inode),
 			   mflags, mark->mask, mark->ignored_mask);
 		show_mark_fhandle(m, inode);
 		seq_putc(m, '\n');
diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c
index 75634379f82e..8ec396f8b78a 100644
--- a/fs/proc/nommu.c
+++ b/fs/proc/nommu.c
@@ -46,7 +46,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
 
 	if (file) {
 		struct inode *inode = file_inode(region->vm_file);
-		dev = inode->i_sb->s_dev;
+		dev = inode_get_dev(inode);
 		ino = inode->i_ino;
 	}
 
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index f0c8b33d99b1..a0c818ea1879 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -293,7 +293,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
 
 	if (file) {
 		struct inode *inode = file_inode(vma->vm_file);
-		dev = inode->i_sb->s_dev;
+		dev = inode_get_dev(inode);
 		ino = inode->i_ino;
 		pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
 	}
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 23266694db11..df61aa8938a3 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -158,7 +158,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
 
 	if (file) {
 		struct inode *inode = file_inode(vma->vm_file);
-		dev = inode->i_sb->s_dev;
+		dev = inode_get_dev(inode);
 		ino = inode->i_ino;
 		pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
 	}
diff --git a/fs/stat.c b/fs/stat.c
index f494b182c7c7..01955747772f 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -31,7 +31,7 @@
  */
 void generic_fillattr(struct inode *inode, struct kstat *stat)
 {
-	stat->dev = inode->i_sb->s_dev;
+	stat->dev = inode_get_dev(inode);
 	stat->ino = inode->i_ino;
 	stat->mode = inode->i_mode;
 	stat->nlink = inode->i_nlink;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 803e5a9b2654..d0c2cc74d323 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1803,6 +1803,7 @@ struct super_operations {
 				  struct shrink_control *);
 	long (*free_cached_objects)(struct super_block *,
 				    struct shrink_control *);
+	dev_t (*get_inode_dev)(const struct inode *);
 };
 
 /*
@@ -3224,4 +3225,12 @@ static inline bool dir_relax_shared(struct inode *inode)
 extern bool path_noexec(const struct path *path);
 extern void inode_nohighmem(struct inode *inode);
 
+static inline dev_t inode_get_dev(const struct inode *inode)
+{
+	if (inode->i_sb->s_op->get_inode_dev)
+		return inode->i_sb->s_op->get_inode_dev(inode);
+
+	return inode->i_sb->s_dev;
+}
+
 #endif /* _LINUX_FS_H */
diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h
index 63a7680347cb..87412ff5a08c 100644
--- a/include/trace/events/filelock.h
+++ b/include/trace/events/filelock.h
@@ -47,7 +47,7 @@ TRACE_EVENT(locks_get_lock_context,
 	),
 
 	TP_fast_assign(
-		__entry->s_dev = inode->i_sb->s_dev;
+		__entry->s_dev = inode_get_dev(inode);
 		__entry->i_ino = inode->i_ino;
 		__entry->type = type;
 		__entry->ctx = ctx;
@@ -79,7 +79,7 @@ DECLARE_EVENT_CLASS(filelock_lock,
 
 	TP_fast_assign(
 		__entry->fl = fl ? fl : NULL;
-		__entry->s_dev = inode->i_sb->s_dev;
+		__entry->s_dev = inode_get_dev(inode);
 		__entry->i_ino = inode->i_ino;
 		__entry->fl_next = fl ? fl->fl_next : NULL;
 		__entry->fl_owner = fl ? fl->fl_owner : NULL;
@@ -131,7 +131,7 @@ DECLARE_EVENT_CLASS(filelock_lease,
 
 	TP_fast_assign(
 		__entry->fl = fl ? fl : NULL;
-		__entry->s_dev = inode->i_sb->s_dev;
+		__entry->s_dev = inode_get_dev(inode);
 		__entry->i_ino = inode->i_ino;
 		__entry->fl_next = fl ? fl->fl_next : NULL;
 		__entry->fl_owner = fl ? fl->fl_owner : NULL;
@@ -181,7 +181,7 @@ TRACE_EVENT(generic_add_lease,
 	),
 
 	TP_fast_assign(
-		__entry->s_dev = inode->i_sb->s_dev;
+		__entry->s_dev = inode_get_dev(inode);
 		__entry->i_ino = inode->i_ino;
 		__entry->wcount = atomic_read(&inode->i_writecount);
 		__entry->dcount = d_count(fl->fl_file->f_path.dentry);
diff --git a/include/trace/events/filemap.h b/include/trace/events/filemap.h
index 42febb6bc1d5..a839a70faa28 100644
--- a/include/trace/events/filemap.h
+++ b/include/trace/events/filemap.h
@@ -29,7 +29,7 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,
 		__entry->i_ino = page->mapping->host->i_ino;
 		__entry->index = page->index;
 		if (page->mapping->host->i_sb)
-			__entry->s_dev = page->mapping->host->i_sb->s_dev;
+			__entry->s_dev = inode_get_dev(page->mapping->host);
 		else
 			__entry->s_dev = page->mapping->host->i_rdev;
 	),
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 7bd8783a590f..75520d963340 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -710,7 +710,7 @@ DECLARE_EVENT_CLASS(writeback_inode_template,
 	),
 
 	TP_fast_assign(
-		__entry->dev	= inode->i_sb->s_dev;
+		__entry->dev	= inode_get_dev(inode);
 		__entry->ino	= inode->i_ino;
 		__entry->state	= inode->i_state;
 		__entry->mode	= inode->i_mode;
diff --git a/kernel/audit.c b/kernel/audit.c
index 4b7d49868ce1..c9987f7c1987 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -2044,7 +2044,7 @@ void audit_copy_inode(struct audit_names *name, const struct dentry *dentry,
 		      struct inode *inode)
 {
 	name->ino   = inode->i_ino;
-	name->dev   = inode->i_sb->s_dev;
+	name->dev   = inode_get_dev(inode);
 	name->mode  = inode->i_mode;
 	name->uid   = inode->i_uid;
 	name->gid   = inode->i_gid;
diff --git a/kernel/audit_fsnotify.c b/kernel/audit_fsnotify.c
index 52f368b6561e..4d4d4eea102e 100644
--- a/kernel/audit_fsnotify.c
+++ b/kernel/audit_fsnotify.c
@@ -76,7 +76,7 @@ int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_
 static void audit_update_mark(struct audit_fsnotify_mark *audit_mark,
 			     const struct inode *inode)
 {
-	audit_mark->dev = inode ? inode->i_sb->s_dev : AUDIT_DEV_UNSET;
+	audit_mark->dev = inode ? inode_get_dev(inode) : AUDIT_DEV_UNSET;
 	audit_mark->ino = inode ? inode->i_ino : AUDIT_INO_UNSET;
 }
 
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 62d686d96581..7332b57709ba 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -368,7 +368,7 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
 	inode_unlock(d_backing_inode(parent->dentry));
 	if (d_is_positive(d)) {
 		/* update watch filter fields */
-		watch->dev = d->d_sb->s_dev;
+		watch->dev = inode_get_dev(d_backing_inode(d));
 		watch->ino = d_backing_inode(d)->i_ino;
 	}
 	dput(d);
@@ -497,7 +497,7 @@ static int audit_watch_handle_event(struct fsnotify_group *group,
 	}
 
 	if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
-		audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
+		audit_update_watch(parent, dname, inode_get_dev(inode), inode->i_ino, 0);
 	else if (mask & (FS_DELETE|FS_MOVED_FROM))
 		audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1);
 	else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
@@ -551,7 +551,7 @@ int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark)
 	if (!exe_file)
 		return 0;
 	ino = file_inode(exe_file)->i_ino;
-	dev = file_inode(exe_file)->i_sb->s_dev;
+	dev = inode_get_dev(file_inode(exe_file));
 	fput(exe_file);
 	return audit_mark_compare(mark, ino, dev);
 }
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index bb724baa7ac9..52462c9432c2 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1794,7 +1794,7 @@ void __audit_inode(struct filename *name, const struct dentry *dentry,
 		if (n->ino) {
 			/* valid inode number, use that for the comparison */
 			if (n->ino != inode->i_ino ||
-			    n->dev != inode->i_sb->s_dev)
+			    n->dev != inode_get_dev(inode))
 				continue;
 		} else if (n->name) {
 			/* inode number has not been set, check the name */
@@ -1880,7 +1880,7 @@ void __audit_inode_child(struct inode *parent,
 		     n->type != AUDIT_TYPE_UNKNOWN))
 			continue;
 
-		if (n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev &&
+		if (n->ino == parent->i_ino && n->dev == inode_get_dev(parent) &&
 		    !audit_compare_dname_path(dname,
 					      n->name->name, n->name_len)) {
 			if (n->type == AUDIT_TYPE_UNKNOWN)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 6e75a5c9412d..ab870aadd6ec 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6865,7 +6865,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
 			goto cpy_name;
 		}
 		inode = file_inode(vma->vm_file);
-		dev = inode->i_sb->s_dev;
+		dev = inode_get_dev(inode);
 		ino = inode->i_ino;
 		gen = inode->i_generation;
 		maj = MAJOR(dev);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 2527dfeddb00..62a338c4e641 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -99,7 +99,7 @@ static int hwpoison_filter_dev(struct page *p)
 	if (mapping == NULL || mapping->host == NULL)
 		return -EINVAL;
 
-	dev = mapping->host->i_sb->s_dev;
+	dev = inode_get_dev(mapping->host);
 	if (hwpoison_filter_dev_major != ~0U &&
 	    hwpoison_filter_dev_major != MAJOR(dev))
 		return -EINVAL;
diff --git a/net/unix/diag.c b/net/unix/diag.c
index 4d9679701a6d..ff37599922c7 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -26,7 +26,7 @@ static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
 	if (dentry) {
 		struct unix_diag_vfs uv = {
 			.udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
-			.udiag_vfs_dev = dentry->d_sb->s_dev,
+			.udiag_vfs_dev = inode_get_dev(d_backing_inode(dentry)),
 		};
 
 		return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c
index 6c4528d4b48f..9f16cb5529ad 100644
--- a/security/tomoyo/condition.c
+++ b/security/tomoyo/condition.c
@@ -721,7 +721,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
 			stat->gid  = inode->i_gid;
 			stat->ino  = inode->i_ino;
 			stat->mode = inode->i_mode;
-			stat->dev  = inode->i_sb->s_dev;
+			stat->dev  = inode_get_dev(inode);
 			stat->rdev = inode->i_rdev;
 			obj->stat_valid[i] = true;
 		}
-- 
2.13.0