[v5,10/17] fanotify: cache fsid in fsnotify_mark_connector
diff mbox series

Message ID 20190110170444.30616-11-amir73il@gmail.com
State New
Headers show
Series
  • fanotify: add support for more event types
Related show

Commit Message

Amir Goldstein Jan. 10, 2019, 5:04 p.m. UTC
For FAN_REPORT_FID, we need to encode fid with fsid of the filesystem on
every event. To avoid having to call vfs_statfs() on every event to get
fsid, we store the fsid in fsnotify_mark_connector on the first time we
add a mark and on handle event we use the cached fsid.

Subsequent calls to add mark on the same object are expected to pass the
same fsid, so the call will fail on cached fsid mismatch.

If an event is reported on several mark types (inode, mount, filesystem),
all connectors should already have the same fsid, so we use the cached
fsid from the first connector.

Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/notify/fanotify/fanotify.c      | 51 +++++++++++++++++-------
 fs/notify/fanotify/fanotify.h      |  5 ++-
 fs/notify/fanotify/fanotify_user.c | 62 ++++++++++++++++++------------
 fs/notify/mark.c                   | 47 +++++++++++++++++-----
 include/linux/fsnotify_backend.h   | 24 +++++++++---
 5 files changed, 135 insertions(+), 54 deletions(-)

Comments

kernel test robot Jan. 11, 2019, 3:13 a.m. UTC | #1
Hi Amir,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.0-rc1 next-20190110]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Amir-Goldstein/fanotify-add-support-for-more-event-types/20190111-090241
config: mips-loongson1c_defconfig (attached as .config)
compiler: mipsel-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.2.0 make.cross ARCH=mips 

All warnings (new ones prefixed by >>):

   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs/notify/mark.c:76:
   fs/notify/mark.c: In function 'fsnotify_add_mark_list':
>> include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
>> fs/notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs/notify/mark.c:587:65: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                   ~^
                                                                   %lx
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs/notify/mark.c:76:
   include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
>> fs/notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs/notify/mark.c:587:68: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                      ~^
                                                                      %lx
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs/notify/mark.c:76:
   include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 6 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
>> fs/notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs/notify/mark.c:587:74: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                            ~^
                                                                            %lx
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs/notify/mark.c:76:
   include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
>> fs/notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs/notify/mark.c:587:77: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                               ~^
                                                                               %lx
--
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs//notify/mark.c:76:
   fs//notify/mark.c: In function 'fsnotify_add_mark_list':
>> include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
   fs//notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs//notify/mark.c:587:65: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                   ~^
                                                                   %lx
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs//notify/mark.c:76:
   include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
   fs//notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs//notify/mark.c:587:68: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                      ~^
                                                                      %lx
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs//notify/mark.c:76:
   include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 6 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
   fs//notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs//notify/mark.c:587:74: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                            ~^
                                                                            %lx
   In file included from include/linux/kernel.h:14:0,
                    from include/linux/list.h:9,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs//notify/mark.c:76:
   include/linux/kern_levels.h:5:18: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'long int' [-Wformat=]
    #define KERN_SOH "\001"  /* ASCII Start Of Header */
                     ^
   include/linux/printk.h:424:10: note: in definition of macro 'printk_ratelimited'
      printk(fmt, ##__VA_ARGS__);    \
             ^~~
   include/linux/kern_levels.h:12:22: note: in expansion of macro 'KERN_SOH'
    #define KERN_WARNING KERN_SOH "4" /* warning conditions */
                         ^~~~~~~~
   include/linux/printk.h:440:21: note: in expansion of macro 'KERN_WARNING'
     printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
                        ^~~~~~~~~~~~
   fs//notify/mark.c:587:3: note: in expansion of macro 'pr_warn_ratelimited'
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
      ^~~~~~~~~~~~~~~~~~~
   fs//notify/mark.c:587:77: note: format string is defined here
      pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
                                                                               ~^
                                                                               %lx

vim +/pr_warn_ratelimited +587 fs/notify/mark.c

   545	
   546	/*
   547	 * Add mark into proper place in given list of marks. These marks may be used
   548	 * for the fsnotify backend to determine which event types should be delivered
   549	 * to which group and for which inodes. These marks are ordered according to
   550	 * priority, highest number first, and then by the group's location in memory.
   551	 */
   552	static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
   553					  fsnotify_connp_t *connp, unsigned int type,
   554					  int allow_dups, __kernel_fsid_t *fsid)
   555	{
   556		struct fsnotify_mark *lmark, *last = NULL;
   557		struct fsnotify_mark_connector *conn;
   558		int cmp;
   559		int err = 0;
   560	
   561		if (WARN_ON(!fsnotify_valid_obj_type(type)))
   562			return -EINVAL;
   563	
   564		/* Backend is expected to check for zero fsid (e.g. tmpfs) */
   565		if (fsid && WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1]))
   566			return -ENODEV;
   567	
   568	restart:
   569		spin_lock(&mark->lock);
   570		conn = fsnotify_grab_connector(connp);
   571		if (!conn) {
   572			spin_unlock(&mark->lock);
   573			err = fsnotify_attach_connector_to_object(connp, type, fsid);
   574			if (err)
   575				return err;
   576			goto restart;
   577		} else if (fsid && (conn->fsid.val[0] || conn->fsid.val[1]) &&
   578			   (fsid->val[0] != conn->fsid.val[0] ||
   579			    fsid->val[1] != conn->fsid.val[1])) {
   580			/*
   581			 * Backend is expected to check for non uniform fsid
   582			 * (e.g. btrfs), but maybe we missed something?
   583			 * Only allow setting conn->fsid once to non zero fsid.
   584			 * inotify and non-fid fanotify groups do not set nor test
   585			 * conn->fsid.
   586			 */
 > 587			pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
   588					    __func__, conn->type,
   589					    fsid->val[0], fsid->val[1],
   590					    conn->fsid.val[0], conn->fsid.val[1]);
   591			err = -EXDEV;
   592			goto out_err;
   593		}
   594	
   595		/* is mark the first mark? */
   596		if (hlist_empty(&conn->list)) {
   597			hlist_add_head_rcu(&mark->obj_list, &conn->list);
   598			goto added;
   599		}
   600	
   601		/* should mark be in the middle of the current list? */
   602		hlist_for_each_entry(lmark, &conn->list, obj_list) {
   603			last = lmark;
   604	
   605			if ((lmark->group == mark->group) &&
   606			    (lmark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) &&
   607			    !allow_dups) {
   608				err = -EEXIST;
   609				goto out_err;
   610			}
   611	
   612			cmp = fsnotify_compare_groups(lmark->group, mark->group);
   613			if (cmp >= 0) {
   614				hlist_add_before_rcu(&mark->obj_list, &lmark->obj_list);
   615				goto added;
   616			}
   617		}
   618	
   619		BUG_ON(last == NULL);
   620		/* mark should be the last entry.  last is the current last entry */
   621		hlist_add_behind_rcu(&mark->obj_list, &last->obj_list);
   622	added:
   623		mark->connector = conn;
   624	out_err:
   625		spin_unlock(&conn->lock);
   626		spin_unlock(&mark->lock);
   627		return err;
   628	}
   629	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Dan Carpenter Jan. 14, 2019, 7:30 a.m. UTC | #2
Hi Amir,

url:    https://github.com/0day-ci/linux/commits/Amir-Goldstein/fanotify-add-support-for-more-event-types/20190111-090241

smatch warnings:
fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error: uninitialized symbol 'type'.
fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error: uninitialized symbol 'err'.

# https://github.com/0day-ci/linux/commit/48b7a1af51abcb893917d986bc5ae5bae2d21ad6
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 48b7a1af51abcb893917d986bc5ae5bae2d21ad6
vim +/type +194 fs/notify/fanotify/fanotify.c

1c529063 Eric Paris     2009-12-17  154  
688d9244 Amir Goldstein 2019-01-10  155  static int fanotify_encode_fid(struct fanotify_event *event,
48b7a1af Amir Goldstein 2019-01-10  156  			       const struct path *path, gfp_t gfp,
48b7a1af Amir Goldstein 2019-01-10  157  			       __kernel_fsid_t *fsid)
688d9244 Amir Goldstein 2019-01-10  158  {
688d9244 Amir Goldstein 2019-01-10  159  	struct fanotify_fid *fid = &event->fid;
688d9244 Amir Goldstein 2019-01-10  160  	int dwords, bytes = 0;
688d9244 Amir Goldstein 2019-01-10  161  	int err, type;
688d9244 Amir Goldstein 2019-01-10  162  
48b7a1af Amir Goldstein 2019-01-10  163  	if (!fsid)
48b7a1af Amir Goldstein 2019-01-10  164  		goto out_err;
48b7a1af Amir Goldstein 2019-01-10  165  
688d9244 Amir Goldstein 2019-01-10  166  	fid->ext_fh = NULL;
688d9244 Amir Goldstein 2019-01-10  167  	dwords = 0;
688d9244 Amir Goldstein 2019-01-10  168  	err = -ENOENT;
688d9244 Amir Goldstein 2019-01-10  169  	type = exportfs_encode_fh(path->dentry, NULL, &dwords,  0);
688d9244 Amir Goldstein 2019-01-10  170  	if (!dwords)
688d9244 Amir Goldstein 2019-01-10  171  		goto out_err;
688d9244 Amir Goldstein 2019-01-10  172  
688d9244 Amir Goldstein 2019-01-10  173  	bytes = dwords << 2;
688d9244 Amir Goldstein 2019-01-10  174  	if (bytes > FANOTIFY_INLINE_FH_LEN) {
688d9244 Amir Goldstein 2019-01-10  175  		/* Treat failure to allocate fh as failure to allocate event */
688d9244 Amir Goldstein 2019-01-10  176  		err = -ENOMEM;
688d9244 Amir Goldstein 2019-01-10  177  		fid->ext_fh = kmalloc(bytes, gfp);
688d9244 Amir Goldstein 2019-01-10  178  		if (!fid->ext_fh)
688d9244 Amir Goldstein 2019-01-10  179  			goto out_err;
688d9244 Amir Goldstein 2019-01-10  180  	}
688d9244 Amir Goldstein 2019-01-10  181  
688d9244 Amir Goldstein 2019-01-10  182  	type = exportfs_encode_fh(path->dentry, fanotify_fid_fh(fid, bytes),
688d9244 Amir Goldstein 2019-01-10  183  				  &dwords,  0);
688d9244 Amir Goldstein 2019-01-10  184  	err = -EINVAL;
688d9244 Amir Goldstein 2019-01-10  185  	if (!type || type == FILEID_INVALID || bytes != dwords << 2)
688d9244 Amir Goldstein 2019-01-10  186  		goto out_err;
688d9244 Amir Goldstein 2019-01-10  187  
48b7a1af Amir Goldstein 2019-01-10  188  	fid->fsid = *fsid;
688d9244 Amir Goldstein 2019-01-10  189  	event->fh_len = bytes;
688d9244 Amir Goldstein 2019-01-10  190  
688d9244 Amir Goldstein 2019-01-10  191  	return type;
688d9244 Amir Goldstein 2019-01-10  192  
688d9244 Amir Goldstein 2019-01-10  193  out_err:
688d9244 Amir Goldstein 2019-01-10 @194  	pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, type=%d, bytes=%d, err=%i)\n",
48b7a1af Amir Goldstein 2019-01-10  195  			    fsid ? fsid->val[0] : 0, fsid ? fsid->val[1] : 0,
688d9244 Amir Goldstein 2019-01-10  196  			    type, bytes, err);
688d9244 Amir Goldstein 2019-01-10  197  	kfree(fid->ext_fh);
688d9244 Amir Goldstein 2019-01-10  198  	fid->ext_fh = NULL;
688d9244 Amir Goldstein 2019-01-10  199  	event->fh_len = 0;
688d9244 Amir Goldstein 2019-01-10  200  
688d9244 Amir Goldstein 2019-01-10  201  	return FILEID_INVALID;
688d9244 Amir Goldstein 2019-01-10  202  }
688d9244 Amir Goldstein 2019-01-10  203  

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Amir Goldstein Jan. 14, 2019, 9:17 a.m. UTC | #3
On Mon, Jan 14, 2019 at 9:31 AM Dan Carpenter <dan.carpenter@oracle.com> wrote:
>
> Hi Amir,
>
> url:    https://github.com/0day-ci/linux/commits/Amir-Goldstein/fanotify-add-support-for-more-event-types/20190111-090241
>
> smatch warnings:
> fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error: uninitialized symbol 'type'.
> fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error: uninitialized symbol 'err'.
>
> # https://github.com/0day-ci/linux/commit/48b7a1af51abcb893917d986bc5ae5bae2d21ad6
> git remote add linux-review https://github.com/0day-ci/linux
> git remote update linux-review
> git checkout 48b7a1af51abcb893917d986bc5ae5bae2d21ad6
> vim +/type +194 fs/notify/fanotify/fanotify.c
>

Thanks for the report!

> 1c529063 Eric Paris     2009-12-17  154
> 688d9244 Amir Goldstein 2019-01-10  155  static int fanotify_encode_fid(struct fanotify_event *event,
> 48b7a1af Amir Goldstein 2019-01-10  156                                const struct path *path, gfp_t gfp,
> 48b7a1af Amir Goldstein 2019-01-10  157                                __kernel_fsid_t *fsid)
> 688d9244 Amir Goldstein 2019-01-10  158  {
> 688d9244 Amir Goldstein 2019-01-10  159         struct fanotify_fid *fid = &event->fid;
> 688d9244 Amir Goldstein 2019-01-10  160         int dwords, bytes = 0;
> 688d9244 Amir Goldstein 2019-01-10  161         int err, type;
> 688d9244 Amir Goldstein 2019-01-10  162
> 48b7a1af Amir Goldstein 2019-01-10  163         if (!fsid)
> 48b7a1af Amir Goldstein 2019-01-10  164                 goto out_err;


Jan,

Can you fix this up on commit to:
int err = -ENODEV;
int type = FILEID_INVALID;

Thanks,
Amir.
Jan Kara Feb. 7, 2019, 2:48 p.m. UTC | #4
On Thu 10-01-19 19:04:37, Amir Goldstein wrote:
> For FAN_REPORT_FID, we need to encode fid with fsid of the filesystem on
> every event. To avoid having to call vfs_statfs() on every event to get
> fsid, we store the fsid in fsnotify_mark_connector on the first time we
> add a mark and on handle event we use the cached fsid.
> 
> Subsequent calls to add mark on the same object are expected to pass the
> same fsid, so the call will fail on cached fsid mismatch.
> 
> If an event is reported on several mark types (inode, mount, filesystem),
> all connectors should already have the same fsid, so we use the cached
> fsid from the first connector.
> 
> Suggested-by: Jan Kara <jack@suse.cz>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>

I've somewhat modified the patch to simplify code flow around
fanotify_get_fid() and also made fsid argument for
fsnotify_add_mark_locked() mandatory. The resulting patch is attached.

								Honza

> ---
>  fs/notify/fanotify/fanotify.c      | 51 +++++++++++++++++-------
>  fs/notify/fanotify/fanotify.h      |  5 ++-
>  fs/notify/fanotify/fanotify_user.c | 62 ++++++++++++++++++------------
>  fs/notify/mark.c                   | 47 +++++++++++++++++-----
>  include/linux/fsnotify_backend.h   | 24 +++++++++---
>  5 files changed, 135 insertions(+), 54 deletions(-)
> 
> diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> index e431f63c9f58..1bbf8b90dd83 100644
> --- a/fs/notify/fanotify/fanotify.c
> +++ b/fs/notify/fanotify/fanotify.c
> @@ -153,14 +153,16 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
>  }
>  
>  static int fanotify_encode_fid(struct fanotify_event *event,
> -			       const struct path *path, gfp_t gfp)
> +			       const struct path *path, gfp_t gfp,
> +			       __kernel_fsid_t *fsid)
>  {
>  	struct fanotify_fid *fid = &event->fid;
>  	int dwords, bytes = 0;
> -	struct kstatfs stat;
>  	int err, type;
>  
> -	stat.f_fsid.val[0] = stat.f_fsid.val[1] = 0;
> +	if (!fsid)
> +		goto out_err;
> +
>  	fid->ext_fh = NULL;
>  	dwords = 0;
>  	err = -ENOENT;
> @@ -168,10 +170,6 @@ static int fanotify_encode_fid(struct fanotify_event *event,
>  	if (!dwords)
>  		goto out_err;
>  
> -	err = vfs_statfs(path, &stat);
> -	if (err)
> -		goto out_err;
> -
>  	bytes = dwords << 2;
>  	if (bytes > FANOTIFY_INLINE_FH_LEN) {
>  		/* Treat failure to allocate fh as failure to allocate event */
> @@ -187,14 +185,14 @@ static int fanotify_encode_fid(struct fanotify_event *event,
>  	if (!type || type == FILEID_INVALID || bytes != dwords << 2)
>  		goto out_err;
>  
> -	fid->fsid = stat.f_fsid;
> +	fid->fsid = *fsid;
>  	event->fh_len = bytes;
>  
>  	return type;
>  
>  out_err:
>  	pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, type=%d, bytes=%d, err=%i)\n",
> -			    stat.f_fsid.val[0], stat.f_fsid.val[1],
> +			    fsid ? fsid->val[0] : 0, fsid ? fsid->val[1] : 0,
>  			    type, bytes, err);
>  	kfree(fid->ext_fh);
>  	fid->ext_fh = NULL;
> @@ -204,8 +202,9 @@ static int fanotify_encode_fid(struct fanotify_event *event,
>  }
>  
>  struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
> -						 struct inode *inode, u32 mask,
> -						 const struct path *path)
> +					    struct inode *inode, u32 mask,
> +					    const struct path *path,
> +					    __kernel_fsid_t *fsid)
>  {
>  	struct fanotify_event *event = NULL;
>  	gfp_t gfp = GFP_KERNEL_ACCOUNT;
> @@ -244,7 +243,7 @@ init: __maybe_unused
>  	event->fh_len = 0;
>  	if (path && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
>  		/* Report the event without a file identifier on encode error */
> -		event->fh_type = fanotify_encode_fid(event, path, gfp);
> +		event->fh_type = fanotify_encode_fid(event, path, gfp, fsid);
>  	} else if (path) {
>  		event->fh_type = FILEID_ROOT;
>  		event->path = *path;
> @@ -259,6 +258,28 @@ init: __maybe_unused
>  	return event;
>  }
>  
> +/*
> + * Get cached fsid of the filesystem containing the object from any connector.
> + * All connectors are supposed to have the same fsid, but we do not verify that
> + * here.
> + */
> +static __kernel_fsid_t *fanotify_get_fsid(struct fsnotify_iter_info *iter_info,
> +					  __kernel_fsid_t *fsid)
> +{
> +	int type;
> +
> +	fsnotify_foreach_obj_type(type) {
> +		if (!fsnotify_iter_should_report_type(iter_info, type))
> +			continue;
> +
> +		*fsid = iter_info->marks[type]->connector->fsid;
> +		if (!WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1]))
> +			return fsid;
> +	}
> +
> +	return NULL;
> +}
> +
>  static int fanotify_handle_event(struct fsnotify_group *group,
>  				 struct inode *inode,
>  				 u32 mask, const void *data, int data_type,
> @@ -268,6 +289,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
>  	int ret = 0;
>  	struct fanotify_event *event;
>  	struct fsnotify_event *fsn_event;
> +	__kernel_fsid_t __fsid, *fsid = NULL;
>  
>  	BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
>  	BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
> @@ -300,7 +322,10 @@ static int fanotify_handle_event(struct fsnotify_group *group,
>  			return 0;
>  	}
>  
> -	event = fanotify_alloc_event(group, inode, mask, data);
> +	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID))
> +		fsid = fanotify_get_fsid(iter_info, &__fsid);
> +
> +	event = fanotify_alloc_event(group, inode, mask, data, fsid);
>  	ret = -ENOMEM;
>  	if (unlikely(!event)) {
>  		/*
> diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
> index 4aafc7144c3d..5b072afa4e19 100644
> --- a/fs/notify/fanotify/fanotify.h
> +++ b/fs/notify/fanotify/fanotify.h
> @@ -131,5 +131,6 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
>  }
>  
>  struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
> -						 struct inode *inode, u32 mask,
> -						 const struct path *path);
> +					    struct inode *inode, u32 mask,
> +					    const struct path *path,
> +					    __kernel_fsid_t *fsid);
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index 211ec6332d31..467e6431fbe9 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -653,7 +653,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
>  
>  static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
>  						   fsnotify_connp_t *connp,
> -						   unsigned int type)
> +						   unsigned int type,
> +						   __kernel_fsid_t *fsid)
>  {
>  	struct fsnotify_mark *mark;
>  	int ret;
> @@ -666,7 +667,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
>  		return ERR_PTR(-ENOMEM);
>  
>  	fsnotify_init_mark(mark, group);
> -	ret = fsnotify_add_mark_locked(mark, connp, type, 0);
> +	ret = fsnotify_add_mark_locked_fsid(mark, connp, type, 0, fsid);
>  	if (ret) {
>  		fsnotify_put_mark(mark);
>  		return ERR_PTR(ret);
> @@ -678,7 +679,8 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
>  
>  static int fanotify_add_mark(struct fsnotify_group *group,
>  			     fsnotify_connp_t *connp, unsigned int type,
> -			     __u32 mask, unsigned int flags)
> +			     __u32 mask, unsigned int flags,
> +			     __kernel_fsid_t *fsid)
>  {
>  	struct fsnotify_mark *fsn_mark;
>  	__u32 added;
> @@ -686,7 +688,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
>  	mutex_lock(&group->mark_mutex);
>  	fsn_mark = fsnotify_find_mark(connp, group);
>  	if (!fsn_mark) {
> -		fsn_mark = fanotify_add_new_mark(group, connp, type);
> +		fsn_mark = fanotify_add_new_mark(group, connp, type, fsid);
>  		if (IS_ERR(fsn_mark)) {
>  			mutex_unlock(&group->mark_mutex);
>  			return PTR_ERR(fsn_mark);
> @@ -703,23 +705,23 @@ static int fanotify_add_mark(struct fsnotify_group *group,
>  
>  static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
>  				      struct vfsmount *mnt, __u32 mask,
> -				      unsigned int flags)
> +				      unsigned int flags, __kernel_fsid_t *fsid)
>  {
>  	return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
> -				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags);
> +				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
>  }
>  
>  static int fanotify_add_sb_mark(struct fsnotify_group *group,
> -				      struct super_block *sb, __u32 mask,
> -				      unsigned int flags)
> +				struct super_block *sb, __u32 mask,
> +				unsigned int flags, __kernel_fsid_t *fsid)
>  {
>  	return fanotify_add_mark(group, &sb->s_fsnotify_marks,
> -				 FSNOTIFY_OBJ_TYPE_SB, mask, flags);
> +				 FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
>  }
>  
>  static int fanotify_add_inode_mark(struct fsnotify_group *group,
>  				   struct inode *inode, __u32 mask,
> -				   unsigned int flags)
> +				   unsigned int flags, __kernel_fsid_t *fsid)
>  {
>  	pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
>  
> @@ -734,7 +736,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
>  		return 0;
>  
>  	return fanotify_add_mark(group, &inode->i_fsnotify_marks,
> -				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags);
> +				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
>  }
>  
>  /* fanotify syscalls */
> @@ -798,7 +800,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
>  	atomic_inc(&user->fanotify_listeners);
>  	group->memcg = get_mem_cgroup_from_mm(current->mm);
>  
> -	oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL);
> +	oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, NULL);
>  	if (unlikely(!oevent)) {
>  		fd = -ENOMEM;
>  		goto out_destroy_group;
> @@ -861,9 +863,9 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
>  }
>  
>  /* Check if filesystem can encode a unique fid */
> -static int fanotify_test_fid(struct path *path)
> +static int fanotify_test_fid(struct path *path, struct kstatfs *stat)
>  {
> -	struct kstatfs stat, root_stat;
> +	struct kstatfs root_stat;
>  	struct path root = {
>  		.mnt = path->mnt,
>  		.dentry = path->dentry->d_sb->s_root,
> @@ -873,11 +875,11 @@ static int fanotify_test_fid(struct path *path)
>  	/*
>  	 * Make sure path is not in filesystem with zero fsid (e.g. tmpfs).
>  	 */
> -	err = vfs_statfs(path, &stat);
> +	err = vfs_statfs(path, stat);
>  	if (err)
>  		return err;
>  
> -	if (!stat.f_fsid.val[0] && !stat.f_fsid.val[1])
> +	if (!stat->f_fsid.val[0] && !stat->f_fsid.val[1])
>  		return -ENODEV;
>  
>  	/*
> @@ -888,8 +890,8 @@ static int fanotify_test_fid(struct path *path)
>  	if (err)
>  		return err;
>  
> -	if (root_stat.f_fsid.val[0] != stat.f_fsid.val[0] ||
> -	    root_stat.f_fsid.val[1] != stat.f_fsid.val[1])
> +	if (root_stat.f_fsid.val[0] != stat->f_fsid.val[0] ||
> +	    root_stat.f_fsid.val[1] != stat->f_fsid.val[1])
>  		return -EXDEV;
>  
>  	/*
> @@ -914,6 +916,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
>  	struct fsnotify_group *group;
>  	struct fd f;
>  	struct path path;
> +	struct kstatfs stat;
> +	__kernel_fsid_t *fsid = NULL;
>  	u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
>  	unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
>  	int ret;
> @@ -992,9 +996,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
>  		goto fput_and_out;
>  
>  	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
> -		ret = fanotify_test_fid(&path);
> +		ret = fanotify_test_fid(&path, &stat);
>  		if (ret)
>  			goto path_put_and_out;
> +
> +		fsid = &stat.f_fsid;
>  	}
>  
>  	/* inode held in place by reference to path; group by fget on fd */
> @@ -1007,19 +1013,25 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
>  	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
>  	case FAN_MARK_ADD:
>  		if (mark_type == FAN_MARK_MOUNT)
> -			ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags);
> +			ret = fanotify_add_vfsmount_mark(group, mnt, mask,
> +							 flags, fsid);
>  		else if (mark_type == FAN_MARK_FILESYSTEM)
> -			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask, flags);
> +			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
> +						   flags, fsid);
>  		else
> -			ret = fanotify_add_inode_mark(group, inode, mask, flags);
> +			ret = fanotify_add_inode_mark(group, inode, mask,
> +						      flags, fsid);
>  		break;
>  	case FAN_MARK_REMOVE:
>  		if (mark_type == FAN_MARK_MOUNT)
> -			ret = fanotify_remove_vfsmount_mark(group, mnt, mask, flags);
> +			ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
> +							    flags);
>  		else if (mark_type == FAN_MARK_FILESYSTEM)
> -			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask, flags);
> +			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
> +						      flags);
>  		else
> -			ret = fanotify_remove_inode_mark(group, inode, mask, flags);
> +			ret = fanotify_remove_inode_mark(group, inode, mask,
> +							 flags);
>  		break;
>  	default:
>  		ret = -EINVAL;
> diff --git a/fs/notify/mark.c b/fs/notify/mark.c
> index d2dd16cb5989..3c5f39cc7fa3 100644
> --- a/fs/notify/mark.c
> +++ b/fs/notify/mark.c
> @@ -82,6 +82,7 @@
>  #include <linux/slab.h>
>  #include <linux/spinlock.h>
>  #include <linux/srcu.h>
> +#include <linux/ratelimit.h>
>  
>  #include <linux/atomic.h>
>  
> @@ -481,7 +482,8 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
>  }
>  
>  static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
> -					       unsigned int type)
> +					       unsigned int type,
> +					       __kernel_fsid_t *fsid)
>  {
>  	struct inode *inode = NULL;
>  	struct fsnotify_mark_connector *conn;
> @@ -493,6 +495,11 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
>  	INIT_HLIST_HEAD(&conn->list);
>  	conn->type = type;
>  	conn->obj = connp;
> +	/* Cache fsid of filesystem containing the object */
> +	if (fsid)
> +		conn->fsid = *fsid;
> +	else
> +		conn->fsid.val[0] = conn->fsid.val[1] = 0;
>  	if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
>  		inode = igrab(fsnotify_conn_inode(conn));
>  	/*
> @@ -544,7 +551,7 @@ static struct fsnotify_mark_connector *fsnotify_grab_connector(
>   */
>  static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
>  				  fsnotify_connp_t *connp, unsigned int type,
> -				  int allow_dups)
> +				  int allow_dups, __kernel_fsid_t *fsid)
>  {
>  	struct fsnotify_mark *lmark, *last = NULL;
>  	struct fsnotify_mark_connector *conn;
> @@ -553,15 +560,36 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
>  
>  	if (WARN_ON(!fsnotify_valid_obj_type(type)))
>  		return -EINVAL;
> +
> +	/* Backend is expected to check for zero fsid (e.g. tmpfs) */
> +	if (fsid && WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1]))
> +		return -ENODEV;
> +
>  restart:
>  	spin_lock(&mark->lock);
>  	conn = fsnotify_grab_connector(connp);
>  	if (!conn) {
>  		spin_unlock(&mark->lock);
> -		err = fsnotify_attach_connector_to_object(connp, type);
> +		err = fsnotify_attach_connector_to_object(connp, type, fsid);
>  		if (err)
>  			return err;
>  		goto restart;
> +	} else if (fsid && (conn->fsid.val[0] || conn->fsid.val[1]) &&
> +		   (fsid->val[0] != conn->fsid.val[0] ||
> +		    fsid->val[1] != conn->fsid.val[1])) {
> +		/*
> +		 * Backend is expected to check for non uniform fsid
> +		 * (e.g. btrfs), but maybe we missed something?
> +		 * Only allow setting conn->fsid once to non zero fsid.
> +		 * inotify and non-fid fanotify groups do not set nor test
> +		 * conn->fsid.
> +		 */
> +		pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
> +				    __func__, conn->type,
> +				    fsid->val[0], fsid->val[1],
> +				    conn->fsid.val[0], conn->fsid.val[1]);
> +		err = -EXDEV;
> +		goto out_err;
>  	}
>  
>  	/* is mark the first mark? */
> @@ -604,9 +632,9 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
>   * These marks may be used for the fsnotify backend to determine which
>   * event types should be delivered to which group.
>   */
> -int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
> -			     fsnotify_connp_t *connp, unsigned int type,
> -			     int allow_dups)
> +int fsnotify_add_mark_locked_fsid(struct fsnotify_mark *mark,
> +				  fsnotify_connp_t *connp, unsigned int type,
> +				  int allow_dups, __kernel_fsid_t *fsid)
>  {
>  	struct fsnotify_group *group = mark->group;
>  	int ret = 0;
> @@ -627,7 +655,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
>  	fsnotify_get_mark(mark); /* for g_list */
>  	spin_unlock(&mark->lock);
>  
> -	ret = fsnotify_add_mark_list(mark, connp, type, allow_dups);
> +	ret = fsnotify_add_mark_list(mark, connp, type, allow_dups, fsid);
>  	if (ret)
>  		goto err;
>  
> @@ -648,13 +676,14 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
>  }
>  
>  int fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp,
> -		      unsigned int type, int allow_dups)
> +		      unsigned int type, int allow_dups, __kernel_fsid_t *fsid)
>  {
>  	int ret;
>  	struct fsnotify_group *group = mark->group;
>  
>  	mutex_lock(&group->mark_mutex);
> -	ret = fsnotify_add_mark_locked(mark, connp, type, allow_dups);
> +	ret = fsnotify_add_mark_locked_fsid(mark, connp, type, allow_dups,
> +					    fsid);
>  	mutex_unlock(&group->mark_mutex);
>  	return ret;
>  }
> diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
> index 1e4b88bd1443..b66c4199d629 100644
> --- a/include/linux/fsnotify_backend.h
> +++ b/include/linux/fsnotify_backend.h
> @@ -293,6 +293,7 @@ typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t;
>  struct fsnotify_mark_connector {
>  	spinlock_t lock;
>  	unsigned int type;	/* Type of object [lock] */
> +	__kernel_fsid_t fsid;	/* fsid of filesystem containing object */
>  	union {
>  		/* Object pointer [lock] */
>  		fsnotify_connp_t *obj;
> @@ -433,20 +434,32 @@ extern void fsnotify_init_mark(struct fsnotify_mark *mark,
>  /* Find mark belonging to given group in the list of marks */
>  extern struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp,
>  						struct fsnotify_group *group);
> +/* Get cached fsid of filesystem containing object */
> +extern int fsnotify_get_conn_fsid(const struct fsnotify_mark_connector *conn,
> +				  __kernel_fsid_t *fsid);
>  /* attach the mark to the object */
>  extern int fsnotify_add_mark(struct fsnotify_mark *mark,
>  			     fsnotify_connp_t *connp, unsigned int type,
> -			     int allow_dups);
> -extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
> -				    fsnotify_connp_t *connp, unsigned int type,
> -				    int allow_dups);
> +			     int allow_dups, __kernel_fsid_t *fsid);
> +extern int fsnotify_add_mark_locked_fsid(struct fsnotify_mark *mark,
> +					 fsnotify_connp_t *connp,
> +					 unsigned int type, int allow_dups,
> +					 __kernel_fsid_t *fsid);
> +static inline int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
> +					   fsnotify_connp_t *connp,
> +					   unsigned int type, int allow_dups)
> +{
> +	return fsnotify_add_mark_locked_fsid(mark, connp, type, allow_dups,
> +					     NULL);
> +}
> +
>  /* attach the mark to the inode */
>  static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
>  					  struct inode *inode,
>  					  int allow_dups)
>  {
>  	return fsnotify_add_mark(mark, &inode->i_fsnotify_marks,
> -				 FSNOTIFY_OBJ_TYPE_INODE, allow_dups);
> +				 FSNOTIFY_OBJ_TYPE_INODE, allow_dups, NULL);
>  }
>  static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
>  						 struct inode *inode,
> @@ -455,6 +468,7 @@ static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
>  	return fsnotify_add_mark_locked(mark, &inode->i_fsnotify_marks,
>  					FSNOTIFY_OBJ_TYPE_INODE, allow_dups);
>  }
> +
>  /* given a group and a mark, flag mark to be freed when all references are dropped */
>  extern void fsnotify_destroy_mark(struct fsnotify_mark *mark,
>  				  struct fsnotify_group *group);
> -- 
> 2.17.1
>
Amir Goldstein Feb. 7, 2019, 4:31 p.m. UTC | #5
On Thu, Feb 7, 2019 at 4:48 PM Jan Kara <jack@suse.cz> wrote:
>
> On Thu 10-01-19 19:04:37, Amir Goldstein wrote:
> > For FAN_REPORT_FID, we need to encode fid with fsid of the filesystem on
> > every event. To avoid having to call vfs_statfs() on every event to get
> > fsid, we store the fsid in fsnotify_mark_connector on the first time we
> > add a mark and on handle event we use the cached fsid.
> >
> > Subsequent calls to add mark on the same object are expected to pass the
> > same fsid, so the call will fail on cached fsid mismatch.
> >
> > If an event is reported on several mark types (inode, mount, filesystem),
> > all connectors should already have the same fsid, so we use the cached
> > fsid from the first connector.
> >
> > Suggested-by: Jan Kara <jack@suse.cz>
> > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
>
> I've somewhat modified the patch to simplify code flow around
> fanotify_get_fid() and also made fsid argument for
> fsnotify_add_mark_locked() mandatory. The resulting patch is attached.
>

ACK. Two nits.

1. Dan Carpenter reported a smatch warning. Please fix:

fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error:
uninitialized symbol 'type'.
fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error:
uninitialized symbol 'err'.

2. checkpatch doesn't like cutting long debug strings into 80 chars
(and Greg's linuxsty.vim plugin doesn't warn about them)
referring to the long string that you broke up
pr_warn_ratelimited("%s: fsid mismatch on object of type %u: "...

Thanks,
Amir.
Jan Kara Feb. 8, 2019, 10:15 a.m. UTC | #6
On Thu 07-02-19 18:31:19, Amir Goldstein wrote:
> On Thu, Feb 7, 2019 at 4:48 PM Jan Kara <jack@suse.cz> wrote:
> >
> > On Thu 10-01-19 19:04:37, Amir Goldstein wrote:
> > > For FAN_REPORT_FID, we need to encode fid with fsid of the filesystem on
> > > every event. To avoid having to call vfs_statfs() on every event to get
> > > fsid, we store the fsid in fsnotify_mark_connector on the first time we
> > > add a mark and on handle event we use the cached fsid.
> > >
> > > Subsequent calls to add mark on the same object are expected to pass the
> > > same fsid, so the call will fail on cached fsid mismatch.
> > >
> > > If an event is reported on several mark types (inode, mount, filesystem),
> > > all connectors should already have the same fsid, so we use the cached
> > > fsid from the first connector.
> > >
> > > Suggested-by: Jan Kara <jack@suse.cz>
> > > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> >
> > I've somewhat modified the patch to simplify code flow around
> > fanotify_get_fid() and also made fsid argument for
> > fsnotify_add_mark_locked() mandatory. The resulting patch is attached.
> >
> 
> ACK. Two nits.
> 
> 1. Dan Carpenter reported a smatch warning. Please fix:
> 
> fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error:
> uninitialized symbol 'type'.
> fs/notify/fanotify/fanotify.c:194 fanotify_encode_fid() error:
> uninitialized symbol 'err'.

Yes, but this problem went away with my changes as well since we do 'goto
out_err' only after setting err and type.

> 2. checkpatch doesn't like cutting long debug strings into 80 chars
> (and Greg's linuxsty.vim plugin doesn't warn about them)
> referring to the long string that you broke up
> pr_warn_ratelimited("%s: fsid mismatch on object of type %u: "...

I know and I don't take that too seriously. The point is: You don't want to
break the string so that it is easily greppable. But as long as the core
part of the string is in one line, it's OK. In particular once you get to
the part of the format string where you have the format specifiers,
breaking the string is irrelevant for grepping anymore since nobody can
sanely grep that part of the string. And I took care to follow these
guidances.

								Honza

Patch
diff mbox series

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index e431f63c9f58..1bbf8b90dd83 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -153,14 +153,16 @@  static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
 }
 
 static int fanotify_encode_fid(struct fanotify_event *event,
-			       const struct path *path, gfp_t gfp)
+			       const struct path *path, gfp_t gfp,
+			       __kernel_fsid_t *fsid)
 {
 	struct fanotify_fid *fid = &event->fid;
 	int dwords, bytes = 0;
-	struct kstatfs stat;
 	int err, type;
 
-	stat.f_fsid.val[0] = stat.f_fsid.val[1] = 0;
+	if (!fsid)
+		goto out_err;
+
 	fid->ext_fh = NULL;
 	dwords = 0;
 	err = -ENOENT;
@@ -168,10 +170,6 @@  static int fanotify_encode_fid(struct fanotify_event *event,
 	if (!dwords)
 		goto out_err;
 
-	err = vfs_statfs(path, &stat);
-	if (err)
-		goto out_err;
-
 	bytes = dwords << 2;
 	if (bytes > FANOTIFY_INLINE_FH_LEN) {
 		/* Treat failure to allocate fh as failure to allocate event */
@@ -187,14 +185,14 @@  static int fanotify_encode_fid(struct fanotify_event *event,
 	if (!type || type == FILEID_INVALID || bytes != dwords << 2)
 		goto out_err;
 
-	fid->fsid = stat.f_fsid;
+	fid->fsid = *fsid;
 	event->fh_len = bytes;
 
 	return type;
 
 out_err:
 	pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, type=%d, bytes=%d, err=%i)\n",
-			    stat.f_fsid.val[0], stat.f_fsid.val[1],
+			    fsid ? fsid->val[0] : 0, fsid ? fsid->val[1] : 0,
 			    type, bytes, err);
 	kfree(fid->ext_fh);
 	fid->ext_fh = NULL;
@@ -204,8 +202,9 @@  static int fanotify_encode_fid(struct fanotify_event *event,
 }
 
 struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
-						 struct inode *inode, u32 mask,
-						 const struct path *path)
+					    struct inode *inode, u32 mask,
+					    const struct path *path,
+					    __kernel_fsid_t *fsid)
 {
 	struct fanotify_event *event = NULL;
 	gfp_t gfp = GFP_KERNEL_ACCOUNT;
@@ -244,7 +243,7 @@  init: __maybe_unused
 	event->fh_len = 0;
 	if (path && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
 		/* Report the event without a file identifier on encode error */
-		event->fh_type = fanotify_encode_fid(event, path, gfp);
+		event->fh_type = fanotify_encode_fid(event, path, gfp, fsid);
 	} else if (path) {
 		event->fh_type = FILEID_ROOT;
 		event->path = *path;
@@ -259,6 +258,28 @@  init: __maybe_unused
 	return event;
 }
 
+/*
+ * Get cached fsid of the filesystem containing the object from any connector.
+ * All connectors are supposed to have the same fsid, but we do not verify that
+ * here.
+ */
+static __kernel_fsid_t *fanotify_get_fsid(struct fsnotify_iter_info *iter_info,
+					  __kernel_fsid_t *fsid)
+{
+	int type;
+
+	fsnotify_foreach_obj_type(type) {
+		if (!fsnotify_iter_should_report_type(iter_info, type))
+			continue;
+
+		*fsid = iter_info->marks[type]->connector->fsid;
+		if (!WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1]))
+			return fsid;
+	}
+
+	return NULL;
+}
+
 static int fanotify_handle_event(struct fsnotify_group *group,
 				 struct inode *inode,
 				 u32 mask, const void *data, int data_type,
@@ -268,6 +289,7 @@  static int fanotify_handle_event(struct fsnotify_group *group,
 	int ret = 0;
 	struct fanotify_event *event;
 	struct fsnotify_event *fsn_event;
+	__kernel_fsid_t __fsid, *fsid = NULL;
 
 	BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
 	BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
@@ -300,7 +322,10 @@  static int fanotify_handle_event(struct fsnotify_group *group,
 			return 0;
 	}
 
-	event = fanotify_alloc_event(group, inode, mask, data);
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID))
+		fsid = fanotify_get_fsid(iter_info, &__fsid);
+
+	event = fanotify_alloc_event(group, inode, mask, data, fsid);
 	ret = -ENOMEM;
 	if (unlikely(!event)) {
 		/*
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 4aafc7144c3d..5b072afa4e19 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -131,5 +131,6 @@  static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
 }
 
 struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
-						 struct inode *inode, u32 mask,
-						 const struct path *path);
+					    struct inode *inode, u32 mask,
+					    const struct path *path,
+					    __kernel_fsid_t *fsid);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 211ec6332d31..467e6431fbe9 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -653,7 +653,8 @@  static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
 
 static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
 						   fsnotify_connp_t *connp,
-						   unsigned int type)
+						   unsigned int type,
+						   __kernel_fsid_t *fsid)
 {
 	struct fsnotify_mark *mark;
 	int ret;
@@ -666,7 +667,7 @@  static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
 		return ERR_PTR(-ENOMEM);
 
 	fsnotify_init_mark(mark, group);
-	ret = fsnotify_add_mark_locked(mark, connp, type, 0);
+	ret = fsnotify_add_mark_locked_fsid(mark, connp, type, 0, fsid);
 	if (ret) {
 		fsnotify_put_mark(mark);
 		return ERR_PTR(ret);
@@ -678,7 +679,8 @@  static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
 
 static int fanotify_add_mark(struct fsnotify_group *group,
 			     fsnotify_connp_t *connp, unsigned int type,
-			     __u32 mask, unsigned int flags)
+			     __u32 mask, unsigned int flags,
+			     __kernel_fsid_t *fsid)
 {
 	struct fsnotify_mark *fsn_mark;
 	__u32 added;
@@ -686,7 +688,7 @@  static int fanotify_add_mark(struct fsnotify_group *group,
 	mutex_lock(&group->mark_mutex);
 	fsn_mark = fsnotify_find_mark(connp, group);
 	if (!fsn_mark) {
-		fsn_mark = fanotify_add_new_mark(group, connp, type);
+		fsn_mark = fanotify_add_new_mark(group, connp, type, fsid);
 		if (IS_ERR(fsn_mark)) {
 			mutex_unlock(&group->mark_mutex);
 			return PTR_ERR(fsn_mark);
@@ -703,23 +705,23 @@  static int fanotify_add_mark(struct fsnotify_group *group,
 
 static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
 				      struct vfsmount *mnt, __u32 mask,
-				      unsigned int flags)
+				      unsigned int flags, __kernel_fsid_t *fsid)
 {
 	return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags);
+				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
 }
 
 static int fanotify_add_sb_mark(struct fsnotify_group *group,
-				      struct super_block *sb, __u32 mask,
-				      unsigned int flags)
+				struct super_block *sb, __u32 mask,
+				unsigned int flags, __kernel_fsid_t *fsid)
 {
 	return fanotify_add_mark(group, &sb->s_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_SB, mask, flags);
+				 FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
 }
 
 static int fanotify_add_inode_mark(struct fsnotify_group *group,
 				   struct inode *inode, __u32 mask,
-				   unsigned int flags)
+				   unsigned int flags, __kernel_fsid_t *fsid)
 {
 	pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
 
@@ -734,7 +736,7 @@  static int fanotify_add_inode_mark(struct fsnotify_group *group,
 		return 0;
 
 	return fanotify_add_mark(group, &inode->i_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags);
+				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
 }
 
 /* fanotify syscalls */
@@ -798,7 +800,7 @@  SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 	atomic_inc(&user->fanotify_listeners);
 	group->memcg = get_mem_cgroup_from_mm(current->mm);
 
-	oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL);
+	oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, NULL);
 	if (unlikely(!oevent)) {
 		fd = -ENOMEM;
 		goto out_destroy_group;
@@ -861,9 +863,9 @@  SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 }
 
 /* Check if filesystem can encode a unique fid */
-static int fanotify_test_fid(struct path *path)
+static int fanotify_test_fid(struct path *path, struct kstatfs *stat)
 {
-	struct kstatfs stat, root_stat;
+	struct kstatfs root_stat;
 	struct path root = {
 		.mnt = path->mnt,
 		.dentry = path->dentry->d_sb->s_root,
@@ -873,11 +875,11 @@  static int fanotify_test_fid(struct path *path)
 	/*
 	 * Make sure path is not in filesystem with zero fsid (e.g. tmpfs).
 	 */
-	err = vfs_statfs(path, &stat);
+	err = vfs_statfs(path, stat);
 	if (err)
 		return err;
 
-	if (!stat.f_fsid.val[0] && !stat.f_fsid.val[1])
+	if (!stat->f_fsid.val[0] && !stat->f_fsid.val[1])
 		return -ENODEV;
 
 	/*
@@ -888,8 +890,8 @@  static int fanotify_test_fid(struct path *path)
 	if (err)
 		return err;
 
-	if (root_stat.f_fsid.val[0] != stat.f_fsid.val[0] ||
-	    root_stat.f_fsid.val[1] != stat.f_fsid.val[1])
+	if (root_stat.f_fsid.val[0] != stat->f_fsid.val[0] ||
+	    root_stat.f_fsid.val[1] != stat->f_fsid.val[1])
 		return -EXDEV;
 
 	/*
@@ -914,6 +916,8 @@  static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 	struct fsnotify_group *group;
 	struct fd f;
 	struct path path;
+	struct kstatfs stat;
+	__kernel_fsid_t *fsid = NULL;
 	u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
 	unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
 	int ret;
@@ -992,9 +996,11 @@  static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 		goto fput_and_out;
 
 	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
-		ret = fanotify_test_fid(&path);
+		ret = fanotify_test_fid(&path, &stat);
 		if (ret)
 			goto path_put_and_out;
+
+		fsid = &stat.f_fsid;
 	}
 
 	/* inode held in place by reference to path; group by fget on fd */
@@ -1007,19 +1013,25 @@  static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
 	case FAN_MARK_ADD:
 		if (mark_type == FAN_MARK_MOUNT)
-			ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags);
+			ret = fanotify_add_vfsmount_mark(group, mnt, mask,
+							 flags, fsid);
 		else if (mark_type == FAN_MARK_FILESYSTEM)
-			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask, flags);
+			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
+						   flags, fsid);
 		else
-			ret = fanotify_add_inode_mark(group, inode, mask, flags);
+			ret = fanotify_add_inode_mark(group, inode, mask,
+						      flags, fsid);
 		break;
 	case FAN_MARK_REMOVE:
 		if (mark_type == FAN_MARK_MOUNT)
-			ret = fanotify_remove_vfsmount_mark(group, mnt, mask, flags);
+			ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
+							    flags);
 		else if (mark_type == FAN_MARK_FILESYSTEM)
-			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask, flags);
+			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
+						      flags);
 		else
-			ret = fanotify_remove_inode_mark(group, inode, mask, flags);
+			ret = fanotify_remove_inode_mark(group, inode, mask,
+							 flags);
 		break;
 	default:
 		ret = -EINVAL;
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index d2dd16cb5989..3c5f39cc7fa3 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -82,6 +82,7 @@ 
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/srcu.h>
+#include <linux/ratelimit.h>
 
 #include <linux/atomic.h>
 
@@ -481,7 +482,8 @@  int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
 }
 
 static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
-					       unsigned int type)
+					       unsigned int type,
+					       __kernel_fsid_t *fsid)
 {
 	struct inode *inode = NULL;
 	struct fsnotify_mark_connector *conn;
@@ -493,6 +495,11 @@  static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
 	INIT_HLIST_HEAD(&conn->list);
 	conn->type = type;
 	conn->obj = connp;
+	/* Cache fsid of filesystem containing the object */
+	if (fsid)
+		conn->fsid = *fsid;
+	else
+		conn->fsid.val[0] = conn->fsid.val[1] = 0;
 	if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
 		inode = igrab(fsnotify_conn_inode(conn));
 	/*
@@ -544,7 +551,7 @@  static struct fsnotify_mark_connector *fsnotify_grab_connector(
  */
 static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
 				  fsnotify_connp_t *connp, unsigned int type,
-				  int allow_dups)
+				  int allow_dups, __kernel_fsid_t *fsid)
 {
 	struct fsnotify_mark *lmark, *last = NULL;
 	struct fsnotify_mark_connector *conn;
@@ -553,15 +560,36 @@  static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
 
 	if (WARN_ON(!fsnotify_valid_obj_type(type)))
 		return -EINVAL;
+
+	/* Backend is expected to check for zero fsid (e.g. tmpfs) */
+	if (fsid && WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1]))
+		return -ENODEV;
+
 restart:
 	spin_lock(&mark->lock);
 	conn = fsnotify_grab_connector(connp);
 	if (!conn) {
 		spin_unlock(&mark->lock);
-		err = fsnotify_attach_connector_to_object(connp, type);
+		err = fsnotify_attach_connector_to_object(connp, type, fsid);
 		if (err)
 			return err;
 		goto restart;
+	} else if (fsid && (conn->fsid.val[0] || conn->fsid.val[1]) &&
+		   (fsid->val[0] != conn->fsid.val[0] ||
+		    fsid->val[1] != conn->fsid.val[1])) {
+		/*
+		 * Backend is expected to check for non uniform fsid
+		 * (e.g. btrfs), but maybe we missed something?
+		 * Only allow setting conn->fsid once to non zero fsid.
+		 * inotify and non-fid fanotify groups do not set nor test
+		 * conn->fsid.
+		 */
+		pr_warn_ratelimited("%s: fsid mismatch on object of type %u: %x.%x != %x.%x\n",
+				    __func__, conn->type,
+				    fsid->val[0], fsid->val[1],
+				    conn->fsid.val[0], conn->fsid.val[1]);
+		err = -EXDEV;
+		goto out_err;
 	}
 
 	/* is mark the first mark? */
@@ -604,9 +632,9 @@  static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
  * These marks may be used for the fsnotify backend to determine which
  * event types should be delivered to which group.
  */
-int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
-			     fsnotify_connp_t *connp, unsigned int type,
-			     int allow_dups)
+int fsnotify_add_mark_locked_fsid(struct fsnotify_mark *mark,
+				  fsnotify_connp_t *connp, unsigned int type,
+				  int allow_dups, __kernel_fsid_t *fsid)
 {
 	struct fsnotify_group *group = mark->group;
 	int ret = 0;
@@ -627,7 +655,7 @@  int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
 	fsnotify_get_mark(mark); /* for g_list */
 	spin_unlock(&mark->lock);
 
-	ret = fsnotify_add_mark_list(mark, connp, type, allow_dups);
+	ret = fsnotify_add_mark_list(mark, connp, type, allow_dups, fsid);
 	if (ret)
 		goto err;
 
@@ -648,13 +676,14 @@  int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
 }
 
 int fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp,
-		      unsigned int type, int allow_dups)
+		      unsigned int type, int allow_dups, __kernel_fsid_t *fsid)
 {
 	int ret;
 	struct fsnotify_group *group = mark->group;
 
 	mutex_lock(&group->mark_mutex);
-	ret = fsnotify_add_mark_locked(mark, connp, type, allow_dups);
+	ret = fsnotify_add_mark_locked_fsid(mark, connp, type, allow_dups,
+					    fsid);
 	mutex_unlock(&group->mark_mutex);
 	return ret;
 }
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 1e4b88bd1443..b66c4199d629 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -293,6 +293,7 @@  typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t;
 struct fsnotify_mark_connector {
 	spinlock_t lock;
 	unsigned int type;	/* Type of object [lock] */
+	__kernel_fsid_t fsid;	/* fsid of filesystem containing object */
 	union {
 		/* Object pointer [lock] */
 		fsnotify_connp_t *obj;
@@ -433,20 +434,32 @@  extern void fsnotify_init_mark(struct fsnotify_mark *mark,
 /* Find mark belonging to given group in the list of marks */
 extern struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp,
 						struct fsnotify_group *group);
+/* Get cached fsid of filesystem containing object */
+extern int fsnotify_get_conn_fsid(const struct fsnotify_mark_connector *conn,
+				  __kernel_fsid_t *fsid);
 /* attach the mark to the object */
 extern int fsnotify_add_mark(struct fsnotify_mark *mark,
 			     fsnotify_connp_t *connp, unsigned int type,
-			     int allow_dups);
-extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
-				    fsnotify_connp_t *connp, unsigned int type,
-				    int allow_dups);
+			     int allow_dups, __kernel_fsid_t *fsid);
+extern int fsnotify_add_mark_locked_fsid(struct fsnotify_mark *mark,
+					 fsnotify_connp_t *connp,
+					 unsigned int type, int allow_dups,
+					 __kernel_fsid_t *fsid);
+static inline int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
+					   fsnotify_connp_t *connp,
+					   unsigned int type, int allow_dups)
+{
+	return fsnotify_add_mark_locked_fsid(mark, connp, type, allow_dups,
+					     NULL);
+}
+
 /* attach the mark to the inode */
 static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
 					  struct inode *inode,
 					  int allow_dups)
 {
 	return fsnotify_add_mark(mark, &inode->i_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_INODE, allow_dups);
+				 FSNOTIFY_OBJ_TYPE_INODE, allow_dups, NULL);
 }
 static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
 						 struct inode *inode,
@@ -455,6 +468,7 @@  static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
 	return fsnotify_add_mark_locked(mark, &inode->i_fsnotify_marks,
 					FSNOTIFY_OBJ_TYPE_INODE, allow_dups);
 }
+
 /* given a group and a mark, flag mark to be freed when all references are dropped */
 extern void fsnotify_destroy_mark(struct fsnotify_mark *mark,
 				  struct fsnotify_group *group);