[RFC v3 0/4] fs: Add generic file system event notifications

17 messages, 6 authors, 2015-07-30 · open the first message on its own page

[RFC v3 0/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-06-16 13:09:29

Hi All,

First of all, apologies for the delay: illness ruled out my plans
for having this out for a review as intended.

Anyway this is an updated version of the patchset for generic filesystem
events interface [1][2], hopefully handling issues raised during the previous
run.

Changes from v2:
	- Switched to kref for reference counting
	- Support for the events has been made optional (config option)
	- Use dynamically assigned id for multicast group instead of
	  using static one
	- Verify if there are any net listeners prior to sending the msg
	- Make the interface more namespace-aware (handling mount dropped
	  and showing the content of config file). As for the network
	  namespaces - as before only the init net namespace is being
	  supported.

Changes from v1:
	- Improved synchronization: switched to RCU accompanied with
	  ref counting mechanism
	- Limiting scope of supported event types along with default
	  event codes
	- Slightly modified configuration (event types followed by arguments
	  where required)
	- Updated documentation
	- Unified naming for netlink attributes
	- Updated netlink message format to include dev minor:major numbers
	  despite the filesystem type
	- Switched to single cmd id for messages
	- Removed the per-config-entry ids

---
[1] https://lkml.org/lkml/2015/4/15/46
[2] https://lkml.org/lkml/2015/4/27/244
---

Beata Michalska (4):
  fs: Add generic file system event notifications
  ext4: Add helper function to mark group as corrupted
  ext4: Add support for generic FS events
  shmem: Add support for generic FS events

 Documentation/filesystems/events.txt |  232 ++++++++++
 fs/Kconfig                           |    2 +
 fs/Makefile                          |    1 +
 fs/events/Kconfig                    |    7 +
 fs/events/Makefile                   |    5 +
 fs/events/fs_event.c                 |  809 ++++++++++++++++++++++++++++++++++
 fs/events/fs_event.h                 |   22 +
 fs/events/fs_event_netlink.c         |  104 +++++
 fs/ext4/balloc.c                     |   25 +-
 fs/ext4/ext4.h                       |   10 +
 fs/ext4/ialloc.c                     |    5 +-
 fs/ext4/inode.c                      |    2 +-
 fs/ext4/mballoc.c                    |   17 +-
 fs/ext4/resize.c                     |    1 +
 fs/ext4/super.c                      |   39 ++
 fs/namespace.c                       |    1 +
 include/linux/fs.h                   |    6 +-
 include/linux/fs_event.h             |   72 +++
 include/uapi/linux/Kbuild            |    1 +
 include/uapi/linux/fs_event.h        |   58 +++
 mm/shmem.c                           |   33 +-
 21 files changed, 1419 insertions(+), 33 deletions(-)
 create mode 100644 Documentation/filesystems/events.txt
 create mode 100644 fs/events/Kconfig
 create mode 100644 fs/events/Makefile
 create mode 100644 fs/events/fs_event.c
 create mode 100644 fs/events/fs_event.h
 create mode 100644 fs/events/fs_event_netlink.c
 create mode 100644 include/linux/fs_event.h
 create mode 100644 include/uapi/linux/fs_event.h

-- 
1.7.9.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

[RFC v3 1/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-06-16 13:09:30

Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
---
 Documentation/filesystems/events.txt |  232 ++++++++++
 fs/Kconfig                           |    2 +
 fs/Makefile                          |    1 +
 fs/events/Kconfig                    |    7 +
 fs/events/Makefile                   |    5 +
 fs/events/fs_event.c                 |  809 ++++++++++++++++++++++++++++++++++
 fs/events/fs_event.h                 |   22 +
 fs/events/fs_event_netlink.c         |  104 +++++
 fs/namespace.c                       |    1 +
 include/linux/fs.h                   |    6 +-
 include/linux/fs_event.h             |   72 +++
 include/uapi/linux/Kbuild            |    1 +
 include/uapi/linux/fs_event.h        |   58 +++
 13 files changed, 1319 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/filesystems/events.txt
 create mode 100644 fs/events/Kconfig
 create mode 100644 fs/events/Makefile
 create mode 100644 fs/events/fs_event.c
 create mode 100644 fs/events/fs_event.h
 create mode 100644 fs/events/fs_event_netlink.c
 create mode 100644 include/linux/fs_event.h
 create mode 100644 include/uapi/linux/fs_event.h
diff --git a/Documentation/filesystems/events.txt b/Documentation/filesystems/events.txt
new file mode 100644
index 0000000..c2e6227
--- /dev/null
+++ b/Documentation/filesystems/events.txt
@@ -0,0 +1,232 @@
+
+	Generic file system event notification interface
+
+Document created 23 April 2015 by Beata Michalska <b.michalska@samsung.com>
+
+1. The reason behind:
+=====================
+
+There are many corner cases when things might get messy with the filesystems.
+And it is not always obvious what and when went wrong. Sometimes you might
+get some subtle hints that there is something going on - but by the time
+you realise it, it might be too late as you are already out-of-space
+or the filesystem has been remounted as read-only (i.e.). The generic
+interface for the filesystem events fills the gap by providing a rather
+easy way of real-time notifications triggered whenever something interesting
+happens, allowing filesystems to report events in a common way, as they occur.
+
+2. How does it work:
+====================
+
+The interface itself has been exposed as fstrace-type Virtual File System,
+primarily to ease the process of setting up the configuration for the
+notifications. So for starters, it needs to get mounted (obviously):
+
+	mount -t fstrace none /sys/fs/events
+
+This will unveil the single fstrace filesystem entry - the 'config' file,
+through which the notification are being set-up.
+
+Activating notifications for particular filesystem is as straightforward
+as writing into the 'config' file. Note that by default all events, despite
+the actual filesystem type, are being disregarded.
+
+Synopsis of config:
+------------------
+
+	MOUNT EVENT_TYPE [L1] [L2]
+
+ MOUNT      : the filesystem's mount point
+ EVENT_TYPE : event types - currently two of them are being supported:
+
+	      * generic events ("G") covering most common warnings
+	      and errors that might be reported by any filesystem;
+	      this option does not take any arguments;
+
+	      * threshold notifications ("T") - events sent whenever
+	      the amount of available space drops below certain level;
+	      it is possible to specify two threshold levels though
+	      only one is required to properly setup the notifications;
+	      as those refer to the number of available blocks, the lower
+	      level [L1] needs to be higher than the upper one [L2]
+
+Sample request could look like the following:
+
+ echo /sample/mount/point G T 710000 500000 > /sys/fs/events/config
+
+Multiple request might be specified provided they are separated with semicolon.
+
+The configuration itself might be modified at any time. One can add/remove
+particular event types for given fielsystem, modify the threshold levels,
+and remove single or all entries from the 'config' file.
+
+ - Adding new event type:
+
+ $ echo MOUNT EVENT_TYPE > /sys/fs/events/config
+
+(Note that is is enough to provide the event type to be enabled without
+the already set ones.)
+
+ - Removing event type:
+
+ $ echo '!MOUNT EVENT_TYPE' > /sys/fs/events/config
+
+ - Updating threshold limits:
+
+ $ echo MOUNT T L1 L2 > /sys/fs/events/config
+
+ - Removing single entry:
+
+ $ echo '!MOUNT' > /sys/fs/events/config
+
+ - Removing all entries:
+
+ $ echo > /sys/fs/events/config
+
+Reading the file will list all registered entries with their current set-up
+along with some additional info like the filesystem type and the backing device
+name if available.
+
+Final, though a very important note on the configuration: when and if the
+actual events are being triggered falls way beyond the scope of the generic
+filesystem events interface. It is up to a particular filesystem
+implementation which events are to be supported - if any at all. So if
+given filesystem does not support the event notifications, an attempt to
+enable those through 'config' file will fail.
+
+
+3. The generic netlink interface support:
+=========================================
+
+Whenever an event notification is triggered (by given filesystem) the current
+configuration is being validated to decide whether a userpsace notification
+should be launched. If there has been no request (in a mean of 'config' file
+entry) for given event, one will be silently disregarded. If, on the other
+hand, someone is 'watching' given filesystem for specific events, a generic
+netlink message will be sent. A dedicated multicast group has been provided
+solely for this purpose so in order to receive such notifications, one should
+subscribe to this new multicast group. As for now only the init network
+namespace is being supported.
+
+3.1 Message format
+
+The FS_NL_C_EVENT shall be stored within the generic netlink message header
+as the command field. The message payload will provide more detailed info:
+the backing device major and minor numbers, the event code and the id of
+the process which action led to the event occurrence. In case of threshold
+notifications, the current number of available blocks will be included
+in the payload as well.
+
+
+	 0                   1                   2                   3
+	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|	            NETLINK MESSAGE HEADER			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 		GENERIC NETLINK MESSAGE HEADER   		|
+	| 	   (with FS_NL_C_EVENT as genlmsghdr cdm field)		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 	      Optional user specific message header		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|		   GENERIC MESSAGE PAYLOAD:			|
+	+---------------------------------------------------------------+
+	| 		  FS_NL_A_EVENT_ID  (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|	  	  FS_NL_A_DEV_MAJOR (NLA_U32)			|
+	+---------------------------------------------------------------+
+	| 	  	  FS_NL_A_DEV_MINOR (NLA_U32) 			|
+	+---------------------------------------------------------------+
+	|  		  FS_NL_A_CAUSED_ID (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|  		    FS_NL_A_DATA (NLA_U64)			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+The above figure is based on:
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto#Message_Format
+
+
+4. API Reference:
+=================
+
+ 4.1 Generic file system event interface data & operations
+
+ #include <linux/fs_event.h>
+
+ struct fs_trace_info {
+	void	__rcu	*e_priv		 /* READ ONLY */
+	unsigned int 	events_cap_mask; /* Supported notifications */
+	const struct fs_trace_operations *ops;
+ };
+
+ struct fs_trace_operations {
+	void (*query)(struct super_block *, u64 *);
+ };
+
+ In order to get the fireworks and stuff, each filesystem needs to setup
+ the events_cap_mask field of the fs_trace_info structure, which has been
+ embedded within the super_block structure. This should reflect the type of
+ events the filesystem wants to support. In case of threshold notifications,
+ apart from setting the FS_EVENT_THRESH flag, the 'query' callback should
+ be provided as this enables the events interface to get the up-to-date
+ state of the number of available blocks whenever those notifications are
+ being requested.
+
+ The 'e_priv' field of the fs_trace_info structure should be completely ignored
+ as it's for INTERNAL USE ONLY. So don't even think of messing with it, if you
+ do not want to get yourself into some real trouble. If still, you are tempted
+ to do so - feel free, it's gonna be pure fun. Consider yourself warned.
+
+
+ 4.2 Event notification:
+
+ #include <linux/fs_event.h>
+ void fs_event_notify(struct super_block *sb, unsigned int event_id);
+
+ Notify the generic FS event interface of an occurring event.
+ This shall be used by any file system that wishes to inform any potential
+ listeners/watchers of a particular event.
+ - sb:         the filesystem's super block
+ - event_id:   an event identifier
+
+ 4.3 Threshold notifications:
+
+ #include <linux/fs_event.h>
+ void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+ void fs_event_free_space(struct super_block *sb, u64 ncount);
+
+ Each filesystme supporting the threshold notifications should call
+ fs_event_alloc_space/fs_event_free_space respectively whenever the
+ amount of available blocks changes.
+ - sb:     the filesystem's super block
+ - ncount: number of blocks being acquired/released
+
+ Note that to properly handle the threshold notifications the fs events
+ interface needs to be kept up to date by the filesystems. Each should
+ register fs_trace_operations to enable querying the current number of
+ available blocks.
+
+ 4.4 Sending message through generic netlink interface
+
+ #include <linux/fs_event.h>
+
+ int fs_netlink_send_event(size_t size, unsigned int event_id,
+	int (*compose_msg)(struct sk_buff *skb, void *data), void *cbdata);
+
+ Although the fs event interface is fully responsible for sending the messages
+ over the netlink, filesystems might use the FS_EVENT multicast group to send
+ their own custom messages.
+ - size:        the size of the message payload
+ - event_id:    the event identifier
+ - compose_msg: a callback responsible for filling-in the message payload
+ - cbdata:      message custom data
+
+ Calling fs_netlink_send_event will result in a message being sent by
+ the FS_EVENT multicast group. Note that the body of the message should be
+ prepared (set-up )by the caller - through compose_msg callback. The message's
+ sk_buff will be allocated on behalf of the caller (thus the size parameter).
+ The compose_msg should only fill the payload with proper data. Unless
+ the event id is specified as FS_EVENT_NONE, it's value shall be added
+ to the payload prior to calling the compose_msg.
+
+
diff --git a/fs/Kconfig b/fs/Kconfig
index ec35851..a89e678 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -69,6 +69,8 @@ config FILE_LOCKING
           for filesystems like NFS and for the flock() system
           call. Disabling this option saves about 11k.
 
+source "fs/events/Kconfig"
+
 source "fs/notify/Kconfig"
 
 source "fs/quota/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index a88ac48..bcb3048 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -126,3 +126,4 @@ obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
 obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/
+obj-$(CONFIG_FS_EVENTS)		+= events/
diff --git a/fs/events/Kconfig b/fs/events/Kconfig
new file mode 100644
index 0000000..1c60195
--- /dev/null
+++ b/fs/events/Kconfig
@@ -0,0 +1,7 @@
+# Generic Files System events interface
+config FS_EVENTS
+	bool "Generic filesystem events"
+	select NET
+	default y
+	help
+	  Enable generic filesystem events interface
diff --git a/fs/events/Makefile b/fs/events/Makefile
new file mode 100644
index 0000000..9c98337
--- /dev/null
+++ b/fs/events/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Linux Generic File System Event Interface
+#
+
+obj-y := fs_event.o fs_event_netlink.o
diff --git a/fs/events/fs_event.c b/fs/events/fs_event.c
new file mode 100644
index 0000000..1037311
--- /dev/null
+++ b/fs/events/fs_event.c
@@ -0,0 +1,809 @@
+/*
+ * Generic File System Evens Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/nsproxy.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <net/genetlink.h>
+#include "../pnode.h"
+#include "fs_event.h"
+
+static LIST_HEAD(fs_trace_list);
+static DEFINE_MUTEX(fs_trace_lock);
+
+static struct kmem_cache *fs_trace_cachep __read_mostly;
+
+static atomic_t stray_traces = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(trace_wq);
+/*
+ * Threshold notification state bits.
+ * Note the reverse as this refers to the number
+ * of available blocks.
+ */
+#define THRESH_LR_BELOW		0x0001 /* Falling below the lower range */
+#define THRESH_LR_BEYOND	0x0002
+#define THRESH_UR_BELOW		0x0004
+#define THRESH_UR_BEYOND	0x0008 /* Going beyond the upper range */
+
+#define THRESH_LR_ON	(THRESH_LR_BELOW | THRESH_LR_BEYOND)
+#define THRESH_UR_ON	(THRESH_UR_BELOW | THRESH_UR_BEYOND)
+
+#define FS_TRACE_ADD	0x100000
+
+struct fs_trace_entry {
+	struct kref	 count;
+	atomic_t	 active;
+	struct super_block *sb;
+	unsigned int	 notify;
+	struct path	 mnt_path;
+	struct list_head  node;
+
+	struct fs_event_thresh {
+		u64		 avail_space;
+		u64		 lrange;
+		u64		 urange;
+		unsigned int	 state;
+	}		 th;
+	struct rcu_head	 rcu_head;
+	spinlock_t	 lock;
+};
+
+static const match_table_t fs_etypes = {
+	{ FS_EVENT_GENERIC, "G"   },
+	{ FS_EVENT_THRESH,  "T"   },
+	{ 0, NULL },
+};
+
+static inline int fs_trace_query_data(struct super_block *sb,
+				       struct fs_trace_entry *en)
+{
+	if (sb->s_etrace.ops && sb->s_etrace.ops->query) {
+		sb->s_etrace.ops->query(sb, &en->th.avail_space);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static inline void fs_trace_entry_free(struct fs_trace_entry *en)
+{
+	kmem_cache_free(fs_trace_cachep, en);
+}
+
+static void fs_destroy_trace_entry(struct kref *en_ref)
+{
+	struct fs_trace_entry *en = container_of(en_ref,
+					 struct fs_trace_entry, count);
+
+	/* Last reference has been dropped */
+	fs_trace_entry_free(en);
+	atomic_dec(&stray_traces);
+}
+
+static void fs_trace_entry_put(struct fs_trace_entry *en)
+{
+	kref_put(&en->count, fs_destroy_trace_entry);
+}
+
+static void fs_release_trace_entry(struct rcu_head *rcu_head)
+{
+	struct fs_trace_entry *en = container_of(rcu_head,
+						 struct fs_trace_entry,
+						 rcu_head);
+	/*
+	 * As opposed to typical reference drop, this one is being
+	 * called from the rcu callback. This is to make sure all
+	 * readers have managed to safely grab the reference before
+	 * the change to rcu pointer is visible to all and before
+	 * the reference is dropped here.
+	 */
+	fs_trace_entry_put(en);
+}
+
+static void fs_drop_trace_entry(struct fs_trace_entry *en)
+{
+	struct super_block *sb;
+
+	lockdep_assert_held(&fs_trace_lock);
+	/*
+	 * The trace entry might have already been removed
+	 * from the list of active traces with the proper
+	 * ref drop, though it was still in use handling
+	 * one of the fs events. This means that the object
+	 * has been already scheduled for being released.
+	 * So leave...
+	 */
+
+	if (!atomic_add_unless(&en->active, -1, 0))
+		return;
+	/*
+	 * At this point the trace entry is being marked as inactive
+	 * so no new references will be allowed.
+	 * Still it might be floating around somewhere
+	 * so drop the reference when the rcu readers are done.
+	 */
+	spin_lock(&en->lock);
+	list_del(&en->node);
+	sb = en->sb;
+	en->sb = NULL;
+	spin_unlock(&en->lock);
+
+	rcu_assign_pointer(sb->s_etrace.e_priv, NULL);
+	call_rcu(&en->rcu_head, fs_release_trace_entry);
+	/* It's safe now to drop the reference to the super */
+	deactivate_super(sb);
+	atomic_inc(&stray_traces);
+}
+
+static inline
+struct fs_trace_entry *fs_trace_entry_get(struct fs_trace_entry *en)
+{
+	if (en) {
+		if (!kref_get_unless_zero(&en->count))
+			return NULL;
+		/* Don't allow referencing inactive object */
+		if (!atomic_read(&en->active)) {
+			fs_trace_entry_put(en);
+			return NULL;
+		}
+	}
+	return en;
+}
+
+static struct fs_trace_entry *fs_trace_entry_get_rcu(struct super_block *sb)
+{
+	struct fs_trace_entry *en;
+
+	if (!sb)
+		return NULL;
+
+	rcu_read_lock();
+	en = rcu_dereference(sb->s_etrace.e_priv);
+	en = fs_trace_entry_get(en);
+	rcu_read_unlock();
+
+	return en;
+}
+
+static int fs_remove_trace_entry(struct super_block *sb)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return -EINVAL;
+
+	mutex_lock(&fs_trace_lock);
+	fs_drop_trace_entry(en);
+	mutex_unlock(&fs_trace_lock);
+	fs_trace_entry_put(en);
+	return 0;
+}
+
+static void fs_remove_all_traces(void)
+{
+	struct fs_trace_entry *en, *guard;
+
+	mutex_lock(&fs_trace_lock);
+	list_for_each_entry_safe(en, guard, &fs_trace_list, node)
+		fs_drop_trace_entry(en);
+	mutex_unlock(&fs_trace_lock);
+}
+
+static int create_common_msg(struct sk_buff *skb, void *data)
+{
+	struct fs_trace_entry *en = (struct fs_trace_entry *)data;
+	struct super_block *sb = en->sb;
+
+	if (nla_put_u32(skb, FS_NL_A_DEV_MAJOR, MAJOR(sb->s_dev))
+	||  nla_put_u32(skb, FS_NL_A_DEV_MINOR, MINOR(sb->s_dev)))
+		return -EINVAL;
+
+	if (nla_put_u64(skb, FS_NL_A_CAUSED_ID, pid_vnr(task_pid(current))))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int create_thresh_msg(struct sk_buff *skb, void *data)
+{
+	struct fs_trace_entry *en = (struct fs_trace_entry *)data;
+	int ret;
+
+	ret = create_common_msg(skb, data);
+	if (!ret)
+		ret = nla_put_u64(skb, FS_NL_A_DATA, en->th.avail_space);
+	return ret;
+}
+
+static void fs_event_send(struct fs_trace_entry *en, unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64));
+
+	fs_netlink_send_event(size, event_id, create_common_msg, en);
+}
+
+static void fs_event_send_thresh(struct fs_trace_entry *en,
+				  unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64)) * 2;
+
+	fs_netlink_send_event(size, event_id, create_thresh_msg, en);
+}
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+	if (atomic_read(&en->active) && (en->notify & FS_EVENT_GENERIC))
+		fs_event_send(en, event_id);
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_notify);
+
+void fs_event_alloc_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+	s64 count;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+	/*
+	 * we shouldn't drop below 0 here,
+	 * unless there is a sync issue somewhere (?)
+	 */
+	count = en->th.avail_space - ncount;
+	en->th.avail_space = count < 0 ? 0 : count;
+
+	if (en->th.avail_space > en->th.lrange)
+		/* Not 'even' close - leave */
+		goto leave;
+
+	if (en->th.avail_space > en->th.urange) {
+		/* Close enough - the lower range has been reached */
+		if (!(en->th.state & THRESH_LR_BEYOND)) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRBELOW);
+			en->th.state &= ~THRESH_LR_BELOW;
+			en->th.state |= THRESH_LR_BEYOND;
+		}
+		goto leave;
+	}
+	if (!(en->th.state & THRESH_UR_BEYOND)) {
+		fs_event_send_thresh(en, FS_THR_URBELOW);
+		en->th.state &=  ~THRESH_UR_BELOW;
+		en->th.state |= THRESH_UR_BEYOND;
+	}
+
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_alloc_space);
+
+void fs_event_free_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+
+	en->th.avail_space += ncount;
+
+	if (en->th.avail_space > en->th.lrange) {
+		if (!(en->th.state & THRESH_LR_BELOW)
+		&& en->th.state & THRESH_LR_BEYOND) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRABOVE);
+			en->th.state &= ~(THRESH_LR_BEYOND|THRESH_UR_BEYOND);
+			en->th.state |= THRESH_LR_BELOW;
+			goto leave;
+		}
+	}
+	if (en->th.avail_space > en->th.urange) {
+		if (!(en->th.state & THRESH_UR_BELOW)
+		&& en->th.state & THRESH_UR_BEYOND) {
+			/* Notify */
+			fs_event_send_thresh(en, FS_THR_URABOVE);
+			en->th.state &= ~THRESH_UR_BEYOND;
+			en->th.state |= THRESH_UR_BELOW;
+		}
+	}
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_free_space);
+
+void fs_event_mount_dropped(struct vfsmount *mnt)
+{
+	/*
+	 * The mount is dropped but the super might not get released
+	 * at once so there is very small chance some notifications
+	 * will come through.
+	 * Note that the mount being dropped here might belong to a different
+	 * namespace - if this is the case, just ignore it.
+	 */
+	struct fs_trace_entry  *en = fs_trace_entry_get_rcu(mnt->mnt_sb);
+	struct vfsmount *en_mnt;
+
+	if (!en || !atomic_read(&en->active))
+		return;
+	/*
+	 * The entry once set, does not change the mountpoint it's being
+	 * pinned to, so no need to take the lock here.
+	 */
+	en_mnt = en->mnt_path.mnt;
+	if (!(real_mount(mnt)->mnt_ns != (real_mount(en_mnt))->mnt_ns))
+		fs_remove_trace_entry(mnt->mnt_sb);
+	fs_trace_entry_put(en);
+}
+
+static int fs_new_trace_entry(struct path *path, struct fs_event_thresh *thresh,
+				unsigned int nmask)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	struct mount *r_mnt;
+
+	en = kmem_cache_zalloc(fs_trace_cachep, GFP_KERNEL);
+	if (unlikely(!en))
+		return -ENOMEM;
+	/*
+	 * Note that no reference is being taken here for the path as it would
+	 * make the unmount unnecessarily puzzling (due to an extra 'valid'
+	 * reference for the mnt).
+	 * This is *rather* safe as the notification on mount being dropped
+	 * will get called prior to releasing the super block - so right
+	 * in time to perform appropriate clean-up
+	 */
+	r_mnt = real_mount(path->mnt);
+
+	en->mnt_path.dentry = r_mnt->mnt.mnt_root;
+	en->mnt_path.mnt = &r_mnt->mnt;
+
+	sb = path->mnt->mnt_sb;
+	en->sb = sb;
+	/*
+	 * Increase the refcount for sb to mark it's being relied on.
+	 * Note that the reference to path is taken by the caller, so it
+	 * is safe to assume there is at least single active reference
+	 * to super as well.
+	 */
+	atomic_inc(&sb->s_active);
+
+	nmask &= sb->s_etrace.events_cap_mask;
+	if (!nmask)
+		goto leave;
+
+	spin_lock_init(&en->lock);
+	INIT_LIST_HEAD(&en->node);
+
+	en->notify = nmask;
+	memcpy(&en->th, thresh, offsetof(struct fs_event_thresh, state));
+	if (nmask & FS_EVENT_THRESH)
+		fs_trace_query_data(sb, en);
+
+	kref_init(&en->count);
+
+	if (rcu_access_pointer(sb->s_etrace.e_priv) != NULL) {
+		struct fs_trace_entry *prev_en;
+
+		prev_en = fs_trace_entry_get_rcu(sb);
+		if (prev_en) {
+			WARN_ON(prev_en);
+			fs_trace_entry_put(prev_en);
+			goto leave;
+		}
+	}
+	atomic_set(&en->active, 1);
+
+	mutex_lock(&fs_trace_lock);
+	list_add(&en->node, &fs_trace_list);
+	mutex_unlock(&fs_trace_lock);
+
+	rcu_assign_pointer(sb->s_etrace.e_priv, en);
+	synchronize_rcu();
+
+	return 0;
+leave:
+	deactivate_super(sb);
+	kmem_cache_free(fs_trace_cachep, en);
+	return -EINVAL;
+}
+
+static int fs_update_trace_entry(struct path *path,
+				  struct fs_event_thresh *thresh,
+				  unsigned int nmask)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	int extend = nmask & FS_TRACE_ADD;
+	int ret = -EINVAL;
+
+	en = fs_trace_entry_get_rcu(path->mnt->mnt_sb);
+	if (!en)
+		return (extend) ? fs_new_trace_entry(path, thresh, nmask)
+				: -EINVAL;
+
+	if (!atomic_read(&en->active))
+		return -EINVAL;
+
+	nmask &= ~FS_TRACE_ADD;
+
+	spin_lock(&en->lock);
+	sb  = en->sb;
+	if (!sb || !(nmask & sb->s_etrace.events_cap_mask))
+		goto leave;
+
+	if (nmask & FS_EVENT_THRESH) {
+		if (extend) {
+			/* Get the current state */
+			if (!(en->notify & FS_EVENT_THRESH))
+				if (fs_trace_query_data(sb, en))
+					goto leave;
+
+			if (thresh->state & THRESH_LR_ON) {
+				en->th.lrange = thresh->lrange;
+				en->th.state &= ~THRESH_LR_ON;
+			}
+
+			if (thresh->state & THRESH_UR_ON) {
+				en->th.urange = thresh->urange;
+				en->th.state &= ~THRESH_UR_ON;
+			}
+		} else {
+			memset(&en->th, 0, sizeof(en->th));
+		}
+	}
+
+	if (extend)
+		en->notify |= nmask;
+	else
+		en->notify &= ~nmask;
+	ret = 0;
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+	return ret;
+}
+
+static int fs_parse_trace_request(int argc, char **argv)
+{
+	struct fs_event_thresh thresh = {0};
+	struct path path;
+	substring_t args[MAX_OPT_ARGS];
+	unsigned int nmask = FS_TRACE_ADD;
+	int token;
+	char *s;
+	int ret = -EINVAL;
+
+	if (!argc) {
+		fs_remove_all_traces();
+		return 0;
+	}
+
+	s = *(argv);
+	if (*s == '!') {
+		/* Clear the trace entry */
+		nmask &= ~FS_TRACE_ADD;
+		++s;
+	}
+
+	if (kern_path_mountpoint(AT_FDCWD, s, &path, LOOKUP_FOLLOW))
+		return -EINVAL;
+
+	if (!(--argc)) {
+		if (!(nmask & FS_TRACE_ADD))
+			ret = fs_remove_trace_entry(path.mnt->mnt_sb);
+		goto leave;
+	}
+
+repeat:
+	args[0].to = args[0].from = NULL;
+	token = match_token(*(++argv), fs_etypes, args);
+	if (!token && !nmask)
+		goto leave;
+
+	nmask |= token & FS_EVENTS_ALL;
+	--argc;
+	if ((token & FS_EVENT_THRESH)  && (nmask & FS_TRACE_ADD)) {
+		/*
+		 * Get the threshold config data:
+		 * lower range
+		 * upper range
+		 */
+		if (!argc)
+			goto leave;
+
+		ret = kstrtoull(*(++argv), 10, &thresh.lrange);
+		if (ret)
+			goto leave;
+		thresh.state |= THRESH_LR_ON;
+		if ((--argc)) {
+			ret = kstrtoull(*(++argv), 10, &thresh.urange);
+			if (ret)
+				goto leave;
+			thresh.state |= THRESH_UR_ON;
+			--argc;
+		}
+		/* The thresholds are based on number of available blocks */
+		if (thresh.lrange < thresh.urange) {
+			ret = -EINVAL;
+			goto leave;
+		}
+	}
+	if (argc)
+		goto repeat;
+
+	ret = fs_update_trace_entry(&path, &thresh, nmask);
+leave:
+	path_put(&path);
+	return ret;
+}
+
+#define DEFAULT_BUF_SIZE PAGE_SIZE
+
+static ssize_t fs_trace_write(struct file *file, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	char **argv;
+	char *kern_buf, *next, *cfg;
+	size_t size, dcount = 0;
+	int argc;
+
+	if (!count)
+		return 0;
+
+	kern_buf = kmalloc(DEFAULT_BUF_SIZE, GFP_KERNEL);
+	if (!kern_buf)
+		return -ENOMEM;
+
+	while (dcount < count) {
+
+		size = count - dcount;
+		if (size >= DEFAULT_BUF_SIZE)
+			size = DEFAULT_BUF_SIZE - 1;
+		if (copy_from_user(kern_buf, buffer + dcount, size)) {
+			dcount = -EINVAL;
+			goto leave;
+		}
+
+		kern_buf[size] = '\0';
+
+		next = cfg = kern_buf;
+
+		do {
+			next = strchr(cfg, ';');
+			if (next)
+				*next = '\0';
+
+			argv = argv_split(GFP_KERNEL, cfg, &argc);
+			if (!argv) {
+				dcount = -ENOMEM;
+				goto leave;
+			}
+
+			if (fs_parse_trace_request(argc, argv)) {
+				dcount = -EINVAL;
+				argv_free(argv);
+				goto leave;
+			}
+
+			argv_free(argv);
+			if (next)
+				cfg = ++next;
+
+		} while (next);
+		dcount += size;
+	}
+leave:
+	kfree(kern_buf);
+	return dcount;
+}
+
+static void *fs_trace_seq_start(struct seq_file *m, loff_t *pos)
+{
+	mutex_lock(&fs_trace_lock);
+	return seq_list_start(&fs_trace_list, *pos);
+}
+
+static void *fs_trace_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &fs_trace_list, pos);
+}
+
+static void fs_trace_seq_stop(struct seq_file *m, void *v)
+{
+	mutex_unlock(&fs_trace_lock);
+}
+
+static int fs_trace_seq_show(struct seq_file *m, void *v)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	struct mount *r_mnt;
+	const struct match_token *match;
+	unsigned int nmask;
+
+	en = list_entry(v, struct fs_trace_entry, node);
+	/* Do not show the entries outside current mount namespace */
+	r_mnt = real_mount(en->mnt_path.mnt);
+	if (r_mnt->mnt_ns != current->nsproxy->mnt_ns) {
+		if (!__is_local_mountpoint(r_mnt->mnt_mountpoint))
+			return 0;
+	}
+
+	sb = en->sb;
+
+	seq_path(m, &en->mnt_path, "\t\n\\");
+	seq_putc(m, ' ');
+
+	seq_escape(m, sb->s_type->name, " \t\n\\");
+	if (sb->s_subtype && sb->s_subtype[0]) {
+		seq_putc(m, '.');
+		seq_escape(m, sb->s_subtype, " \t\n\\");
+	}
+
+	seq_putc(m, ' ');
+	if (sb->s_op->show_devname) {
+		sb->s_op->show_devname(m, en->mnt_path.mnt->mnt_root);
+	} else {
+		seq_escape(m, r_mnt->mnt_devname ? r_mnt->mnt_devname : "none",
+				" \t\n\\");
+	}
+	seq_puts(m, " (");
+
+	nmask = en->notify;
+	for (match = fs_etypes; match->pattern; ++match) {
+		if (match->token & nmask) {
+			seq_puts(m, match->pattern);
+			nmask &= ~match->token;
+			if (nmask)
+				seq_putc(m, ',');
+		}
+	}
+	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
+	seq_puts(m, ")\n");
+	return 0;
+}
+
+static const struct seq_operations fs_trace_seq_ops = {
+	.start	= fs_trace_seq_start,
+	.next	= fs_trace_seq_next,
+	.stop	= fs_trace_seq_stop,
+	.show	= fs_trace_seq_show,
+};
+
+static int fs_trace_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &fs_trace_seq_ops);
+}
+
+static const struct file_operations fs_trace_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fs_trace_open,
+	.write		= fs_trace_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int fs_trace_init(void)
+{
+	fs_trace_cachep = KMEM_CACHE(fs_trace_entry, 0);
+	if (!fs_trace_cachep)
+		return -EINVAL;
+	init_waitqueue_head(&trace_wq);
+	return 0;
+}
+
+/* VFS support */
+static int fs_trace_fill_super(struct super_block *sb, void *data, int silen)
+{
+	int ret;
+	static struct tree_descr desc[] = {
+		[2] = {
+			.name	= "config",
+			.ops	= &fs_trace_fops,
+			.mode	= S_IWUSR | S_IRUGO,
+		},
+		{""},
+	};
+
+	ret = simple_fill_super(sb, 0x7246332, desc);
+	return !ret ? fs_trace_init() : ret;
+}
+
+static struct dentry *fs_trace_do_mount(struct file_system_type *fs_type,
+		 int ntype, const char *dev_name, void *data)
+{
+	return mount_single(fs_type, ntype, data, fs_trace_fill_super);
+}
+
+static void fs_trace_kill_super(struct super_block *sb)
+{
+	/*
+	 * The rcu_barrier here will/should make sure all call_rcu
+	 * callbacks are completed - still there might be some active
+	 * trace objects in use which can make calling the
+	 * kmem_cache_destroy unsafe. So we wait until all traces
+	 * are finally released.
+	 */
+	fs_remove_all_traces();
+	rcu_barrier();
+	wait_event(trace_wq, !atomic_read(&stray_traces));
+
+	kmem_cache_destroy(fs_trace_cachep);
+	kill_litter_super(sb);
+}
+
+static struct kset	*fs_trace_kset;
+
+static struct file_system_type fs_trace_fstype = {
+	.name		= "fstrace",
+	.mount		= fs_trace_do_mount,
+	.kill_sb	= fs_trace_kill_super,
+};
+
+static void __init fs_trace_vfs_init(void)
+{
+	fs_trace_kset = kset_create_and_add("events", NULL, fs_kobj);
+
+	if (!fs_trace_kset)
+		return;
+
+	if (!register_filesystem(&fs_trace_fstype)) {
+		if (!fs_event_netlink_register())
+			return;
+		unregister_filesystem(&fs_trace_fstype);
+	}
+	kset_unregister(fs_trace_kset);
+}
+
+static int __init fs_trace_evens_init(void)
+{
+	fs_trace_vfs_init();
+	return 0;
+};
+module_init(fs_trace_evens_init);
+
diff --git a/fs/events/fs_event.h b/fs/events/fs_event.h
new file mode 100644
index 0000000..23f24c8
--- /dev/null
+++ b/fs/events/fs_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __GENERIC_FS_EVENTS_H
+#define __GENERIC_FS_EVENTS_H
+
+int  fs_event_netlink_register(void);
+void fs_event_netlink_unregister(void);
+
+#endif /* __GENERIC_FS_EVENTS_H */
diff --git a/fs/events/fs_event_netlink.c b/fs/events/fs_event_netlink.c
new file mode 100644
index 0000000..0c97eb7
--- /dev/null
+++ b/fs/events/fs_event_netlink.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include "fs_event.h"
+
+static const struct genl_multicast_group fs_event_mcgroups[] = {
+	{ .name = FS_EVENTS_MCAST_GRP_NAME, },
+};
+
+static struct genl_family fs_event_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= FS_EVENTS_FAMILY_NAME,
+	.version	= 1,
+	.maxattr	= FS_NL_A_MAX,
+	.mcgrps		= fs_event_mcgroups,
+	.n_mcgrps	= ARRAY_SIZE(fs_event_mcgroups),
+};
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb,  void *data),
+			  void *cbdata)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret = 0;
+
+	if (!size || !compose_msg)
+		return -EINVAL;
+
+	/* Skip if there are no listeners */
+	if (!genl_has_listeners(&fs_event_family, &init_net, 0))
+		return 0;
+
+	if (event_id != FS_EVENT_NONE)
+		size += nla_total_size(sizeof(u32));
+	size += nla_total_size(sizeof(u64));
+	skb = genlmsg_new(size, GFP_NOWAIT);
+
+	if (!skb) {
+		pr_debug("Failed to allocate new FS generic netlink message\n");
+		return -ENOMEM;
+	}
+
+	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+			&fs_event_family, 0, FS_NL_C_EVENT);
+	if (!msg_head)
+		goto cleanup;
+
+	if (event_id != FS_EVENT_NONE)
+		if (nla_put_u32(skb, FS_NL_A_EVENT_ID, event_id))
+			goto cancel;
+
+	ret = compose_msg(skb, cbdata);
+	if (ret)
+		goto cancel;
+
+	genlmsg_end(skb, msg_head);
+	ret = genlmsg_multicast(&fs_event_family, skb, 0, 0, GFP_NOWAIT);
+	if (ret && ret != -ENOBUFS && ret != -ESRCH)
+		goto cleanup;
+
+	return ret;
+
+cancel:
+	genlmsg_cancel(skb, msg_head);
+cleanup:
+	nlmsg_free(skb);
+	return ret;
+}
+EXPORT_SYMBOL(fs_netlink_send_event);
+
+int fs_event_netlink_register(void)
+{
+	int ret;
+
+	ret = genl_register_family(&fs_event_family);
+	if (ret)
+		pr_err("Failed to register FS netlink interface\n");
+	return ret;
+}
+
+void fs_event_netlink_unregister(void)
+{
+	genl_unregister_family(&fs_event_family);
+}
diff --git a/fs/namespace.c b/fs/namespace.c
index 82ef140..ec6e2ef 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1031,6 +1031,7 @@ static void cleanup_mnt(struct mount *mnt)
 	if (unlikely(mnt->mnt_pins.first))
 		mnt_pin_kill(mnt);
 	fsnotify_vfsmount_delete(&mnt->mnt);
+	fs_event_mount_dropped(&mnt->mnt);
 	dput(mnt->mnt.mnt_root);
 	deactivate_super(mnt->mnt.mnt_sb);
 	mnt_free_id(mnt);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b4d71b5..b7dadd9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -263,6 +263,10 @@ struct iattr {
  * Includes for diskquotas.
  */
 #include <linux/quota.h>
+/*
+ * Include for Generic File System Events Interface
+ */
+#include <linux/fs_event.h>
 
 /*
  * Maximum number of layers of fs stack.  Needs to be limited to
@@ -1253,7 +1257,7 @@ struct super_block {
 	struct hlist_node	s_instances;
 	unsigned int		s_quota_types;	/* Bitmask of supported quota types */
 	struct quota_info	s_dquot;	/* Diskquota specific options */
-
+	struct fs_trace_info	s_etrace;
 	struct sb_writers	s_writers;
 
 	char s_id[32];				/* Informational name */
diff --git a/include/linux/fs_event.h b/include/linux/fs_event.h
new file mode 100644
index 0000000..83e22dd
--- /dev/null
+++ b/include/linux/fs_event.h
@@ -0,0 +1,72 @@
+/*
+ * Generic File System Events Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef _LINUX_GENERIC_FS_EVETS_
+#define _LINUX_GENERIC_FS_EVETS_
+#include <net/netlink.h>
+#include <uapi/linux/fs_event.h>
+
+/*
+ * Currently supported event types
+ */
+#define FS_EVENT_GENERIC 0x001
+#define FS_EVENT_THRESH	 0x002
+
+#define FS_EVENTS_ALL  (FS_EVENT_GENERIC | FS_EVENT_THRESH)
+
+struct fs_trace_operations {
+	void (*query)(struct super_block *, u64 *);
+};
+
+struct fs_trace_info {
+	void	__rcu	*e_priv;		 /* READ ONLY */
+	unsigned int	events_cap_mask; /* Supported notifications */
+	const struct fs_trace_operations *ops;
+};
+
+#ifdef CONFIG_FS_EVENTS
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id);
+void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+void fs_event_free_space(struct super_block *sb, u64 ncount);
+void fs_event_mount_dropped(struct vfsmount *mnt);
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb, void *data),
+			  void *cbdata);
+
+#else /* CONFIG_FS_EVENTS */
+
+static inline
+void fs_event_notify(struct super_block *sb, unsigned int event_id) {};
+static inline
+void fs_event_alloc_space(struct super_block *sb, u64 ncount) {};
+static inline
+void fs_event_free_space(struct super_block *sb, u64 ncount) {};
+static inline
+void fs_event_mount_dropped(struct vfsmount *mnt) {};
+
+static inline
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msig)(struct sk_buff *skb, void *data),
+			  void *cbdata)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_FS_EVENTS */
+
+#endif /* _LINUX_GENERIC_FS_EVENTS_ */
+
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 68ceb97..dae0fab 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -129,6 +129,7 @@ header-y += firewire-constants.h
 header-y += flat.h
 header-y += fou.h
 header-y += fs.h
+header-y += fs_event.h
 header-y += fsl_hypervisor.h
 header-y += fuse.h
 header-y += futex.h
diff --git a/include/uapi/linux/fs_event.h b/include/uapi/linux/fs_event.h
new file mode 100644
index 0000000..d8b07da
--- /dev/null
+++ b/include/uapi/linux/fs_event.h
@@ -0,0 +1,58 @@
+/*
+ * Generic netlink support for Generic File System Events Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef _UAPI_LINUX_GENERIC_FS_EVENTS_
+#define _UAPI_LINUX_GENERIC_FS_EVENTS_
+
+#define FS_EVENTS_FAMILY_NAME	 "fs_event"
+#define FS_EVENTS_MCAST_GRP_NAME "fs_event_mc_grp"
+
+/*
+ * Generic netlink attribute types
+ */
+enum {
+	FS_NL_A_NONE,
+	FS_NL_A_EVENT_ID,
+	FS_NL_A_DEV_MAJOR,
+	FS_NL_A_DEV_MINOR,
+	FS_NL_A_CAUSED_ID,
+	FS_NL_A_DATA,
+	__FS_NL_A_MAX,
+};
+#define FS_NL_A_MAX (__FS_NL_A_MAX - 1)
+/*
+ * Generic netlink commands
+ */
+#define FS_NL_C_EVENT		1
+
+/*
+ * Supported set of FS events
+ */
+enum {
+	FS_EVENT_NONE,
+	FS_WARN_ENOSPC,		/* No space left to reserve data blks */
+	FS_WARN_ENOSPC_META,	/* No space left for metadata */
+	FS_THR_LRBELOW,		/* The threshold lower range has been reached */
+	FS_THR_LRABOVE,		/* The threshold lower range re-activcated*/
+	FS_THR_URBELOW,
+	FS_THR_URABOVE,
+	FS_ERR_REMOUNT_RO,	/* The file system has been remounted as RO */
+	FS_ERR_CORRUPTED	/* Critical error - fs corrupted */
+
+};
+
+#endif /* _UAPI_LINUX_GENERIC_FS_EVENTS_ */
+
-- 
1.7.9.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

[RFC v3 2/4] ext4: Add helper function to mark group as corrupted

From: Beata Michalska <hidden>
Date: 2015-06-16 13:09:31

Add ext4_mark_group_corrupted helper function to
simplify the code and to keep the logic in one place.

Signed-off-by: Beata Michalska <redacted>
---
 fs/ext4/balloc.c  |   15 +++------------
 fs/ext4/ext4.h    |    9 +++++++++
 fs/ext4/ialloc.c  |    5 +----
 fs/ext4/mballoc.c |   11 ++---------
 4 files changed, 15 insertions(+), 25 deletions(-)
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 83a6f49..e95b27a 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -193,10 +193,7 @@ static int ext4_init_block_bitmap(struct super_block *sb,
 	 * essentially implementing a per-group read-only flag. */
 	if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
 		grp = ext4_get_group_info(sb, block_group);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
 			int count;
 			count = ext4_free_inodes_count(sb, gdp);
@@ -379,20 +376,14 @@ static void ext4_validate_block_bitmap(struct super_block *sb,
 		ext4_unlock_group(sb, block_group);
 		ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
 			   block_group, blk);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		return;
 	}
 	if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
 			desc, bh))) {
 		ext4_unlock_group(sb, block_group);
 		ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		return;
 	}
 	set_buffer_verified(bh);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f63c3d5..163afe2 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2535,6 +2535,15 @@ static inline spinlock_t *ext4_group_lock_ptr(struct super_block *sb,
 	return bgl_lock_ptr(EXT4_SB(sb)->s_blockgroup_lock, group);
 }
 
+static inline
+void ext4_mark_group_corrupted(struct ext4_sb_info *sbi,
+				struct ext4_group_info *grp)
+{
+	if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
+		percpu_counter_sub(&sbi->s_freeclusters_counter, grp->bb_free);
+	set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+}
+
 /*
  * Returns true if the filesystem is busy enough that attempts to
  * access the block group locks has run into contention.
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index ac644c3..ebe0499 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -79,10 +79,7 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,
 	if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
 		ext4_error(sb, "Checksum bad for group %u", block_group);
 		grp = ext4_get_group_info(sb, block_group);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
 			int count;
 			count = ext4_free_inodes_count(sb, gdp);
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 8d1e602..24a4b6d 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -760,10 +760,7 @@ void ext4_mb_generate_buddy(struct super_block *sb,
 		 * corrupt and update bb_free using bitmap value
 		 */
 		grp->bb_free = free;
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 	}
 	mb_set_largest_free_order(sb, grp);
 
@@ -1448,12 +1445,8 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
 				      "freeing already freed block "
 				      "(bit %u); block bitmap corrupt.",
 				      block);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   e4b->bd_info->bb_free);
 		/* Mark the block group as corrupt. */
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
-			&e4b->bd_info->bb_state);
+		ext4_mark_group_corrupted(sbi, e4b->bd_info);
 		mb_regenerate_buddy(e4b);
 		goto done;
 	}
-- 
1.7.9.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

[RFC v3 4/4] shmem: Add support for generic FS events

From: Beata Michalska <hidden>
Date: 2015-06-16 13:09:33

Add support for the generic FS events interface
covering threshold notifiactions and the ENOSPC
warning.

Signed-off-by: Beata Michalska <redacted>
---
 mm/shmem.c |   33 ++++++++++++++++++++++++++++++---
 1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index cf2d0ca..a044d12 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -201,6 +201,7 @@ static int shmem_reserve_inode(struct super_block *sb)
 		spin_lock(&sbinfo->stat_lock);
 		if (!sbinfo->free_inodes) {
 			spin_unlock(&sbinfo->stat_lock);
+			fs_event_notify(sb, FS_WARN_ENOSPC);
 			return -ENOSPC;
 		}
 		sbinfo->free_inodes--;
@@ -239,8 +240,10 @@ static void shmem_recalc_inode(struct inode *inode)
 	freed = info->alloced - info->swapped - inode->i_mapping->nrpages;
 	if (freed > 0) {
 		struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
-		if (sbinfo->max_blocks)
+		if (sbinfo->max_blocks) {
 			percpu_counter_add(&sbinfo->used_blocks, -freed);
+			fs_event_free_space(inode->i_sb, freed);
+		}
 		info->alloced -= freed;
 		inode->i_blocks -= freed * BLOCKS_PER_PAGE;
 		shmem_unacct_blocks(info->flags, freed);
@@ -1164,6 +1167,7 @@ repeat:
 				goto unacct;
 			}
 			percpu_counter_inc(&sbinfo->used_blocks);
+			fs_event_alloc_space(inode->i_sb, 1);
 		}
 
 		page = shmem_alloc_page(gfp, info, index);
@@ -1245,8 +1249,10 @@ trunc:
 	spin_unlock(&info->lock);
 decused:
 	sbinfo = SHMEM_SB(inode->i_sb);
-	if (sbinfo->max_blocks)
+	if (sbinfo->max_blocks) {
 		percpu_counter_add(&sbinfo->used_blocks, -1);
+		fs_event_free_space(inode->i_sb, 1);
+	}
 unacct:
 	shmem_unacct_blocks(info->flags, 1);
 failed:
@@ -1258,12 +1264,16 @@ unlock:
 		unlock_page(page);
 		page_cache_release(page);
 	}
-	if (error == -ENOSPC && !once++) {
+	if (error == -ENOSPC) {
+		if (!once++) {
 		info = SHMEM_I(inode);
 		spin_lock(&info->lock);
 		shmem_recalc_inode(inode);
 		spin_unlock(&info->lock);
 		goto repeat;
+		} else {
+			fs_event_notify(inode->i_sb, FS_WARN_ENOSPC);
+		}
 	}
 	if (error == -EEXIST)	/* from above or from radix_tree_insert */
 		goto repeat;
@@ -2729,12 +2739,26 @@ static int shmem_encode_fh(struct inode *inode, __u32 *fh, int *len,
 	return 1;
 }
 
+static void shmem_trace_query(struct super_block *sb, u64 *ncount)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+
+	if (sbinfo->max_blocks)
+		*ncount = sbinfo->max_blocks -
+			percpu_counter_sum(&sbinfo->used_blocks);
+
+}
+
 static const struct export_operations shmem_export_ops = {
 	.get_parent     = shmem_get_parent,
 	.encode_fh      = shmem_encode_fh,
 	.fh_to_dentry	= shmem_fh_to_dentry,
 };
 
+static const struct fs_trace_operations shmem_trace_ops = {
+	.query	= shmem_trace_query,
+};
+
 static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo,
 			       bool remount)
 {
@@ -3020,6 +3044,9 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
 		sb->s_flags |= MS_NOUSER;
 	}
 	sb->s_export_op = &shmem_export_ops;
+	sb->s_etrace.ops = &shmem_trace_ops;
+	sb->s_etrace.events_cap_mask = FS_EVENTS_ALL;
+
 	sb->s_flags |= MS_NOSEC;
 #else
 	sb->s_flags |= MS_NOUSER;
-- 
1.7.9.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 4/4] shmem: Add support for generic FS events

From: Leon Romanovsky <hidden>
Date: 2015-06-17 06:08:41

        }
-       if (error == -ENOSPC && !once++) {
+       if (error == -ENOSPC) {
+               if (!once++) {
                info = SHMEM_I(inode);
                spin_lock(&info->lock);
                shmem_recalc_inode(inode);
                spin_unlock(&info->lock);
                goto repeat;
+               } else {
+                       fs_event_notify(inode->i_sb, FS_WARN_ENOSPC);
+               }
        }
Very minor remark, please fix indentation.

Re: [RFC v3 4/4] shmem: Add support for generic FS events

From: Beata Michalska <hidden>
Date: 2015-06-17 09:23:40

On 06/17/2015 08:08 AM, Leon Romanovsky wrote:
quoted
        }
-       if (error == -ENOSPC && !once++) {
+       if (error == -ENOSPC) {
+               if (!once++) {
                info = SHMEM_I(inode);
                spin_lock(&info->lock);
                shmem_recalc_inode(inode);
                spin_unlock(&info->lock);
                goto repeat;
+               } else {
+                       fs_event_notify(inode->i_sb, FS_WARN_ENOSPC);
+               }
        }
Very minor remark, please fix indentation.
I will, thank You.

BR
Beata

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Dave Chinner <david@fromorbit.com>
Date: 2015-06-17 23:06:05

On Tue, Jun 16, 2015 at 03:09:30PM +0200, Beata Michalska wrote:
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
This has massive scalability problems:
+ 4.3 Threshold notifications:
+
+ #include <linux/fs_event.h>
+ void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+ void fs_event_free_space(struct super_block *sb, u64 ncount);
+
+ Each filesystme supporting the threshold notifications should call
+ fs_event_alloc_space/fs_event_free_space respectively whenever the
+ amount of available blocks changes.
+ - sb:     the filesystem's super block
+ - ncount: number of blocks being acquired/released
... here.
+ Note that to properly handle the threshold notifications the fs events
+ interface needs to be kept up to date by the filesystems. Each should
+ register fs_trace_operations to enable querying the current number of
+ available blocks.
Have you noticed that the filesystems have percpu counters for
tracking global space usage? There's good reason for that - taking a
spinlock in such a hot accounting path causes severe contention.
+static void fs_event_send(struct fs_trace_entry *en, unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64));
+
+	fs_netlink_send_event(size, event_id, create_common_msg, en);
+}
+
+static void fs_event_send_thresh(struct fs_trace_entry *en,
+				  unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64)) * 2;
+
+	fs_netlink_send_event(size, event_id, create_thresh_msg, en);
+}
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+	if (atomic_read(&en->active) && (en->notify & FS_EVENT_GENERIC))
+		fs_event_send(en, event_id);
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_notify);
+
+void fs_event_alloc_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+	s64 count;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
Adds an atomic write to get the trace entry,
+	spin_lock(&en->lock);
a spin lock to lock the entry,

+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+	/*
+	 * we shouldn't drop below 0 here,
+	 * unless there is a sync issue somewhere (?)
+	 */
+	count = en->th.avail_space - ncount;
+	en->th.avail_space = count < 0 ? 0 : count;
+
+	if (en->th.avail_space > en->th.lrange)
+		/* Not 'even' close - leave */
+		goto leave;
+
+	if (en->th.avail_space > en->th.urange) {
+		/* Close enough - the lower range has been reached */
+		if (!(en->th.state & THRESH_LR_BEYOND)) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRBELOW);
+			en->th.state &= ~THRESH_LR_BELOW;
+			en->th.state |= THRESH_LR_BEYOND;
+		}
+		goto leave;
Then puts the entire netlink send path inside this spinlock, which
includes memory allocation and all sorts of non-filesystem code
paths. And it may be inside critical filesystem locks as well....

Apart from the serialisation problem of the locking, adding
memory allocation and the network send path to filesystem code
that is effectively considered "innermost" filesystem code is going
to have all sorts of problems for various filesystems. In the XFS
case, we simply cannot execute this sort of function in the places
where we update global space accounting.

As it is, I think the basic concept of separate tracking of free
space if fundamentally flawed. What I think needs to be done is that
filesystems need access to the thresholds for events, and then the
filesystems call fs_event_send_thresh() themselves from appropriate
contexts (ie. without compromising locking, scalability, memory
allocation recursion constraints, etc).

e.g. instead of tracking every change in free space, a filesystem
might execute this once every few seconds from a workqueue:

	event = fs_event_need_space_warning(sb, <fs_free_space>)
	if (event)
		fs_event_send_thresh(sb, event);

User still gets warnings about space usage, but there's no runtime
overhead or problems with lock/memory allocation contexts, etc.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-06-18 08:25:08

Hi,

On 06/18/2015 01:06 AM, Dave Chinner wrote:
On Tue, Jun 16, 2015 at 03:09:30PM +0200, Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
This has massive scalability problems:
quoted
+ 4.3 Threshold notifications:
+
+ #include <linux/fs_event.h>
+ void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+ void fs_event_free_space(struct super_block *sb, u64 ncount);
+
+ Each filesystme supporting the threshold notifications should call
+ fs_event_alloc_space/fs_event_free_space respectively whenever the
+ amount of available blocks changes.
+ - sb:     the filesystem's super block
+ - ncount: number of blocks being acquired/released
... here.
quoted
+ Note that to properly handle the threshold notifications the fs events
+ interface needs to be kept up to date by the filesystems. Each should
+ register fs_trace_operations to enable querying the current number of
+ available blocks.
Have you noticed that the filesystems have percpu counters for
tracking global space usage? There's good reason for that - taking a
spinlock in such a hot accounting path causes severe contention.
quoted
+static void fs_event_send(struct fs_trace_entry *en, unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64));
+
+	fs_netlink_send_event(size, event_id, create_common_msg, en);
+}
+
+static void fs_event_send_thresh(struct fs_trace_entry *en,
+				  unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64)) * 2;
+
+	fs_netlink_send_event(size, event_id, create_thresh_msg, en);
+}
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+	if (atomic_read(&en->active) && (en->notify & FS_EVENT_GENERIC))
+		fs_event_send(en, event_id);
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_notify);
+
+void fs_event_alloc_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+	s64 count;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
Adds an atomic write to get the trace entry,
quoted
+	spin_lock(&en->lock);
a spin lock to lock the entry,

quoted
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+	/*
+	 * we shouldn't drop below 0 here,
+	 * unless there is a sync issue somewhere (?)
+	 */
+	count = en->th.avail_space - ncount;
+	en->th.avail_space = count < 0 ? 0 : count;
+
+	if (en->th.avail_space > en->th.lrange)
+		/* Not 'even' close - leave */
+		goto leave;
+
+	if (en->th.avail_space > en->th.urange) {
+		/* Close enough - the lower range has been reached */
+		if (!(en->th.state & THRESH_LR_BEYOND)) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRBELOW);
+			en->th.state &= ~THRESH_LR_BELOW;
+			en->th.state |= THRESH_LR_BEYOND;
+		}
+		goto leave;
Then puts the entire netlink send path inside this spinlock, which
includes memory allocation and all sorts of non-filesystem code
paths. And it may be inside critical filesystem locks as well....

Apart from the serialisation problem of the locking, adding
memory allocation and the network send path to filesystem code
that is effectively considered "innermost" filesystem code is going
to have all sorts of problems for various filesystems. In the XFS
case, we simply cannot execute this sort of function in the places
where we update global space accounting.

As it is, I think the basic concept of separate tracking of free
space if fundamentally flawed. What I think needs to be done is that
filesystems need access to the thresholds for events, and then the
filesystems call fs_event_send_thresh() themselves from appropriate
contexts (ie. without compromising locking, scalability, memory
allocation recursion constraints, etc).

e.g. instead of tracking every change in free space, a filesystem
might execute this once every few seconds from a workqueue:

	event = fs_event_need_space_warning(sb, <fs_free_space>)
	if (event)
		fs_event_send_thresh(sb, event);

User still gets warnings about space usage, but there's no runtime
overhead or problems with lock/memory allocation contexts, etc.

Cheers,

Dave.
Having fs to keep a firm hand on thresholds limits would indeed be
far more sane approach though that would require each fs to
add support for that and handle most of it on their own. Avoiding
this was the main rationale behind this rfc.

If fs people agree to that, I'll be more than willing to drop this
in favour of the per-fs tracking solution. 
Personally, I hope they will.


Best Regards
Beata 

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Kinglong Mee <hidden>
Date: 2015-06-18 11:17:21

On 6/16/2015 9:09 PM, Beata Michalska wrote:
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.
... snip ...
+
+Sample request could look like the following:
+
+ echo /sample/mount/point G T 710000 500000 > /sys/fs/events/config
+
+Multiple request might be specified provided they are separated with semicolon.
+
+The configuration itself might be modified at any time. One can add/remove
+particular event types for given fielsystem, modify the threshold levels,
+and remove single or all entries from the 'config' file.
+
+ - Adding new event type:
+
+ $ echo MOUNT EVENT_TYPE > /sys/fs/events/config
+
+(Note that is is enough to provide the event type to be enabled without
Should be "Note that it is ... " here ?
+the already set ones.)
+
+ - Removing event type:
+
+ $ echo '!MOUNT EVENT_TYPE' > /sys/fs/events/config
+
+ - Updating threshold limits:
+
+ $ echo MOUNT T L1 L2 > /sys/fs/events/config
+
+ - Removing single entry:
+
+ $ echo '!MOUNT' > /sys/fs/events/config
+
+ - Removing all entries:
+
+ $ echo > /sys/fs/events/config
+
+Reading the file will list all registered entries with their current set-up
+along with some additional info like the filesystem type and the backing device
+name if available.
+
+Final, though a very important note on the configuration: when and if the
+actual events are being triggered falls way beyond the scope of the generic
+filesystem events interface. It is up to a particular filesystem
+implementation which events are to be supported - if any at all. So if
+given filesystem does not support the event notifications, an attempt to
+enable those through 'config' file will fail.
+
+
+3. The generic netlink interface support:
+=========================================
+
+Whenever an event notification is triggered (by given filesystem) the current
+configuration is being validated to decide whether a userpsace notification
+should be launched. If there has been no request (in a mean of 'config' file
+entry) for given event, one will be silently disregarded. If, on the other
+hand, someone is 'watching' given filesystem for specific events, a generic
+netlink message will be sent. A dedicated multicast group has been provided
+solely for this purpose so in order to receive such notifications, one should
+subscribe to this new multicast group. As for now only the init network
+namespace is being supported.
+
+3.1 Message format
+
+The FS_NL_C_EVENT shall be stored within the generic netlink message header
+as the command field. The message payload will provide more detailed info:
+the backing device major and minor numbers, the event code and the id of
+the process which action led to the event occurrence. In case of threshold
+notifications, the current number of available blocks will be included
+in the payload as well.
+
+
+	 0                   1                   2                   3
+	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|	            NETLINK MESSAGE HEADER			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 		GENERIC NETLINK MESSAGE HEADER   		|
+	| 	   (with FS_NL_C_EVENT as genlmsghdr cdm field)		|
cmd, not cdm.
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 	      Optional user specific message header		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|		   GENERIC MESSAGE PAYLOAD:			|
+	+---------------------------------------------------------------+
+	| 		  FS_NL_A_EVENT_ID  (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|	  	  FS_NL_A_DEV_MAJOR (NLA_U32)			|
+	+---------------------------------------------------------------+
+	| 	  	  FS_NL_A_DEV_MINOR (NLA_U32) 			|
+	+---------------------------------------------------------------+
+	|  		  FS_NL_A_CAUSED_ID (NLA_U32)			|
Should be NLA_U64 ? The following uses as, 

+	if (nla_put_u64(skb, FS_NL_A_CAUSED_ID, pid_vnr(task_pid(current))))
+		return -EINVAL;

Also, I'd like FS_NL_A_CAUSED_PID than FS_NL_A_CAUSED_ID.
+	+---------------------------------------------------------------+
+	|  		    FS_NL_A_DATA (NLA_U64)			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+The above figure is based on:
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto#Message_Format
+
+
... snip... 
+	seq_putc(m, ' ');
+	if (sb->s_op->show_devname) {
+		sb->s_op->show_devname(m, en->mnt_path.mnt->mnt_root);
+	} else {
+		seq_escape(m, r_mnt->mnt_devname ? r_mnt->mnt_devname : "none",
+				" \t\n\\");
+	}
+	seq_puts(m, " (");
+
+	nmask = en->notify;
+	for (match = fs_etypes; match->pattern; ++match) {
+		if (match->token & nmask) {
+			seq_puts(m, match->pattern);
Print here is better.

if (match->pattern & FS_EVENT_THRESH)
	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
+			nmask &= ~match->token;
+			if (nmask)
+				seq_putc(m, ',');
+		}
+	}
+	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
Don't print the lrange/urange (always be zero) when without FS_EVENT_THRESH.
quoted hunk
+	seq_puts(m, ")\n");
+	return 0;
+}
+
+static const struct seq_operations fs_trace_seq_ops = {
+	.start	= fs_trace_seq_start,
+	.next	= fs_trace_seq_next,
+	.stop	= fs_trace_seq_stop,
+	.show	= fs_trace_seq_show,
+};
+
+static int fs_trace_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &fs_trace_seq_ops);
+}
+
+static const struct file_operations fs_trace_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fs_trace_open,
+	.write		= fs_trace_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int fs_trace_init(void)
+{
+	fs_trace_cachep = KMEM_CACHE(fs_trace_entry, 0);
+	if (!fs_trace_cachep)
+		return -EINVAL;
+	init_waitqueue_head(&trace_wq);
+	return 0;
+}
+
+/* VFS support */
+static int fs_trace_fill_super(struct super_block *sb, void *data, int silen)
+{
+	int ret;
+	static struct tree_descr desc[] = {
+		[2] = {
+			.name	= "config",
+			.ops	= &fs_trace_fops,
+			.mode	= S_IWUSR | S_IRUGO,
+		},
+		{""},
+	};
+
+	ret = simple_fill_super(sb, 0x7246332, desc);
+	return !ret ? fs_trace_init() : ret;
+}
+
+static struct dentry *fs_trace_do_mount(struct file_system_type *fs_type,
+		 int ntype, const char *dev_name, void *data)
+{
+	return mount_single(fs_type, ntype, data, fs_trace_fill_super);
+}
+
+static void fs_trace_kill_super(struct super_block *sb)
+{
+	/*
+	 * The rcu_barrier here will/should make sure all call_rcu
+	 * callbacks are completed - still there might be some active
+	 * trace objects in use which can make calling the
+	 * kmem_cache_destroy unsafe. So we wait until all traces
+	 * are finally released.
+	 */
+	fs_remove_all_traces();
+	rcu_barrier();
+	wait_event(trace_wq, !atomic_read(&stray_traces));
+
+	kmem_cache_destroy(fs_trace_cachep);
+	kill_litter_super(sb);
+}
+
+static struct kset	*fs_trace_kset;
+
+static struct file_system_type fs_trace_fstype = {
+	.name		= "fstrace",
+	.mount		= fs_trace_do_mount,
+	.kill_sb	= fs_trace_kill_super,
+};
+
+static void __init fs_trace_vfs_init(void)
+{
+	fs_trace_kset = kset_create_and_add("events", NULL, fs_kobj);
+
+	if (!fs_trace_kset)
+		return;
+
+	if (!register_filesystem(&fs_trace_fstype)) {
+		if (!fs_event_netlink_register())
+			return;
+		unregister_filesystem(&fs_trace_fstype);
+	}
+	kset_unregister(fs_trace_kset);
+}
+
+static int __init fs_trace_evens_init(void)
+{
+	fs_trace_vfs_init();
+	return 0;
+};
+module_init(fs_trace_evens_init);
+
diff --git a/fs/events/fs_event.h b/fs/events/fs_event.h
new file mode 100644
index 0000000..23f24c8
--- /dev/null
+++ b/fs/events/fs_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __GENERIC_FS_EVENTS_H
+#define __GENERIC_FS_EVENTS_H
+
+int  fs_event_netlink_register(void);
+void fs_event_netlink_unregister(void);
+
+#endif /* __GENERIC_FS_EVENTS_H */
diff --git a/fs/events/fs_event_netlink.c b/fs/events/fs_event_netlink.c
new file mode 100644
index 0000000..0c97eb7
--- /dev/null
+++ b/fs/events/fs_event_netlink.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include "fs_event.h"
+
+static const struct genl_multicast_group fs_event_mcgroups[] = {
+	{ .name = FS_EVENTS_MCAST_GRP_NAME, },
+};
+
+static struct genl_family fs_event_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= FS_EVENTS_FAMILY_NAME,
+	.version	= 1,
+	.maxattr	= FS_NL_A_MAX,
+	.mcgrps		= fs_event_mcgroups,
+	.n_mcgrps	= ARRAY_SIZE(fs_event_mcgroups),
+};
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb,  void *data),
+			  void *cbdata)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret = 0;
+
+	if (!size || !compose_msg)
+		return -EINVAL;
+
+	/* Skip if there are no listeners */
+	if (!genl_has_listeners(&fs_event_family, &init_net, 0))
+		return 0;
+
+	if (event_id != FS_EVENT_NONE)
+		size += nla_total_size(sizeof(u32));
+	size += nla_total_size(sizeof(u64));
What is this for ?

thanks
Kinglong Mee

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-06-18 14:50:11

Hi,

On 06/18/2015 01:17 PM, Kinglong Mee wrote:
On 6/16/2015 9:09 PM, Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.
... snip ...
quoted
+
+Sample request could look like the following:
+
+ echo /sample/mount/point G T 710000 500000 > /sys/fs/events/config
+
+Multiple request might be specified provided they are separated with semicolon.
+
+The configuration itself might be modified at any time. One can add/remove
+particular event types for given fielsystem, modify the threshold levels,
+and remove single or all entries from the 'config' file.
+
+ - Adding new event type:
+
+ $ echo MOUNT EVENT_TYPE > /sys/fs/events/config
+
+(Note that is is enough to provide the event type to be enabled without
Should be "Note that it is ... " here ?
Right
quoted
+the already set ones.)
+
+ - Removing event type:
+
+ $ echo '!MOUNT EVENT_TYPE' > /sys/fs/events/config
+
+ - Updating threshold limits:
+
+ $ echo MOUNT T L1 L2 > /sys/fs/events/config
+
+ - Removing single entry:
+
+ $ echo '!MOUNT' > /sys/fs/events/config
+
+ - Removing all entries:
+
+ $ echo > /sys/fs/events/config
+
+Reading the file will list all registered entries with their current set-up
+along with some additional info like the filesystem type and the backing device
+name if available.
+
+Final, though a very important note on the configuration: when and if the
+actual events are being triggered falls way beyond the scope of the generic
+filesystem events interface. It is up to a particular filesystem
+implementation which events are to be supported - if any at all. So if
+given filesystem does not support the event notifications, an attempt to
+enable those through 'config' file will fail.
+
+
+3. The generic netlink interface support:
+=========================================
+
+Whenever an event notification is triggered (by given filesystem) the current
+configuration is being validated to decide whether a userpsace notification
+should be launched. If there has been no request (in a mean of 'config' file
+entry) for given event, one will be silently disregarded. If, on the other
+hand, someone is 'watching' given filesystem for specific events, a generic
+netlink message will be sent. A dedicated multicast group has been provided
+solely for this purpose so in order to receive such notifications, one should
+subscribe to this new multicast group. As for now only the init network
+namespace is being supported.
+
+3.1 Message format
+
+The FS_NL_C_EVENT shall be stored within the generic netlink message header
+as the command field. The message payload will provide more detailed info:
+the backing device major and minor numbers, the event code and the id of
+the process which action led to the event occurrence. In case of threshold
+notifications, the current number of available blocks will be included
+in the payload as well.
+
+
+	 0                   1                   2                   3
+	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|	            NETLINK MESSAGE HEADER			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 		GENERIC NETLINK MESSAGE HEADER   		|
+	| 	   (with FS_NL_C_EVENT as genlmsghdr cdm field)		|
cmd, not cdm.
ditto
quoted
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 	      Optional user specific message header		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|		   GENERIC MESSAGE PAYLOAD:			|
+	+---------------------------------------------------------------+
+	| 		  FS_NL_A_EVENT_ID  (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|	  	  FS_NL_A_DEV_MAJOR (NLA_U32)			|
+	+---------------------------------------------------------------+
+	| 	  	  FS_NL_A_DEV_MINOR (NLA_U32) 			|
+	+---------------------------------------------------------------+
+	|  		  FS_NL_A_CAUSED_ID (NLA_U32)			|
Should be NLA_U64 ? The following uses as, 

+	if (nla_put_u64(skb, FS_NL_A_CAUSED_ID, pid_vnr(task_pid(current))))
+		return -EINVAL;
Yes, or nla_put_u32 - either way my bad
Also, I'd like FS_NL_A_CAUSED_PID than FS_NL_A_CAUSED_ID.
Alright
quoted
+	+---------------------------------------------------------------+
+	|  		    FS_NL_A_DATA (NLA_U64)			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+The above figure is based on:
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto#Message_Format
+
+
... snip... 
quoted
+	seq_putc(m, ' ');
+	if (sb->s_op->show_devname) {
+		sb->s_op->show_devname(m, en->mnt_path.mnt->mnt_root);
+	} else {
+		seq_escape(m, r_mnt->mnt_devname ? r_mnt->mnt_devname : "none",
+				" \t\n\\");
+	}
+	seq_puts(m, " (");
+
+	nmask = en->notify;
+	for (match = fs_etypes; match->pattern; ++match) {
+		if (match->token & nmask) {
+			seq_puts(m, match->pattern);
Print here is better.

if (match->pattern & FS_EVENT_THRESH)
	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
quoted
+			nmask &= ~match->token;
+			if (nmask)
+				seq_putc(m, ',');
+		}
+	}
+	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
Don't print the lrange/urange (always be zero) when without FS_EVENT_THRESH.
ditto
quoted
+	seq_puts(m, ")\n");
+	return 0;
+}
+
+static const struct seq_operations fs_trace_seq_ops = {
+	.start	= fs_trace_seq_start,
+	.next	= fs_trace_seq_next,
+	.stop	= fs_trace_seq_stop,
+	.show	= fs_trace_seq_show,
+};
+
+static int fs_trace_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &fs_trace_seq_ops);
+}
+
+static const struct file_operations fs_trace_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fs_trace_open,
+	.write		= fs_trace_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int fs_trace_init(void)
+{
+	fs_trace_cachep = KMEM_CACHE(fs_trace_entry, 0);
+	if (!fs_trace_cachep)
+		return -EINVAL;
+	init_waitqueue_head(&trace_wq);
+	return 0;
+}
+
+/* VFS support */
+static int fs_trace_fill_super(struct super_block *sb, void *data, int silen)
+{
+	int ret;
+	static struct tree_descr desc[] = {
+		[2] = {
+			.name	= "config",
+			.ops	= &fs_trace_fops,
+			.mode	= S_IWUSR | S_IRUGO,
+		},
+		{""},
+	};
+
+	ret = simple_fill_super(sb, 0x7246332, desc);
+	return !ret ? fs_trace_init() : ret;
+}
+
+static struct dentry *fs_trace_do_mount(struct file_system_type *fs_type,
+		 int ntype, const char *dev_name, void *data)
+{
+	return mount_single(fs_type, ntype, data, fs_trace_fill_super);
+}
+
+static void fs_trace_kill_super(struct super_block *sb)
+{
+	/*
+	 * The rcu_barrier here will/should make sure all call_rcu
+	 * callbacks are completed - still there might be some active
+	 * trace objects in use which can make calling the
+	 * kmem_cache_destroy unsafe. So we wait until all traces
+	 * are finally released.
+	 */
+	fs_remove_all_traces();
+	rcu_barrier();
+	wait_event(trace_wq, !atomic_read(&stray_traces));
+
+	kmem_cache_destroy(fs_trace_cachep);
+	kill_litter_super(sb);
+}
+
+static struct kset	*fs_trace_kset;
+
+static struct file_system_type fs_trace_fstype = {
+	.name		= "fstrace",
+	.mount		= fs_trace_do_mount,
+	.kill_sb	= fs_trace_kill_super,
+};
+
+static void __init fs_trace_vfs_init(void)
+{
+	fs_trace_kset = kset_create_and_add("events", NULL, fs_kobj);
+
+	if (!fs_trace_kset)
+		return;
+
+	if (!register_filesystem(&fs_trace_fstype)) {
+		if (!fs_event_netlink_register())
+			return;
+		unregister_filesystem(&fs_trace_fstype);
+	}
+	kset_unregister(fs_trace_kset);
+}
+
+static int __init fs_trace_evens_init(void)
+{
+	fs_trace_vfs_init();
+	return 0;
+};
+module_init(fs_trace_evens_init);
+
diff --git a/fs/events/fs_event.h b/fs/events/fs_event.h
new file mode 100644
index 0000000..23f24c8
--- /dev/null
+++ b/fs/events/fs_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __GENERIC_FS_EVENTS_H
+#define __GENERIC_FS_EVENTS_H
+
+int  fs_event_netlink_register(void);
+void fs_event_netlink_unregister(void);
+
+#endif /* __GENERIC_FS_EVENTS_H */
diff --git a/fs/events/fs_event_netlink.c b/fs/events/fs_event_netlink.c
new file mode 100644
index 0000000..0c97eb7
--- /dev/null
+++ b/fs/events/fs_event_netlink.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include "fs_event.h"
+
+static const struct genl_multicast_group fs_event_mcgroups[] = {
+	{ .name = FS_EVENTS_MCAST_GRP_NAME, },
+};
+
+static struct genl_family fs_event_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= FS_EVENTS_FAMILY_NAME,
+	.version	= 1,
+	.maxattr	= FS_NL_A_MAX,
+	.mcgrps		= fs_event_mcgroups,
+	.n_mcgrps	= ARRAY_SIZE(fs_event_mcgroups),
+};
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb,  void *data),
+			  void *cbdata)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret = 0;
+
+	if (!size || !compose_msg)
+		return -EINVAL;
+
+	/* Skip if there are no listeners */
+	if (!genl_has_listeners(&fs_event_family, &init_net, 0))
+		return 0;
+
+	if (event_id != FS_EVENT_NONE)
+		size += nla_total_size(sizeof(u32));
+	size += nla_total_size(sizeof(u64));
What is this for ?
This should actually get removed :)
thanks
Kinglong Mee
Thank You,

Best Regards
Beata

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Dave Chinner <david@fromorbit.com>
Date: 2015-06-19 00:03:41

On Thu, Jun 18, 2015 at 10:25:08AM +0200, Beata Michalska wrote:
On 06/18/2015 01:06 AM, Dave Chinner wrote:
quoted
On Tue, Jun 16, 2015 at 03:09:30PM +0200, Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
This has massive scalability problems:
....
quoted
Have you noticed that the filesystems have percpu counters for
tracking global space usage? There's good reason for that - taking a
spinlock in such a hot accounting path causes severe contention.
....
quoted
Then puts the entire netlink send path inside this spinlock, which
includes memory allocation and all sorts of non-filesystem code
paths. And it may be inside critical filesystem locks as well....

Apart from the serialisation problem of the locking, adding
memory allocation and the network send path to filesystem code
that is effectively considered "innermost" filesystem code is going
to have all sorts of problems for various filesystems. In the XFS
case, we simply cannot execute this sort of function in the places
where we update global space accounting.

As it is, I think the basic concept of separate tracking of free
space if fundamentally flawed. What I think needs to be done is that
filesystems need access to the thresholds for events, and then the
filesystems call fs_event_send_thresh() themselves from appropriate
contexts (ie. without compromising locking, scalability, memory
allocation recursion constraints, etc).

e.g. instead of tracking every change in free space, a filesystem
might execute this once every few seconds from a workqueue:

	event = fs_event_need_space_warning(sb, <fs_free_space>)
	if (event)
		fs_event_send_thresh(sb, event);

User still gets warnings about space usage, but there's no runtime
overhead or problems with lock/memory allocation contexts, etc.
Having fs to keep a firm hand on thresholds limits would indeed be
far more sane approach though that would require each fs to
add support for that and handle most of it on their own. Avoiding
quoted
this was the main rationale behind this rfc.
If fs people agree to that, I'll be more than willing to drop this
in favour of the per-fs tracking solution. 
Personally, I hope they will.
I was hoping that you'd think a little more about my suggestion and
work out how to do background threshold event detection generically.
I kind of left it as "an exercise for the reader" because it seems
obvious to me.

Hint: ->statfs allows you to get the total, free and used space
from filesystems in a generic manner.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-06-19 17:28:11

On 06/19/2015 02:03 AM, Dave Chinner wrote:
On Thu, Jun 18, 2015 at 10:25:08AM +0200, Beata Michalska wrote:
quoted
On 06/18/2015 01:06 AM, Dave Chinner wrote:
quoted
On Tue, Jun 16, 2015 at 03:09:30PM +0200, Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
This has massive scalability problems:
....
quoted
quoted
Have you noticed that the filesystems have percpu counters for
tracking global space usage? There's good reason for that - taking a
spinlock in such a hot accounting path causes severe contention.
....
quoted
quoted
Then puts the entire netlink send path inside this spinlock, which
includes memory allocation and all sorts of non-filesystem code
paths. And it may be inside critical filesystem locks as well....

Apart from the serialisation problem of the locking, adding
memory allocation and the network send path to filesystem code
that is effectively considered "innermost" filesystem code is going
to have all sorts of problems for various filesystems. In the XFS
case, we simply cannot execute this sort of function in the places
where we update global space accounting.

As it is, I think the basic concept of separate tracking of free
space if fundamentally flawed. What I think needs to be done is that
filesystems need access to the thresholds for events, and then the
filesystems call fs_event_send_thresh() themselves from appropriate
contexts (ie. without compromising locking, scalability, memory
allocation recursion constraints, etc).

e.g. instead of tracking every change in free space, a filesystem
might execute this once every few seconds from a workqueue:

	event = fs_event_need_space_warning(sb, <fs_free_space>)
	if (event)
		fs_event_send_thresh(sb, event);

User still gets warnings about space usage, but there's no runtime
overhead or problems with lock/memory allocation contexts, etc.
Having fs to keep a firm hand on thresholds limits would indeed be
far more sane approach though that would require each fs to
add support for that and handle most of it on their own. Avoiding
quoted
this was the main rationale behind this rfc.
If fs people agree to that, I'll be more than willing to drop this
in favour of the per-fs tracking solution. 
Personally, I hope they will.
I was hoping that you'd think a little more about my suggestion and
work out how to do background threshold event detection generically.
I kind of left it as "an exercise for the reader" because it seems
obvious to me.

Hint: ->statfs allows you to get the total, free and used space
from filesystems in a generic manner.

Cheers,

Dave.
I haven't given up on that, so yes, I'm still working on a more suitable
generic solution.
Background detection is one of the options, though it needs some more thoughts.
Giving up the sync approach means less accuracy for the threshold notifications,
but I guess this could be fine-tuned to get an acceptable level. Another bump:
how this tuning is supposed to be done (additional config option maybe)? 
The interface would have to keep it somehow sane - but what would 'sane' mean
in this case (?) Also, I'm not sure whether single approach would server here
well for all the potentially supported file systems so this would have to be
properly adjusted (taking the threshold levels into consideration as well). 
And still,it would require some form of synchronization with tracked fs so that
this 'detection' is not being unnecessarily performed (i.e. while fs remains frozen).

There is also an idea of using an interface resembling the stackable fs:
a transparent file system layered on top of the tracked one 
(solely for the tracking purposes). This would simplify handling the trace 
object's lifetime - no more list of registered traces.
It would also give a way of tracking (to some extent) the changes in the amount
of available space, which combined with tweaked background check could give
a solution with less performance overhead than the original one.
I'll try this one and see how it goes.

Thank You for your feedback so far - I really appreciate it.


Best Regards
Beata 

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Dave Chinner <david@fromorbit.com>
Date: 2015-06-19 23:21:17

On Fri, Jun 19, 2015 at 07:28:11PM +0200, Beata Michalska wrote:
On 06/19/2015 02:03 AM, Dave Chinner wrote:
quoted
On Thu, Jun 18, 2015 at 10:25:08AM +0200, Beata Michalska wrote:
quoted
On 06/18/2015 01:06 AM, Dave Chinner wrote:
quoted
On Tue, Jun 16, 2015 at 03:09:30PM +0200, Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
This has massive scalability problems:
....
quoted
quoted
Have you noticed that the filesystems have percpu counters for
tracking global space usage? There's good reason for that - taking a
spinlock in such a hot accounting path causes severe contention.
....
quoted
quoted
Then puts the entire netlink send path inside this spinlock, which
includes memory allocation and all sorts of non-filesystem code
paths. And it may be inside critical filesystem locks as well....

Apart from the serialisation problem of the locking, adding
memory allocation and the network send path to filesystem code
that is effectively considered "innermost" filesystem code is going
to have all sorts of problems for various filesystems. In the XFS
case, we simply cannot execute this sort of function in the places
where we update global space accounting.

As it is, I think the basic concept of separate tracking of free
space if fundamentally flawed. What I think needs to be done is that
filesystems need access to the thresholds for events, and then the
filesystems call fs_event_send_thresh() themselves from appropriate
contexts (ie. without compromising locking, scalability, memory
allocation recursion constraints, etc).

e.g. instead of tracking every change in free space, a filesystem
might execute this once every few seconds from a workqueue:

	event = fs_event_need_space_warning(sb, <fs_free_space>)
	if (event)
		fs_event_send_thresh(sb, event);

User still gets warnings about space usage, but there's no runtime
overhead or problems with lock/memory allocation contexts, etc.
Having fs to keep a firm hand on thresholds limits would indeed be
far more sane approach though that would require each fs to
add support for that and handle most of it on their own. Avoiding
quoted
this was the main rationale behind this rfc.
If fs people agree to that, I'll be more than willing to drop this
in favour of the per-fs tracking solution. 
Personally, I hope they will.
I was hoping that you'd think a little more about my suggestion and
work out how to do background threshold event detection generically.
I kind of left it as "an exercise for the reader" because it seems
obvious to me.

Hint: ->statfs allows you to get the total, free and used space
from filesystems in a generic manner.

Cheers,

Dave.
I haven't given up on that, so yes, I'm still working on a more suitable
generic solution.
Background detection is one of the options, though it needs some more thoughts.
Giving up the sync approach means less accuracy for the threshold notifications,
but I guess this could be fine-tuned to get an acceptable level.
Accuracy really doesn't matter for threshold notifications - by the
time the event is delivered to userspace it can already be wrong.
Another bump:
how this tuning is supposed to be done (additional config option maybe)? 
Why would you need to tune it at all? You can't *stop* the operation
that is triggering the threshold, so a few seconds delay on delivery
isn't going to make any difference to anyone....

You're overthinking this massively. All this needs is a work item
per superblock, and when the thresholds are turned on it queues a
self-repeating delayed work that calls ->statfs, checks against the
configured threshold, issues an event if necessary, and then queues
itself again to run next period. When the threshold is turned off,
the work is cancelled.

Another option: a kernel thread that runs periodically and just
calls iterate_supers() with a function that checks the sb for
threshold events, and if configured runs ->statfs and does the work,
otherwise skips the sb. That avoids all the lifetime issues with
using workqueues, you don't need a struct work, etc.
There is also an idea of using an interface resembling the stackable fs:
No. Just .... No.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-06-22 15:46:27

On 06/20/2015 01:21 AM, Dave Chinner wrote:
On Fri, Jun 19, 2015 at 07:28:11PM +0200, Beata Michalska wrote:
quoted
On 06/19/2015 02:03 AM, Dave Chinner wrote:
quoted
On Thu, Jun 18, 2015 at 10:25:08AM +0200, Beata Michalska wrote:
quoted
On 06/18/2015 01:06 AM, Dave Chinner wrote:
quoted
On Tue, Jun 16, 2015 at 03:09:30PM +0200, Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
This has massive scalability problems:
....
quoted
quoted
Have you noticed that the filesystems have percpu counters for
tracking global space usage? There's good reason for that - taking a
spinlock in such a hot accounting path causes severe contention.
....
quoted
quoted
Then puts the entire netlink send path inside this spinlock, which
includes memory allocation and all sorts of non-filesystem code
paths. And it may be inside critical filesystem locks as well....

Apart from the serialisation problem of the locking, adding
memory allocation and the network send path to filesystem code
that is effectively considered "innermost" filesystem code is going
to have all sorts of problems for various filesystems. In the XFS
case, we simply cannot execute this sort of function in the places
where we update global space accounting.

As it is, I think the basic concept of separate tracking of free
space if fundamentally flawed. What I think needs to be done is that
filesystems need access to the thresholds for events, and then the
filesystems call fs_event_send_thresh() themselves from appropriate
contexts (ie. without compromising locking, scalability, memory
allocation recursion constraints, etc).

e.g. instead of tracking every change in free space, a filesystem
might execute this once every few seconds from a workqueue:

	event = fs_event_need_space_warning(sb, <fs_free_space>)
	if (event)
		fs_event_send_thresh(sb, event);

User still gets warnings about space usage, but there's no runtime
overhead or problems with lock/memory allocation contexts, etc.
Having fs to keep a firm hand on thresholds limits would indeed be
far more sane approach though that would require each fs to
add support for that and handle most of it on their own. Avoiding
quoted
this was the main rationale behind this rfc.
If fs people agree to that, I'll be more than willing to drop this
in favour of the per-fs tracking solution. 
Personally, I hope they will.
I was hoping that you'd think a little more about my suggestion and
work out how to do background threshold event detection generically.
I kind of left it as "an exercise for the reader" because it seems
obvious to me.

Hint: ->statfs allows you to get the total, free and used space
from filesystems in a generic manner.

Cheers,

Dave.
I haven't given up on that, so yes, I'm still working on a more suitable
generic solution.
Background detection is one of the options, though it needs some more thoughts.
Giving up the sync approach means less accuracy for the threshold notifications,
but I guess this could be fine-tuned to get an acceptable level.
Accuracy really doesn't matter for threshold notifications - by the
time the event is delivered to userspace it can already be wrong.
quoted
Another bump:
how this tuning is supposed to be done (additional config option maybe)? 
Why would you need to tune it at all? You can't *stop* the operation
that is triggering the threshold, so a few seconds delay on delivery
isn't going to make any difference to anyone....

You're overthinking this massively. All this needs is a work item
per superblock, and when the thresholds are turned on it queues a
self-repeating delayed work that calls ->statfs, checks against the
configured threshold, issues an event if necessary, and then queues
itself again to run next period. When the threshold is turned off,
the work is cancelled.

Another option: a kernel thread that runs periodically and just
calls iterate_supers() with a function that checks the sb for
threshold events, and if configured runs ->statfs and does the work,
otherwise skips the sb. That avoids all the lifetime issues with
using workqueues, you don't need a struct work, etc.
quoted
There is also an idea of using an interface resembling the stackable fs:
No. Just .... No.

Cheers,

Dave.
Alright, I'll make appropriate changes to move the threshold
verification into the background and see how it works.


Thanks,

Best Regards
Beata 





--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 2/4] ext4: Add helper function to mark group as corrupted

From: Bartlomiej Zolnierkiewicz <hidden>
Date: 2015-07-22 10:40:03

Hi,

On Tuesday, June 16, 2015 03:09:31 PM Beata Michalska wrote:
Add ext4_mark_group_corrupted helper function to
simplify the code and to keep the logic in one place.

Signed-off-by: Beata Michalska <redacted>
This small cleanup patch is not really required for your
notifications framework to work and it seems to be a good
change on its own.  Maybe it can be merged independently of
other patches?  Ted, what is your opinion on it?

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
quoted hunk
---
 fs/ext4/balloc.c  |   15 +++------------
 fs/ext4/ext4.h    |    9 +++++++++
 fs/ext4/ialloc.c  |    5 +----
 fs/ext4/mballoc.c |   11 ++---------
 4 files changed, 15 insertions(+), 25 deletions(-)
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 83a6f49..e95b27a 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -193,10 +193,7 @@ static int ext4_init_block_bitmap(struct super_block *sb,
 	 * essentially implementing a per-group read-only flag. */
 	if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
 		grp = ext4_get_group_info(sb, block_group);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
 			int count;
 			count = ext4_free_inodes_count(sb, gdp);
@@ -379,20 +376,14 @@ static void ext4_validate_block_bitmap(struct super_block *sb,
 		ext4_unlock_group(sb, block_group);
 		ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
 			   block_group, blk);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		return;
 	}
 	if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
 			desc, bh))) {
 		ext4_unlock_group(sb, block_group);
 		ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		return;
 	}
 	set_buffer_verified(bh);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f63c3d5..163afe2 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2535,6 +2535,15 @@ static inline spinlock_t *ext4_group_lock_ptr(struct super_block *sb,
 	return bgl_lock_ptr(EXT4_SB(sb)->s_blockgroup_lock, group);
 }
 
+static inline
+void ext4_mark_group_corrupted(struct ext4_sb_info *sbi,
+				struct ext4_group_info *grp)
+{
+	if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
+		percpu_counter_sub(&sbi->s_freeclusters_counter, grp->bb_free);
+	set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+}
+
 /*
  * Returns true if the filesystem is busy enough that attempts to
  * access the block group locks has run into contention.
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index ac644c3..ebe0499 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -79,10 +79,7 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,
 	if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
 		ext4_error(sb, "Checksum bad for group %u", block_group);
 		grp = ext4_get_group_info(sb, block_group);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 		if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
 			int count;
 			count = ext4_free_inodes_count(sb, gdp);
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 8d1e602..24a4b6d 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -760,10 +760,7 @@ void ext4_mb_generate_buddy(struct super_block *sb,
 		 * corrupt and update bb_free using bitmap value
 		 */
 		grp->bb_free = free;
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   grp->bb_free);
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
+		ext4_mark_group_corrupted(sbi, grp);
 	}
 	mb_set_largest_free_order(sb, grp);
 
@@ -1448,12 +1445,8 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
 				      "freeing already freed block "
 				      "(bit %u); block bitmap corrupt.",
 				      block);
-		if (!EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info))
-			percpu_counter_sub(&sbi->s_freeclusters_counter,
-					   e4b->bd_info->bb_free);
 		/* Mark the block group as corrupt. */
-		set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
-			&e4b->bd_info->bb_state);
+		ext4_mark_group_corrupted(sbi, e4b->bd_info);
 		mb_regenerate_buddy(e4b);
 		goto done;
 	}
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Bartlomiej Zolnierkiewicz <hidden>
Date: 2015-07-22 15:55:34

Hi,

Some comments below.

On Tuesday, June 16, 2015 03:09:30 PM Beata Michalska wrote:
quoted hunk
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
---
 Documentation/filesystems/events.txt |  232 ++++++++++
 fs/Kconfig                           |    2 +
 fs/Makefile                          |    1 +
 fs/events/Kconfig                    |    7 +
 fs/events/Makefile                   |    5 +
 fs/events/fs_event.c                 |  809 ++++++++++++++++++++++++++++++++++
 fs/events/fs_event.h                 |   22 +
 fs/events/fs_event_netlink.c         |  104 +++++
 fs/namespace.c                       |    1 +
 include/linux/fs.h                   |    6 +-
 include/linux/fs_event.h             |   72 +++
 include/uapi/linux/Kbuild            |    1 +
 include/uapi/linux/fs_event.h        |   58 +++
 13 files changed, 1319 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/filesystems/events.txt
 create mode 100644 fs/events/Kconfig
 create mode 100644 fs/events/Makefile
 create mode 100644 fs/events/fs_event.c
 create mode 100644 fs/events/fs_event.h
 create mode 100644 fs/events/fs_event_netlink.c
 create mode 100644 include/linux/fs_event.h
 create mode 100644 include/uapi/linux/fs_event.h
diff --git a/Documentation/filesystems/events.txt b/Documentation/filesystems/events.txt
new file mode 100644
index 0000000..c2e6227
--- /dev/null
+++ b/Documentation/filesystems/events.txt
@@ -0,0 +1,232 @@
+
+	Generic file system event notification interface
+
+Document created 23 April 2015 by Beata Michalska <b.michalska@samsung.com>
+
+1. The reason behind:
+=====================
+
+There are many corner cases when things might get messy with the filesystems.
+And it is not always obvious what and when went wrong. Sometimes you might
+get some subtle hints that there is something going on - but by the time
+you realise it, it might be too late as you are already out-of-space
+or the filesystem has been remounted as read-only (i.e.). The generic
+interface for the filesystem events fills the gap by providing a rather
+easy way of real-time notifications triggered whenever something interesting
+happens, allowing filesystems to report events in a common way, as they occur.
+
+2. How does it work:
+====================
+
+The interface itself has been exposed as fstrace-type Virtual File System,
+primarily to ease the process of setting up the configuration for the
+notifications. So for starters, it needs to get mounted (obviously):
+
+	mount -t fstrace none /sys/fs/events
+
+This will unveil the single fstrace filesystem entry - the 'config' file,
+through which the notification are being set-up.
The patch creates a separate virtual filesystem for single file,
this is an overkill IMHO and a new sysfs or debugfs entry should
be sufficient.
+
+Activating notifications for particular filesystem is as straightforward
+as writing into the 'config' file. Note that by default all events, despite
+the actual filesystem type, are being disregarded.
+
+Synopsis of config:
+------------------
+
+	MOUNT EVENT_TYPE [L1] [L2]
OTOH Why not use the advantages of having a separate virtual
filesystem and create separate directories for each mount point
(+ maybe even extra parent directories for mount namespaces) and
put separate entries for each event type in these directories.

This would also allow usage of eventfd() notification interface
on such files.

Please take look at:

    tools/cgroup/cgroup_event_listener.c

and

    Documentation/cgroups/memcg_test.txt (point 9.10)

to see how much easier it is to observe memory usage thresholds
on memory cgroups compared to available blocks on filesystems
using fs events..

Also while at it please add your example user-space code (posted
on request in a some other mail) to tools/fs_events/ (preferably
in a separate patch).
+
+ MOUNT      : the filesystem's mount point
+ EVENT_TYPE : event types - currently two of them are being supported:
+
+	      * generic events ("G") covering most common warnings
+	      and errors that might be reported by any filesystem;
+	      this option does not take any arguments;
fs_event.h in uapi dir allows following events:

/*
 * Supported set of FS events
 */
enum {
	FS_EVENT_NONE,
	FS_WARN_ENOSPC,		/* No space left to reserve data blks */
	FS_WARN_ENOSPC_META,	/* No space left for metadata */
	FS_THR_LRBELOW,		/* The threshold lower range has been reached */
	FS_THR_LRABOVE,		/* The threshold lower range re-activcated*/
	FS_THR_URBELOW,
	FS_THR_URABOVE,
	FS_ERR_REMOUNT_RO,	/* The file system has been remounted as RO */
	FS_ERR_CORRUPTED	/* Critical error - fs corrupted */

};

For non-threshold related events the current interface allows
only configuration of all or none events to be anabled, i.e.
you cannot selectively enable notification on FS_WARN_ENOSPC
but not on FS_ERR_REMOUNT_RO.

I also think that configuration interface should be made to
match the notification interface when it comes to event types.
+
+	      * threshold notifications ("T") - events sent whenever
+	      the amount of available space drops below certain level;
+	      it is possible to specify two threshold levels though
+	      only one is required to properly setup the notifications;
+	      as those refer to the number of available blocks, the lower
+	      level [L1] needs to be higher than the upper one [L2]
Why is there a limitation of only two thresholds?

It should be relatively easy to make the code support
unlimited number of thresholds.
+
+Sample request could look like the following:
+
+ echo /sample/mount/point G T 710000 500000 > /sys/fs/events/config
+
+Multiple request might be specified provided they are separated with semicolon.
s/request/requests/

I think that allowing multiple event types and requests in one
configuration request is not a good idea.  Currently parsing
code is relatively simple but once somebody decides to enhance
the interface with new event types the parsing code may get
complex & ugly.
+
+The configuration itself might be modified at any time. One can add/remove
+particular event types for given fielsystem, modify the threshold levels,
s/fielsystem/filesystem/
+and remove single or all entries from the 'config' file.
+
+ - Adding new event type:
+
+ $ echo MOUNT EVENT_TYPE > /sys/fs/events/config
+
+(Note that is is enough to provide the event type to be enabled without
s/is is/is/
+the already set ones.)
+
+ - Removing event type:
+
+ $ echo '!MOUNT EVENT_TYPE' > /sys/fs/events/config
+
+ - Updating threshold limits:
+
+ $ echo MOUNT T L1 L2 > /sys/fs/events/config
+
+ - Removing single entry:
+
+ $ echo '!MOUNT' > /sys/fs/events/config
+
+ - Removing all entries:
+
+ $ echo > /sys/fs/events/config
+
+Reading the file will list all registered entries with their current set-up
+along with some additional info like the filesystem type and the backing device
+name if available.
+
+Final, though a very important note on the configuration: when and if the
+actual events are being triggered falls way beyond the scope of the generic
+filesystem events interface. It is up to a particular filesystem
+implementation which events are to be supported - if any at all. So if
+given filesystem does not support the event notifications, an attempt to
+enable those through 'config' file will fail.
+
+
+3. The generic netlink interface support:
+=========================================
+
+Whenever an event notification is triggered (by given filesystem) the current
+configuration is being validated to decide whether a userpsace notification
s/userpsace/userspace/
+should be launched. If there has been no request (in a mean of 'config' file
+entry) for given event, one will be silently disregarded. If, on the other
+hand, someone is 'watching' given filesystem for specific events, a generic
+netlink message will be sent. A dedicated multicast group has been provided
+solely for this purpose so in order to receive such notifications, one should
+subscribe to this new multicast group. As for now only the init network
+namespace is being supported.
+
+3.1 Message format
+
+The FS_NL_C_EVENT shall be stored within the generic netlink message header
+as the command field. The message payload will provide more detailed info:
+the backing device major and minor numbers, the event code and the id of
+the process which action led to the event occurrence. In case of threshold
+notifications, the current number of available blocks will be included
+in the payload as well.
+
+
+	 0                   1                   2                   3
+	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|	            NETLINK MESSAGE HEADER			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 		GENERIC NETLINK MESSAGE HEADER   		|
+	| 	   (with FS_NL_C_EVENT as genlmsghdr cdm field)		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 	      Optional user specific message header		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|		   GENERIC MESSAGE PAYLOAD:			|
+	+---------------------------------------------------------------+
+	| 		  FS_NL_A_EVENT_ID  (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|	  	  FS_NL_A_DEV_MAJOR (NLA_U32)			|
+	+---------------------------------------------------------------+
+	| 	  	  FS_NL_A_DEV_MINOR (NLA_U32) 			|
+	+---------------------------------------------------------------+
+	|  		  FS_NL_A_CAUSED_ID (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|  		    FS_NL_A_DATA (NLA_U64)			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+The above figure is based on:
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto#Message_Format
+
+
+4. API Reference:
+=================
+
+ 4.1 Generic file system event interface data & operations
+
+ #include <linux/fs_event.h>
+
+ struct fs_trace_info {
+	void	__rcu	*e_priv		 /* READ ONLY */
It should be marked as private for fs events core code and
not for use by filesystems' code.  If possible it would be
the best to move it out of this struct,
+	unsigned int 	events_cap_mask; /* Supported notifications */
+	const struct fs_trace_operations *ops;
+ };
+
+ struct fs_trace_operations {
+	void (*query)(struct super_block *, u64 *);
+ };
+
+ In order to get the fireworks and stuff, each filesystem needs to setup
+ the events_cap_mask field of the fs_trace_info structure, which has been
+ embedded within the super_block structure. This should reflect the type of
+ events the filesystem wants to support. In case of threshold notifications,
+ apart from setting the FS_EVENT_THRESH flag, the 'query' callback should
+ be provided as this enables the events interface to get the up-to-date
+ state of the number of available blocks whenever those notifications are
+ being requested.
+
+ The 'e_priv' field of the fs_trace_info structure should be completely ignored
+ as it's for INTERNAL USE ONLY. So don't even think of messing with it, if you
+ do not want to get yourself into some real trouble. If still, you are tempted
+ to do so - feel free, it's gonna be pure fun. Consider yourself warned.
+
+
+ 4.2 Event notification:
+
+ #include <linux/fs_event.h>
+ void fs_event_notify(struct super_block *sb, unsigned int event_id);
+
+ Notify the generic FS event interface of an occurring event.
+ This shall be used by any file system that wishes to inform any potential
+ listeners/watchers of a particular event.
+ - sb:         the filesystem's super block
+ - event_id:   an event identifier
+
+ 4.3 Threshold notifications:
+
+ #include <linux/fs_event.h>
+ void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+ void fs_event_free_space(struct super_block *sb, u64 ncount);
+
+ Each filesystme supporting the threshold notifications should call
+ fs_event_alloc_space/fs_event_free_space respectively whenever the
+ amount of available blocks changes.
+ - sb:     the filesystem's super block
+ - ncount: number of blocks being acquired/released
+
+ Note that to properly handle the threshold notifications the fs events
+ interface needs to be kept up to date by the filesystems. Each should
+ register fs_trace_operations to enable querying the current number of
+ available blocks.
+
+ 4.4 Sending message through generic netlink interface
+
+ #include <linux/fs_event.h>
+
+ int fs_netlink_send_event(size_t size, unsigned int event_id,
+	int (*compose_msg)(struct sk_buff *skb, void *data), void *cbdata);
+
+ Although the fs event interface is fully responsible for sending the messages
+ over the netlink, filesystems might use the FS_EVENT multicast group to send
+ their own custom messages.
+ - size:        the size of the message payload
+ - event_id:    the event identifier
+ - compose_msg: a callback responsible for filling-in the message payload
+ - cbdata:      message custom data
+
+ Calling fs_netlink_send_event will result in a message being sent by
+ the FS_EVENT multicast group. Note that the body of the message should be
+ prepared (set-up )by the caller - through compose_msg callback. The message's
(set-up)
quoted hunk
+ sk_buff will be allocated on behalf of the caller (thus the size parameter).
+ The compose_msg should only fill the payload with proper data. Unless
+ the event id is specified as FS_EVENT_NONE, it's value shall be added
+ to the payload prior to calling the compose_msg.
+
+
diff --git a/fs/Kconfig b/fs/Kconfig
index ec35851..a89e678 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -69,6 +69,8 @@ config FILE_LOCKING
           for filesystems like NFS and for the flock() system
           call. Disabling this option saves about 11k.
 
+source "fs/events/Kconfig"
+
 source "fs/notify/Kconfig"
 
 source "fs/quota/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index a88ac48..bcb3048 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -126,3 +126,4 @@ obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
 obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/
+obj-$(CONFIG_FS_EVENTS)		+= events/
diff --git a/fs/events/Kconfig b/fs/events/Kconfig
new file mode 100644
index 0000000..1c60195
--- /dev/null
+++ b/fs/events/Kconfig
@@ -0,0 +1,7 @@
+# Generic Files System events interface
+config FS_EVENTS
+	bool "Generic filesystem events"
+	select NET
+	default y
Do we really want to default to yes?

[ If so then maybe we want to make the config option visible
  only when EXPERT mode is enabled? ]
+	help
+	  Enable generic filesystem events interface
Please enhance the help entry.
quoted hunk
diff --git a/fs/events/Makefile b/fs/events/Makefile
new file mode 100644
index 0000000..9c98337
--- /dev/null
+++ b/fs/events/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Linux Generic File System Event Interface
+#
+
+obj-y := fs_event.o fs_event_netlink.o
diff --git a/fs/events/fs_event.c b/fs/events/fs_event.c
new file mode 100644
index 0000000..1037311
--- /dev/null
+++ b/fs/events/fs_event.c
@@ -0,0 +1,809 @@
+/*
+ * Generic File System Evens Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/nsproxy.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <net/genetlink.h>
+#include "../pnode.h"
+#include "fs_event.h"
+
+static LIST_HEAD(fs_trace_list);
+static DEFINE_MUTEX(fs_trace_lock);
+
+static struct kmem_cache *fs_trace_cachep __read_mostly;
+
+static atomic_t stray_traces = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(trace_wq);
+/*
+ * Threshold notification state bits.
+ * Note the reverse as this refers to the number
+ * of available blocks.
+ */
+#define THRESH_LR_BELOW		0x0001 /* Falling below the lower range */
+#define THRESH_LR_BEYOND	0x0002
+#define THRESH_UR_BELOW		0x0004
+#define THRESH_UR_BEYOND	0x0008 /* Going beyond the upper range */
+
+#define THRESH_LR_ON	(THRESH_LR_BELOW | THRESH_LR_BEYOND)
+#define THRESH_UR_ON	(THRESH_UR_BELOW | THRESH_UR_BEYOND)
+
+#define FS_TRACE_ADD	0x100000
+
+struct fs_trace_entry {
+	struct kref	 count;
+	atomic_t	 active;
+	struct super_block *sb;
+	unsigned int	 notify;
+	struct path	 mnt_path;
+	struct list_head  node;
+
+	struct fs_event_thresh {
+		u64		 avail_space;
+		u64		 lrange;
+		u64		 urange;
+		unsigned int	 state;
+	}		 th;
+	struct rcu_head	 rcu_head;
+	spinlock_t	 lock;
+};
+
+static const match_table_t fs_etypes = {
+	{ FS_EVENT_GENERIC, "G"   },
+	{ FS_EVENT_THRESH,  "T"   },
+	{ 0, NULL },
+};
+
+static inline int fs_trace_query_data(struct super_block *sb,
+				       struct fs_trace_entry *en)
+{
+	if (sb->s_etrace.ops && sb->s_etrace.ops->query) {
+		sb->s_etrace.ops->query(sb, &en->th.avail_space);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static inline void fs_trace_entry_free(struct fs_trace_entry *en)
I don't see a real need for this wrapper (it is used only once).
+{
+	kmem_cache_free(fs_trace_cachep, en);
+}
+
+static void fs_destroy_trace_entry(struct kref *en_ref)
+{
+	struct fs_trace_entry *en = container_of(en_ref,
+					 struct fs_trace_entry, count);
+
+	/* Last reference has been dropped */
+	fs_trace_entry_free(en);
+	atomic_dec(&stray_traces);
+}
+
+static void fs_trace_entry_put(struct fs_trace_entry *en)
+{
+	kref_put(&en->count, fs_destroy_trace_entry);
+}
+
+static void fs_release_trace_entry(struct rcu_head *rcu_head)
+{
+	struct fs_trace_entry *en = container_of(rcu_head,
+						 struct fs_trace_entry,
+						 rcu_head);
+	/*
+	 * As opposed to typical reference drop, this one is being
+	 * called from the rcu callback. This is to make sure all
+	 * readers have managed to safely grab the reference before
+	 * the change to rcu pointer is visible to all and before
+	 * the reference is dropped here.
+	 */
+	fs_trace_entry_put(en);
+}
+
+static void fs_drop_trace_entry(struct fs_trace_entry *en)
+{
+	struct super_block *sb;
+
+	lockdep_assert_held(&fs_trace_lock);
+	/*
+	 * The trace entry might have already been removed
+	 * from the list of active traces with the proper
+	 * ref drop, though it was still in use handling
+	 * one of the fs events. This means that the object
+	 * has been already scheduled for being released.
+	 * So leave...
+	 */
+
+	if (!atomic_add_unless(&en->active, -1, 0))
+		return;
+	/*
+	 * At this point the trace entry is being marked as inactive
+	 * so no new references will be allowed.
+	 * Still it might be floating around somewhere
+	 * so drop the reference when the rcu readers are done.
+	 */
+	spin_lock(&en->lock);
+	list_del(&en->node);
+	sb = en->sb;
+	en->sb = NULL;
+	spin_unlock(&en->lock);
+
+	rcu_assign_pointer(sb->s_etrace.e_priv, NULL);
+	call_rcu(&en->rcu_head, fs_release_trace_entry);
+	/* It's safe now to drop the reference to the super */
+	deactivate_super(sb);
+	atomic_inc(&stray_traces);
+}
+
+static inline
+struct fs_trace_entry *fs_trace_entry_get(struct fs_trace_entry *en)
+{
+	if (en) {
+		if (!kref_get_unless_zero(&en->count))
+			return NULL;
+		/* Don't allow referencing inactive object */
+		if (!atomic_read(&en->active)) {
+			fs_trace_entry_put(en);
+			return NULL;
+		}
+	}
+	return en;
+}
+
+static struct fs_trace_entry *fs_trace_entry_get_rcu(struct super_block *sb)
+{
+	struct fs_trace_entry *en;
+
+	if (!sb)
+		return NULL;
+
+	rcu_read_lock();
+	en = rcu_dereference(sb->s_etrace.e_priv);
+	en = fs_trace_entry_get(en);
+	rcu_read_unlock();
+
+	return en;
+}
+
+static int fs_remove_trace_entry(struct super_block *sb)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return -EINVAL;
+
+	mutex_lock(&fs_trace_lock);
+	fs_drop_trace_entry(en);
+	mutex_unlock(&fs_trace_lock);
+	fs_trace_entry_put(en);
+	return 0;
+}
+
+static void fs_remove_all_traces(void)
+{
+	struct fs_trace_entry *en, *guard;
+
+	mutex_lock(&fs_trace_lock);
+	list_for_each_entry_safe(en, guard, &fs_trace_list, node)
+		fs_drop_trace_entry(en);
+	mutex_unlock(&fs_trace_lock);
+}
+
+static int create_common_msg(struct sk_buff *skb, void *data)
+{
+	struct fs_trace_entry *en = (struct fs_trace_entry *)data;
+	struct super_block *sb = en->sb;
+
+	if (nla_put_u32(skb, FS_NL_A_DEV_MAJOR, MAJOR(sb->s_dev))
+	||  nla_put_u32(skb, FS_NL_A_DEV_MINOR, MINOR(sb->s_dev)))
+		return -EINVAL;
+
+	if (nla_put_u64(skb, FS_NL_A_CAUSED_ID, pid_vnr(task_pid(current))))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int create_thresh_msg(struct sk_buff *skb, void *data)
+{
+	struct fs_trace_entry *en = (struct fs_trace_entry *)data;
+	int ret;
+
+	ret = create_common_msg(skb, data);
+	if (!ret)
+		ret = nla_put_u64(skb, FS_NL_A_DATA, en->th.avail_space);
+	return ret;
+}
+
+static void fs_event_send(struct fs_trace_entry *en, unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64));
+
+	fs_netlink_send_event(size, event_id, create_common_msg, en);
+}
+
+static void fs_event_send_thresh(struct fs_trace_entry *en,
+				  unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64)) * 2;
+
+	fs_netlink_send_event(size, event_id, create_thresh_msg, en);
+}
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+	if (atomic_read(&en->active) && (en->notify & FS_EVENT_GENERIC))
+		fs_event_send(en, event_id);
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_notify);
+
+void fs_event_alloc_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+	s64 count;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+	/*
+	 * we shouldn't drop below 0 here,
+	 * unless there is a sync issue somewhere (?)
+	 */
+	count = en->th.avail_space - ncount;
+	en->th.avail_space = count < 0 ? 0 : count;
+
+	if (en->th.avail_space > en->th.lrange)
+		/* Not 'even' close - leave */
+		goto leave;
+
+	if (en->th.avail_space > en->th.urange) {
+		/* Close enough - the lower range has been reached */
+		if (!(en->th.state & THRESH_LR_BEYOND)) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRBELOW);
+			en->th.state &= ~THRESH_LR_BELOW;
+			en->th.state |= THRESH_LR_BEYOND;
+		}
+		goto leave;
+	}
+	if (!(en->th.state & THRESH_UR_BEYOND)) {
+		fs_event_send_thresh(en, FS_THR_URBELOW);
+		en->th.state &=  ~THRESH_UR_BELOW;
+		en->th.state |= THRESH_UR_BEYOND;
+	}
+
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_alloc_space);
+
+void fs_event_free_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+
+	en->th.avail_space += ncount;
+
+	if (en->th.avail_space > en->th.lrange) {
+		if (!(en->th.state & THRESH_LR_BELOW)
+		&& en->th.state & THRESH_LR_BEYOND) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRABOVE);
+			en->th.state &= ~(THRESH_LR_BEYOND|THRESH_UR_BEYOND);
+			en->th.state |= THRESH_LR_BELOW;
+			goto leave;
+		}
+	}
+	if (en->th.avail_space > en->th.urange) {
+		if (!(en->th.state & THRESH_UR_BELOW)
+		&& en->th.state & THRESH_UR_BEYOND) {
+			/* Notify */
+			fs_event_send_thresh(en, FS_THR_URABOVE);
+			en->th.state &= ~THRESH_UR_BEYOND;
+			en->th.state |= THRESH_UR_BELOW;
+		}
+	}
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_free_space);
+
+void fs_event_mount_dropped(struct vfsmount *mnt)
+{
+	/*
+	 * The mount is dropped but the super might not get released
+	 * at once so there is very small chance some notifications
+	 * will come through.
+	 * Note that the mount being dropped here might belong to a different
+	 * namespace - if this is the case, just ignore it.
+	 */
+	struct fs_trace_entry  *en = fs_trace_entry_get_rcu(mnt->mnt_sb);
+	struct vfsmount *en_mnt;
+
+	if (!en || !atomic_read(&en->active))
+		return;
+	/*
+	 * The entry once set, does not change the mountpoint it's being
+	 * pinned to, so no need to take the lock here.
+	 */
+	en_mnt = en->mnt_path.mnt;
+	if (!(real_mount(mnt)->mnt_ns != (real_mount(en_mnt))->mnt_ns))
+		fs_remove_trace_entry(mnt->mnt_sb);
+	fs_trace_entry_put(en);
+}
+
+static int fs_new_trace_entry(struct path *path, struct fs_event_thresh *thresh,
+				unsigned int nmask)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	struct mount *r_mnt;
+
+	en = kmem_cache_zalloc(fs_trace_cachep, GFP_KERNEL);
+	if (unlikely(!en))
+		return -ENOMEM;
+	/*
+	 * Note that no reference is being taken here for the path as it would
+	 * make the unmount unnecessarily puzzling (due to an extra 'valid'
+	 * reference for the mnt).
+	 * This is *rather* safe as the notification on mount being dropped
+	 * will get called prior to releasing the super block - so right
+	 * in time to perform appropriate clean-up
+	 */
+	r_mnt = real_mount(path->mnt);
+
+	en->mnt_path.dentry = r_mnt->mnt.mnt_root;
+	en->mnt_path.mnt = &r_mnt->mnt;
+
+	sb = path->mnt->mnt_sb;
+	en->sb = sb;
+	/*
+	 * Increase the refcount for sb to mark it's being relied on.
+	 * Note that the reference to path is taken by the caller, so it
+	 * is safe to assume there is at least single active reference
+	 * to super as well.
+	 */
+	atomic_inc(&sb->s_active);
+
+	nmask &= sb->s_etrace.events_cap_mask;
+	if (!nmask)
+		goto leave;
+
+	spin_lock_init(&en->lock);
+	INIT_LIST_HEAD(&en->node);
+
+	en->notify = nmask;
+	memcpy(&en->th, thresh, offsetof(struct fs_event_thresh, state));
+	if (nmask & FS_EVENT_THRESH)
+		fs_trace_query_data(sb, en);
+
+	kref_init(&en->count);
+
+	if (rcu_access_pointer(sb->s_etrace.e_priv) != NULL) {
+		struct fs_trace_entry *prev_en;
+
+		prev_en = fs_trace_entry_get_rcu(sb);
+		if (prev_en) {
+			WARN_ON(prev_en);
+			fs_trace_entry_put(prev_en);
+			goto leave;
+		}
+	}
+	atomic_set(&en->active, 1);
+
+	mutex_lock(&fs_trace_lock);
+	list_add(&en->node, &fs_trace_list);
+	mutex_unlock(&fs_trace_lock);
+
+	rcu_assign_pointer(sb->s_etrace.e_priv, en);
+	synchronize_rcu();
+
+	return 0;
+leave:
+	deactivate_super(sb);
+	kmem_cache_free(fs_trace_cachep, en);
+	return -EINVAL;
+}
+
+static int fs_update_trace_entry(struct path *path,
+				  struct fs_event_thresh *thresh,
+				  unsigned int nmask)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	int extend = nmask & FS_TRACE_ADD;
+	int ret = -EINVAL;
+
+	en = fs_trace_entry_get_rcu(path->mnt->mnt_sb);
+	if (!en)
+		return (extend) ? fs_new_trace_entry(path, thresh, nmask)
+				: -EINVAL;
+
+	if (!atomic_read(&en->active))
+		return -EINVAL;
+
+	nmask &= ~FS_TRACE_ADD;
+
+	spin_lock(&en->lock);
+	sb  = en->sb;
+	if (!sb || !(nmask & sb->s_etrace.events_cap_mask))
+		goto leave;
+
+	if (nmask & FS_EVENT_THRESH) {
+		if (extend) {
+			/* Get the current state */
+			if (!(en->notify & FS_EVENT_THRESH))
+				if (fs_trace_query_data(sb, en))
+					goto leave;
+
+			if (thresh->state & THRESH_LR_ON) {
+				en->th.lrange = thresh->lrange;
+				en->th.state &= ~THRESH_LR_ON;
+			}
+
+			if (thresh->state & THRESH_UR_ON) {
+				en->th.urange = thresh->urange;
+				en->th.state &= ~THRESH_UR_ON;
+			}
+		} else {
+			memset(&en->th, 0, sizeof(en->th));
+		}
+	}
+
+	if (extend)
+		en->notify |= nmask;
+	else
+		en->notify &= ~nmask;
+	ret = 0;
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+	return ret;
+}
+
+static int fs_parse_trace_request(int argc, char **argv)
+{
+	struct fs_event_thresh thresh = {0};
+	struct path path;
+	substring_t args[MAX_OPT_ARGS];
+	unsigned int nmask = FS_TRACE_ADD;
+	int token;
+	char *s;
+	int ret = -EINVAL;
+
+	if (!argc) {
+		fs_remove_all_traces();
+		return 0;
+	}
+
+	s = *(argv);
+	if (*s == '!') {
+		/* Clear the trace entry */
+		nmask &= ~FS_TRACE_ADD;
+		++s;
+	}
+
+	if (kern_path_mountpoint(AT_FDCWD, s, &path, LOOKUP_FOLLOW))
+		return -EINVAL;
+
+	if (!(--argc)) {
+		if (!(nmask & FS_TRACE_ADD))
+			ret = fs_remove_trace_entry(path.mnt->mnt_sb);
+		goto leave;
+	}
+
+repeat:
+	args[0].to = args[0].from = NULL;
+	token = match_token(*(++argv), fs_etypes, args);
+	if (!token && !nmask)
+		goto leave;
+
+	nmask |= token & FS_EVENTS_ALL;
+	--argc;
+	if ((token & FS_EVENT_THRESH)  && (nmask & FS_TRACE_ADD)) {
+		/*
+		 * Get the threshold config data:
+		 * lower range
+		 * upper range
+		 */
+		if (!argc)
+			goto leave;
+
+		ret = kstrtoull(*(++argv), 10, &thresh.lrange);
+		if (ret)
+			goto leave;
+		thresh.state |= THRESH_LR_ON;
+		if ((--argc)) {
+			ret = kstrtoull(*(++argv), 10, &thresh.urange);
+			if (ret)
+				goto leave;
+			thresh.state |= THRESH_UR_ON;
+			--argc;
+		}
+		/* The thresholds are based on number of available blocks */
+		if (thresh.lrange < thresh.urange) {
+			ret = -EINVAL;
+			goto leave;
+		}
+	}
+	if (argc)
+		goto repeat;
+
+	ret = fs_update_trace_entry(&path, &thresh, nmask);
+leave:
+	path_put(&path);
+	return ret;
+}
+
+#define DEFAULT_BUF_SIZE PAGE_SIZE
+
+static ssize_t fs_trace_write(struct file *file, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	char **argv;
+	char *kern_buf, *next, *cfg;
+	size_t size, dcount = 0;
+	int argc;
+
+	if (!count)
+		return 0;
+
+	kern_buf = kmalloc(DEFAULT_BUF_SIZE, GFP_KERNEL);
+	if (!kern_buf)
+		return -ENOMEM;
+
+	while (dcount < count) {
+
+		size = count - dcount;
+		if (size >= DEFAULT_BUF_SIZE)
+			size = DEFAULT_BUF_SIZE - 1;
+		if (copy_from_user(kern_buf, buffer + dcount, size)) {
+			dcount = -EINVAL;
+			goto leave;
+		}
+
+		kern_buf[size] = '\0';
+
+		next = cfg = kern_buf;
+
+		do {
+			next = strchr(cfg, ';');
+			if (next)
+				*next = '\0';
+
+			argv = argv_split(GFP_KERNEL, cfg, &argc);
+			if (!argv) {
+				dcount = -ENOMEM;
+				goto leave;
+			}
+
+			if (fs_parse_trace_request(argc, argv)) {
+				dcount = -EINVAL;
+				argv_free(argv);
+				goto leave;
+			}
+
+			argv_free(argv);
+			if (next)
+				cfg = ++next;
+
+		} while (next);
+		dcount += size;
+	}
+leave:
+	kfree(kern_buf);
+	return dcount;
+}
+
+static void *fs_trace_seq_start(struct seq_file *m, loff_t *pos)
+{
+	mutex_lock(&fs_trace_lock);
+	return seq_list_start(&fs_trace_list, *pos);
+}
+
+static void *fs_trace_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &fs_trace_list, pos);
+}
+
+static void fs_trace_seq_stop(struct seq_file *m, void *v)
+{
+	mutex_unlock(&fs_trace_lock);
+}
+
+static int fs_trace_seq_show(struct seq_file *m, void *v)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	struct mount *r_mnt;
+	const struct match_token *match;
+	unsigned int nmask;
+
+	en = list_entry(v, struct fs_trace_entry, node);
+	/* Do not show the entries outside current mount namespace */
+	r_mnt = real_mount(en->mnt_path.mnt);
+	if (r_mnt->mnt_ns != current->nsproxy->mnt_ns) {
+		if (!__is_local_mountpoint(r_mnt->mnt_mountpoint))
+			return 0;
+	}
+
+	sb = en->sb;
+
+	seq_path(m, &en->mnt_path, "\t\n\\");
+	seq_putc(m, ' ');
+
+	seq_escape(m, sb->s_type->name, " \t\n\\");
+	if (sb->s_subtype && sb->s_subtype[0]) {
+		seq_putc(m, '.');
+		seq_escape(m, sb->s_subtype, " \t\n\\");
+	}
+
+	seq_putc(m, ' ');
+	if (sb->s_op->show_devname) {
+		sb->s_op->show_devname(m, en->mnt_path.mnt->mnt_root);
+	} else {
+		seq_escape(m, r_mnt->mnt_devname ? r_mnt->mnt_devname : "none",
+				" \t\n\\");
+	}
+	seq_puts(m, " (");
+
+	nmask = en->notify;
+	for (match = fs_etypes; match->pattern; ++match) {
+		if (match->token & nmask) {
+			seq_puts(m, match->pattern);
+			nmask &= ~match->token;
+			if (nmask)
+				seq_putc(m, ',');
+		}
+	}
+	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
+	seq_puts(m, ")\n");
+	return 0;
+}
+
+static const struct seq_operations fs_trace_seq_ops = {
+	.start	= fs_trace_seq_start,
+	.next	= fs_trace_seq_next,
+	.stop	= fs_trace_seq_stop,
+	.show	= fs_trace_seq_show,
+};
+
+static int fs_trace_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &fs_trace_seq_ops);
+}
+
+static const struct file_operations fs_trace_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fs_trace_open,
+	.write		= fs_trace_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int fs_trace_init(void)
+{
+	fs_trace_cachep = KMEM_CACHE(fs_trace_entry, 0);
+	if (!fs_trace_cachep)
+		return -EINVAL;
+	init_waitqueue_head(&trace_wq);
+	return 0;
+}
+
+/* VFS support */
+static int fs_trace_fill_super(struct super_block *sb, void *data, int silen)
+{
+	int ret;
+	static struct tree_descr desc[] = {
+		[2] = {
+			.name	= "config",
+			.ops	= &fs_trace_fops,
+			.mode	= S_IWUSR | S_IRUGO,
+		},
+		{""},
+	};
+
+	ret = simple_fill_super(sb, 0x7246332, desc);
Please use a define for a magic number.
quoted hunk
+	return !ret ? fs_trace_init() : ret;
+}
+
+static struct dentry *fs_trace_do_mount(struct file_system_type *fs_type,
+		 int ntype, const char *dev_name, void *data)
+{
+	return mount_single(fs_type, ntype, data, fs_trace_fill_super);
+}
+
+static void fs_trace_kill_super(struct super_block *sb)
+{
+	/*
+	 * The rcu_barrier here will/should make sure all call_rcu
+	 * callbacks are completed - still there might be some active
+	 * trace objects in use which can make calling the
+	 * kmem_cache_destroy unsafe. So we wait until all traces
+	 * are finally released.
+	 */
+	fs_remove_all_traces();
+	rcu_barrier();
+	wait_event(trace_wq, !atomic_read(&stray_traces));
+
+	kmem_cache_destroy(fs_trace_cachep);
+	kill_litter_super(sb);
+}
+
+static struct kset	*fs_trace_kset;
+
+static struct file_system_type fs_trace_fstype = {
+	.name		= "fstrace",
+	.mount		= fs_trace_do_mount,
+	.kill_sb	= fs_trace_kill_super,
+};
+
+static void __init fs_trace_vfs_init(void)
+{
+	fs_trace_kset = kset_create_and_add("events", NULL, fs_kobj);
+
+	if (!fs_trace_kset)
+		return;
+
+	if (!register_filesystem(&fs_trace_fstype)) {
+		if (!fs_event_netlink_register())
+			return;
+		unregister_filesystem(&fs_trace_fstype);
+	}
+	kset_unregister(fs_trace_kset);
+}
+
+static int __init fs_trace_evens_init(void)
+{
+	fs_trace_vfs_init();
+	return 0;
+};
+module_init(fs_trace_evens_init);
+
diff --git a/fs/events/fs_event.h b/fs/events/fs_event.h
new file mode 100644
index 0000000..23f24c8
--- /dev/null
+++ b/fs/events/fs_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __GENERIC_FS_EVENTS_H
+#define __GENERIC_FS_EVENTS_H
+
+int  fs_event_netlink_register(void);
+void fs_event_netlink_unregister(void);
+
+#endif /* __GENERIC_FS_EVENTS_H */
diff --git a/fs/events/fs_event_netlink.c b/fs/events/fs_event_netlink.c
new file mode 100644
index 0000000..0c97eb7
--- /dev/null
+++ b/fs/events/fs_event_netlink.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include "fs_event.h"
+
+static const struct genl_multicast_group fs_event_mcgroups[] = {
+	{ .name = FS_EVENTS_MCAST_GRP_NAME, },
+};
+
+static struct genl_family fs_event_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= FS_EVENTS_FAMILY_NAME,
+	.version	= 1,
+	.maxattr	= FS_NL_A_MAX,
+	.mcgrps		= fs_event_mcgroups,
+	.n_mcgrps	= ARRAY_SIZE(fs_event_mcgroups),
+};
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb,  void *data),
+			  void *cbdata)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret = 0;
+
+	if (!size || !compose_msg)
+		return -EINVAL;
+
+	/* Skip if there are no listeners */
+	if (!genl_has_listeners(&fs_event_family, &init_net, 0))
+		return 0;
+
+	if (event_id != FS_EVENT_NONE)
+		size += nla_total_size(sizeof(u32));
+	size += nla_total_size(sizeof(u64));
+	skb = genlmsg_new(size, GFP_NOWAIT);
+
+	if (!skb) {
+		pr_debug("Failed to allocate new FS generic netlink message\n");
+		return -ENOMEM;
+	}
+
+	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+			&fs_event_family, 0, FS_NL_C_EVENT);
+	if (!msg_head)
+		goto cleanup;
+
+	if (event_id != FS_EVENT_NONE)
+		if (nla_put_u32(skb, FS_NL_A_EVENT_ID, event_id))
+			goto cancel;
+
+	ret = compose_msg(skb, cbdata);
+	if (ret)
+		goto cancel;
+
+	genlmsg_end(skb, msg_head);
+	ret = genlmsg_multicast(&fs_event_family, skb, 0, 0, GFP_NOWAIT);
+	if (ret && ret != -ENOBUFS && ret != -ESRCH)
+		goto cleanup;
+
+	return ret;
+
+cancel:
+	genlmsg_cancel(skb, msg_head);
+cleanup:
+	nlmsg_free(skb);
+	return ret;
+}
+EXPORT_SYMBOL(fs_netlink_send_event);
+
+int fs_event_netlink_register(void)
+{
+	int ret;
+
+	ret = genl_register_family(&fs_event_family);
+	if (ret)
+		pr_err("Failed to register FS netlink interface\n");
+	return ret;
+}
+
+void fs_event_netlink_unregister(void)
+{
+	genl_unregister_family(&fs_event_family);
+}
diff --git a/fs/namespace.c b/fs/namespace.c
index 82ef140..ec6e2ef 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1031,6 +1031,7 @@ static void cleanup_mnt(struct mount *mnt)
 	if (unlikely(mnt->mnt_pins.first))
 		mnt_pin_kill(mnt);
 	fsnotify_vfsmount_delete(&mnt->mnt);
+	fs_event_mount_dropped(&mnt->mnt);
 	dput(mnt->mnt.mnt_root);
 	deactivate_super(mnt->mnt.mnt_sb);
 	mnt_free_id(mnt);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b4d71b5..b7dadd9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -263,6 +263,10 @@ struct iattr {
  * Includes for diskquotas.
  */
 #include <linux/quota.h>
+/*
+ * Include for Generic File System Events Interface
+ */
+#include <linux/fs_event.h>
 
 /*
  * Maximum number of layers of fs stack.  Needs to be limited to
@@ -1253,7 +1257,7 @@ struct super_block {
 	struct hlist_node	s_instances;
 	unsigned int		s_quota_types;	/* Bitmask of supported quota types */
 	struct quota_info	s_dquot;	/* Diskquota specific options */
-
+	struct fs_trace_info	s_etrace;
 	struct sb_writers	s_writers;
 
 	char s_id[32];				/* Informational name */
diff --git a/include/linux/fs_event.h b/include/linux/fs_event.h
new file mode 100644
index 0000000..83e22dd
--- /dev/null
+++ b/include/linux/fs_event.h
@@ -0,0 +1,72 @@
+/*
+ * Generic File System Events Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef _LINUX_GENERIC_FS_EVETS_
+#define _LINUX_GENERIC_FS_EVETS_
EVETS?

Also the define name usually corresponds to the header filename.
quoted hunk
+#include <net/netlink.h>
+#include <uapi/linux/fs_event.h>
+
+/*
+ * Currently supported event types
+ */
+#define FS_EVENT_GENERIC 0x001
+#define FS_EVENT_THRESH	 0x002
+
+#define FS_EVENTS_ALL  (FS_EVENT_GENERIC | FS_EVENT_THRESH)
+
+struct fs_trace_operations {
+	void (*query)(struct super_block *, u64 *);
+};
+
+struct fs_trace_info {
+	void	__rcu	*e_priv;		 /* READ ONLY */
+	unsigned int	events_cap_mask; /* Supported notifications */
+	const struct fs_trace_operations *ops;
+};
+
+#ifdef CONFIG_FS_EVENTS
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id);
+void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+void fs_event_free_space(struct super_block *sb, u64 ncount);
+void fs_event_mount_dropped(struct vfsmount *mnt);
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb, void *data),
+			  void *cbdata);
+
+#else /* CONFIG_FS_EVENTS */
+
+static inline
+void fs_event_notify(struct super_block *sb, unsigned int event_id) {};
+static inline
+void fs_event_alloc_space(struct super_block *sb, u64 ncount) {};
+static inline
+void fs_event_free_space(struct super_block *sb, u64 ncount) {};
+static inline
+void fs_event_mount_dropped(struct vfsmount *mnt) {};
+
+static inline
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msig)(struct sk_buff *skb, void *data),
+			  void *cbdata)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_FS_EVENTS */
+
+#endif /* _LINUX_GENERIC_FS_EVENTS_ */
+
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 68ceb97..dae0fab 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -129,6 +129,7 @@ header-y += firewire-constants.h
 header-y += flat.h
 header-y += fou.h
 header-y += fs.h
+header-y += fs_event.h
 header-y += fsl_hypervisor.h
 header-y += fuse.h
 header-y += futex.h
diff --git a/include/uapi/linux/fs_event.h b/include/uapi/linux/fs_event.h
new file mode 100644
index 0000000..d8b07da
--- /dev/null
+++ b/include/uapi/linux/fs_event.h
@@ -0,0 +1,58 @@
+/*
+ * Generic netlink support for Generic File System Events Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef _UAPI_LINUX_GENERIC_FS_EVENTS_
+#define _UAPI_LINUX_GENERIC_FS_EVENTS_
Define name usually corresponds to the header filename.
+#define FS_EVENTS_FAMILY_NAME	 "fs_event"
+#define FS_EVENTS_MCAST_GRP_NAME "fs_event_mc_grp"
+
+/*
+ * Generic netlink attribute types
+ */
+enum {
+	FS_NL_A_NONE,
+	FS_NL_A_EVENT_ID,
+	FS_NL_A_DEV_MAJOR,
+	FS_NL_A_DEV_MINOR,
+	FS_NL_A_CAUSED_ID,
+	FS_NL_A_DATA,
+	__FS_NL_A_MAX,
+};
+#define FS_NL_A_MAX (__FS_NL_A_MAX - 1)
+/*
+ * Generic netlink commands
+ */
+#define FS_NL_C_EVENT		1
+
+/*
+ * Supported set of FS events
+ */
+enum {
+	FS_EVENT_NONE,
+	FS_WARN_ENOSPC,		/* No space left to reserve data blks */
+	FS_WARN_ENOSPC_META,	/* No space left for metadata */
+	FS_THR_LRBELOW,		/* The threshold lower range has been reached */
+	FS_THR_LRABOVE,		/* The threshold lower range re-activcated*/
+	FS_THR_URBELOW,
+	FS_THR_URABOVE,
+	FS_ERR_REMOUNT_RO,	/* The file system has been remounted as RO */
+	FS_ERR_CORRUPTED	/* Critical error - fs corrupted */
+
+};
+
+#endif /* _UAPI_LINUX_GENERIC_FS_EVENTS_ */
+
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

Re: [RFC v3 1/4] fs: Add generic file system event notifications

From: Beata Michalska <hidden>
Date: 2015-07-30 08:22:33

On 07/22/2015 05:55 PM, Bartlomiej Zolnierkiewicz wrote:
Hi,

Some comments below.

On Tuesday, June 16, 2015 03:09:30 PM Beata Michalska wrote:
quoted
Introduce configurable generic interface for file
system-wide event notifications, to provide file
systems with a common way of reporting any potential
issues as they emerge.

The notifications are to be issued through generic
netlink interface by newly introduced multicast group.

Threshold notifications have been included, allowing
triggering an event whenever the amount of free space drops
below a certain level - or levels to be more precise as two
of them are being supported: the lower and the upper range.
The notifications work both ways: once the threshold level
has been reached, an event shall be generated whenever
the number of available blocks goes up again re-activating
the threshold.

The interface has been exposed through a vfs. Once mounted,
it serves as an entry point for the set-up where one can
register for particular file system events.

Signed-off-by: Beata Michalska <redacted>
---
 Documentation/filesystems/events.txt |  232 ++++++++++
 fs/Kconfig                           |    2 +
 fs/Makefile                          |    1 +
 fs/events/Kconfig                    |    7 +
 fs/events/Makefile                   |    5 +
 fs/events/fs_event.c                 |  809 ++++++++++++++++++++++++++++++++++
 fs/events/fs_event.h                 |   22 +
 fs/events/fs_event_netlink.c         |  104 +++++
 fs/namespace.c                       |    1 +
 include/linux/fs.h                   |    6 +-
 include/linux/fs_event.h             |   72 +++
 include/uapi/linux/Kbuild            |    1 +
 include/uapi/linux/fs_event.h        |   58 +++
 13 files changed, 1319 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/filesystems/events.txt
 create mode 100644 fs/events/Kconfig
 create mode 100644 fs/events/Makefile
 create mode 100644 fs/events/fs_event.c
 create mode 100644 fs/events/fs_event.h
 create mode 100644 fs/events/fs_event_netlink.c
 create mode 100644 include/linux/fs_event.h
 create mode 100644 include/uapi/linux/fs_event.h
diff --git a/Documentation/filesystems/events.txt b/Documentation/filesystems/events.txt
new file mode 100644
index 0000000..c2e6227
--- /dev/null
+++ b/Documentation/filesystems/events.txt
@@ -0,0 +1,232 @@
+
+	Generic file system event notification interface
+
+Document created 23 April 2015 by Beata Michalska <b.michalska@samsung.com>
+
+1. The reason behind:
+=====================
+
+There are many corner cases when things might get messy with the filesystems.
+And it is not always obvious what and when went wrong. Sometimes you might
+get some subtle hints that there is something going on - but by the time
+you realise it, it might be too late as you are already out-of-space
+or the filesystem has been remounted as read-only (i.e.). The generic
+interface for the filesystem events fills the gap by providing a rather
+easy way of real-time notifications triggered whenever something interesting
+happens, allowing filesystems to report events in a common way, as they occur.
+
+2. How does it work:
+====================
+
+The interface itself has been exposed as fstrace-type Virtual File System,
+primarily to ease the process of setting up the configuration for the
+notifications. So for starters, it needs to get mounted (obviously):
+
+	mount -t fstrace none /sys/fs/events
+
+This will unveil the single fstrace filesystem entry - the 'config' file,
+through which the notification are being set-up.
The patch creates a separate virtual filesystem for single file,
this is an overkill IMHO and a new sysfs or debugfs entry should
be sufficient.
quoted
+
+Activating notifications for particular filesystem is as straightforward
+as writing into the 'config' file. Note that by default all events, despite
+the actual filesystem type, are being disregarded.
+
+Synopsis of config:
+------------------
+
+	MOUNT EVENT_TYPE [L1] [L2]
OTOH Why not use the advantages of having a separate virtual
filesystem and create separate directories for each mount point
(+ maybe even extra parent directories for mount namespaces) and
put separate entries for each event type in these directories.

This would also allow usage of eventfd() notification interface
on such files.

Please take look at:

    tools/cgroup/cgroup_event_listener.c

and

    Documentation/cgroups/memcg_test.txt (point 9.10)

to see how much easier it is to observe memory usage thresholds
on memory cgroups compared to available blocks on filesystems
using fs events..
I'll give it some thoughts as the solution you are proposing eliminates
some issues related with the generic netlink (mostly the one concerning the
network namespaces) though I'd rather avoid creating numerous entries
for each mount/mount namespace. I guess the best option is to meet halfway.

Also while at it please add your example user-space code (posted
on request in a some other mail) to tools/fs_events/ (preferably
in a separate patch).
Will do, once there is an overall agreement on the form of the
events interface.
quoted
+
+ MOUNT      : the filesystem's mount point
+ EVENT_TYPE : event types - currently two of them are being supported:
+
+	      * generic events ("G") covering most common warnings
+	      and errors that might be reported by any filesystem;
+	      this option does not take any arguments;
fs_event.h in uapi dir allows following events:

/*
 * Supported set of FS events
 */
enum {
	FS_EVENT_NONE,
	FS_WARN_ENOSPC,		/* No space left to reserve data blks */
	FS_WARN_ENOSPC_META,	/* No space left for metadata */
	FS_THR_LRBELOW,		/* The threshold lower range has been reached */
	FS_THR_LRABOVE,		/* The threshold lower range re-activcated*/
	FS_THR_URBELOW,
	FS_THR_URABOVE,
	FS_ERR_REMOUNT_RO,	/* The file system has been remounted as RO */
	FS_ERR_CORRUPTED	/* Critical error - fs corrupted */

};

For non-threshold related events the current interface allows
only configuration of all or none events to be anabled, i.e.
you cannot selectively enable notification on FS_WARN_ENOSPC
but not on FS_ERR_REMOUNT_RO.

I also think that configuration interface should be made to
match the notification interface when it comes to event types.
Will take it into consideration - thanks.
quoted
+
+	      * threshold notifications ("T") - events sent whenever
+	      the amount of available space drops below certain level;
+	      it is possible to specify two threshold levels though
+	      only one is required to properly setup the notifications;
+	      as those refer to the number of available blocks, the lower
+	      level [L1] needs to be higher than the upper one [L2]
Why is there a limitation of only two thresholds?

It should be relatively easy to make the code support
unlimited number of thresholds.
quoted
+
+Sample request could look like the following:
+
+ echo /sample/mount/point G T 710000 500000 > /sys/fs/events/config
+
+Multiple request might be specified provided they are separated with semicolon.
s/request/requests/

I think that allowing multiple event types and requests in one
configuration request is not a good idea.  Currently parsing
code is relatively simple but once somebody decides to enhance
the interface with new event types the parsing code may get
complex & ugly.
Noted.
quoted
+
+The configuration itself might be modified at any time. One can add/remove
+particular event types for given fielsystem, modify the threshold levels,
s/fielsystem/filesystem/
quoted
+and remove single or all entries from the 'config' file.
+
+ - Adding new event type:
+
+ $ echo MOUNT EVENT_TYPE > /sys/fs/events/config
+
+(Note that is is enough to provide the event type to be enabled without
s/is is/is/
quoted
+the already set ones.)
+
+ - Removing event type:
+
+ $ echo '!MOUNT EVENT_TYPE' > /sys/fs/events/config
+
+ - Updating threshold limits:
+
+ $ echo MOUNT T L1 L2 > /sys/fs/events/config
+
+ - Removing single entry:
+
+ $ echo '!MOUNT' > /sys/fs/events/config
+
+ - Removing all entries:
+
+ $ echo > /sys/fs/events/config
+
+Reading the file will list all registered entries with their current set-up
+along with some additional info like the filesystem type and the backing device
+name if available.
+
+Final, though a very important note on the configuration: when and if the
+actual events are being triggered falls way beyond the scope of the generic
+filesystem events interface. It is up to a particular filesystem
+implementation which events are to be supported - if any at all. So if
+given filesystem does not support the event notifications, an attempt to
+enable those through 'config' file will fail.
+
+
+3. The generic netlink interface support:
+=========================================
+
+Whenever an event notification is triggered (by given filesystem) the current
+configuration is being validated to decide whether a userpsace notification
s/userpsace/userspace/
quoted
+should be launched. If there has been no request (in a mean of 'config' file
+entry) for given event, one will be silently disregarded. If, on the other
+hand, someone is 'watching' given filesystem for specific events, a generic
+netlink message will be sent. A dedicated multicast group has been provided
+solely for this purpose so in order to receive such notifications, one should
+subscribe to this new multicast group. As for now only the init network
+namespace is being supported.
+
+3.1 Message format
+
+The FS_NL_C_EVENT shall be stored within the generic netlink message header
+as the command field. The message payload will provide more detailed info:
+the backing device major and minor numbers, the event code and the id of
+the process which action led to the event occurrence. In case of threshold
+notifications, the current number of available blocks will be included
+in the payload as well.
+
+
+	 0                   1                   2                   3
+	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|	            NETLINK MESSAGE HEADER			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 		GENERIC NETLINK MESSAGE HEADER   		|
+	| 	   (with FS_NL_C_EVENT as genlmsghdr cdm field)		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	| 	      Optional user specific message header		|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	|		   GENERIC MESSAGE PAYLOAD:			|
+	+---------------------------------------------------------------+
+	| 		  FS_NL_A_EVENT_ID  (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|	  	  FS_NL_A_DEV_MAJOR (NLA_U32)			|
+	+---------------------------------------------------------------+
+	| 	  	  FS_NL_A_DEV_MINOR (NLA_U32) 			|
+	+---------------------------------------------------------------+
+	|  		  FS_NL_A_CAUSED_ID (NLA_U32)			|
+	+---------------------------------------------------------------+
+	|  		    FS_NL_A_DATA (NLA_U64)			|
+	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+The above figure is based on:
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto#Message_Format
+
+
+4. API Reference:
+=================
+
+ 4.1 Generic file system event interface data & operations
+
+ #include <linux/fs_event.h>
+
+ struct fs_trace_info {
+	void	__rcu	*e_priv		 /* READ ONLY */
It should be marked as private for fs events core code and
not for use by filesystems' code.  If possible it would be
the best to move it out of this struct,
quoted
+	unsigned int 	events_cap_mask; /* Supported notifications */
+	const struct fs_trace_operations *ops;
+ };
+
+ struct fs_trace_operations {
+	void (*query)(struct super_block *, u64 *);
+ };
+
+ In order to get the fireworks and stuff, each filesystem needs to setup
+ the events_cap_mask field of the fs_trace_info structure, which has been
+ embedded within the super_block structure. This should reflect the type of
+ events the filesystem wants to support. In case of threshold notifications,
+ apart from setting the FS_EVENT_THRESH flag, the 'query' callback should
+ be provided as this enables the events interface to get the up-to-date
+ state of the number of available blocks whenever those notifications are
+ being requested.
+
+ The 'e_priv' field of the fs_trace_info structure should be completely ignored
+ as it's for INTERNAL USE ONLY. So don't even think of messing with it, if you
+ do not want to get yourself into some real trouble. If still, you are tempted
+ to do so - feel free, it's gonna be pure fun. Consider yourself warned.
+
+
+ 4.2 Event notification:
+
+ #include <linux/fs_event.h>
+ void fs_event_notify(struct super_block *sb, unsigned int event_id);
+
+ Notify the generic FS event interface of an occurring event.
+ This shall be used by any file system that wishes to inform any potential
+ listeners/watchers of a particular event.
+ - sb:         the filesystem's super block
+ - event_id:   an event identifier
+
+ 4.3 Threshold notifications:
+
+ #include <linux/fs_event.h>
+ void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+ void fs_event_free_space(struct super_block *sb, u64 ncount);
+
+ Each filesystme supporting the threshold notifications should call
+ fs_event_alloc_space/fs_event_free_space respectively whenever the
+ amount of available blocks changes.
+ - sb:     the filesystem's super block
+ - ncount: number of blocks being acquired/released
+
+ Note that to properly handle the threshold notifications the fs events
+ interface needs to be kept up to date by the filesystems. Each should
+ register fs_trace_operations to enable querying the current number of
+ available blocks.
+
+ 4.4 Sending message through generic netlink interface
+
+ #include <linux/fs_event.h>
+
+ int fs_netlink_send_event(size_t size, unsigned int event_id,
+	int (*compose_msg)(struct sk_buff *skb, void *data), void *cbdata);
+
+ Although the fs event interface is fully responsible for sending the messages
+ over the netlink, filesystems might use the FS_EVENT multicast group to send
+ their own custom messages.
+ - size:        the size of the message payload
+ - event_id:    the event identifier
+ - compose_msg: a callback responsible for filling-in the message payload
+ - cbdata:      message custom data
+
+ Calling fs_netlink_send_event will result in a message being sent by
+ the FS_EVENT multicast group. Note that the body of the message should be
+ prepared (set-up )by the caller - through compose_msg callback. The message's
(set-up)
quoted
+ sk_buff will be allocated on behalf of the caller (thus the size parameter).
+ The compose_msg should only fill the payload with proper data. Unless
+ the event id is specified as FS_EVENT_NONE, it's value shall be added
+ to the payload prior to calling the compose_msg.
+
+
diff --git a/fs/Kconfig b/fs/Kconfig
index ec35851..a89e678 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -69,6 +69,8 @@ config FILE_LOCKING
           for filesystems like NFS and for the flock() system
           call. Disabling this option saves about 11k.
 
+source "fs/events/Kconfig"
+
 source "fs/notify/Kconfig"
 
 source "fs/quota/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index a88ac48..bcb3048 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -126,3 +126,4 @@ obj-y				+= exofs/ # Multiple modules
 obj-$(CONFIG_CEPH_FS)		+= ceph/
 obj-$(CONFIG_PSTORE)		+= pstore/
 obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/
+obj-$(CONFIG_FS_EVENTS)		+= events/
diff --git a/fs/events/Kconfig b/fs/events/Kconfig
new file mode 100644
index 0000000..1c60195
--- /dev/null
+++ b/fs/events/Kconfig
@@ -0,0 +1,7 @@
+# Generic Files System events interface
+config FS_EVENTS
+	bool "Generic filesystem events"
+	select NET
+	default y
Do we really want to default to yes?

[ If so then maybe we want to make the config option visible
  only when EXPERT mode is enabled? ]
quoted
+	help
+	  Enable generic filesystem events interface
Please enhance the help entry.
quoted
diff --git a/fs/events/Makefile b/fs/events/Makefile
new file mode 100644
index 0000000..9c98337
--- /dev/null
+++ b/fs/events/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Linux Generic File System Event Interface
+#
+
+obj-y := fs_event.o fs_event_netlink.o
diff --git a/fs/events/fs_event.c b/fs/events/fs_event.c
new file mode 100644
index 0000000..1037311
--- /dev/null
+++ b/fs/events/fs_event.c
@@ -0,0 +1,809 @@
+/*
+ * Generic File System Evens Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/nsproxy.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <net/genetlink.h>
+#include "../pnode.h"
+#include "fs_event.h"
+
+static LIST_HEAD(fs_trace_list);
+static DEFINE_MUTEX(fs_trace_lock);
+
+static struct kmem_cache *fs_trace_cachep __read_mostly;
+
+static atomic_t stray_traces = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(trace_wq);
+/*
+ * Threshold notification state bits.
+ * Note the reverse as this refers to the number
+ * of available blocks.
+ */
+#define THRESH_LR_BELOW		0x0001 /* Falling below the lower range */
+#define THRESH_LR_BEYOND	0x0002
+#define THRESH_UR_BELOW		0x0004
+#define THRESH_UR_BEYOND	0x0008 /* Going beyond the upper range */
+
+#define THRESH_LR_ON	(THRESH_LR_BELOW | THRESH_LR_BEYOND)
+#define THRESH_UR_ON	(THRESH_UR_BELOW | THRESH_UR_BEYOND)
+
+#define FS_TRACE_ADD	0x100000
+
+struct fs_trace_entry {
+	struct kref	 count;
+	atomic_t	 active;
+	struct super_block *sb;
+	unsigned int	 notify;
+	struct path	 mnt_path;
+	struct list_head  node;
+
+	struct fs_event_thresh {
+		u64		 avail_space;
+		u64		 lrange;
+		u64		 urange;
+		unsigned int	 state;
+	}		 th;
+	struct rcu_head	 rcu_head;
+	spinlock_t	 lock;
+};
+
+static const match_table_t fs_etypes = {
+	{ FS_EVENT_GENERIC, "G"   },
+	{ FS_EVENT_THRESH,  "T"   },
+	{ 0, NULL },
+};
+
+static inline int fs_trace_query_data(struct super_block *sb,
+				       struct fs_trace_entry *en)
+{
+	if (sb->s_etrace.ops && sb->s_etrace.ops->query) {
+		sb->s_etrace.ops->query(sb, &en->th.avail_space);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static inline void fs_trace_entry_free(struct fs_trace_entry *en)
I don't see a real need for this wrapper (it is used only once).
quoted
+{
+	kmem_cache_free(fs_trace_cachep, en);
+}
+
+static void fs_destroy_trace_entry(struct kref *en_ref)
+{
+	struct fs_trace_entry *en = container_of(en_ref,
+					 struct fs_trace_entry, count);
+
+	/* Last reference has been dropped */
+	fs_trace_entry_free(en);
+	atomic_dec(&stray_traces);
+}
+
+static void fs_trace_entry_put(struct fs_trace_entry *en)
+{
+	kref_put(&en->count, fs_destroy_trace_entry);
+}
+
+static void fs_release_trace_entry(struct rcu_head *rcu_head)
+{
+	struct fs_trace_entry *en = container_of(rcu_head,
+						 struct fs_trace_entry,
+						 rcu_head);
+	/*
+	 * As opposed to typical reference drop, this one is being
+	 * called from the rcu callback. This is to make sure all
+	 * readers have managed to safely grab the reference before
+	 * the change to rcu pointer is visible to all and before
+	 * the reference is dropped here.
+	 */
+	fs_trace_entry_put(en);
+}
+
+static void fs_drop_trace_entry(struct fs_trace_entry *en)
+{
+	struct super_block *sb;
+
+	lockdep_assert_held(&fs_trace_lock);
+	/*
+	 * The trace entry might have already been removed
+	 * from the list of active traces with the proper
+	 * ref drop, though it was still in use handling
+	 * one of the fs events. This means that the object
+	 * has been already scheduled for being released.
+	 * So leave...
+	 */
+
+	if (!atomic_add_unless(&en->active, -1, 0))
+		return;
+	/*
+	 * At this point the trace entry is being marked as inactive
+	 * so no new references will be allowed.
+	 * Still it might be floating around somewhere
+	 * so drop the reference when the rcu readers are done.
+	 */
+	spin_lock(&en->lock);
+	list_del(&en->node);
+	sb = en->sb;
+	en->sb = NULL;
+	spin_unlock(&en->lock);
+
+	rcu_assign_pointer(sb->s_etrace.e_priv, NULL);
+	call_rcu(&en->rcu_head, fs_release_trace_entry);
+	/* It's safe now to drop the reference to the super */
+	deactivate_super(sb);
+	atomic_inc(&stray_traces);
+}
+
+static inline
+struct fs_trace_entry *fs_trace_entry_get(struct fs_trace_entry *en)
+{
+	if (en) {
+		if (!kref_get_unless_zero(&en->count))
+			return NULL;
+		/* Don't allow referencing inactive object */
+		if (!atomic_read(&en->active)) {
+			fs_trace_entry_put(en);
+			return NULL;
+		}
+	}
+	return en;
+}
+
+static struct fs_trace_entry *fs_trace_entry_get_rcu(struct super_block *sb)
+{
+	struct fs_trace_entry *en;
+
+	if (!sb)
+		return NULL;
+
+	rcu_read_lock();
+	en = rcu_dereference(sb->s_etrace.e_priv);
+	en = fs_trace_entry_get(en);
+	rcu_read_unlock();
+
+	return en;
+}
+
+static int fs_remove_trace_entry(struct super_block *sb)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return -EINVAL;
+
+	mutex_lock(&fs_trace_lock);
+	fs_drop_trace_entry(en);
+	mutex_unlock(&fs_trace_lock);
+	fs_trace_entry_put(en);
+	return 0;
+}
+
+static void fs_remove_all_traces(void)
+{
+	struct fs_trace_entry *en, *guard;
+
+	mutex_lock(&fs_trace_lock);
+	list_for_each_entry_safe(en, guard, &fs_trace_list, node)
+		fs_drop_trace_entry(en);
+	mutex_unlock(&fs_trace_lock);
+}
+
+static int create_common_msg(struct sk_buff *skb, void *data)
+{
+	struct fs_trace_entry *en = (struct fs_trace_entry *)data;
+	struct super_block *sb = en->sb;
+
+	if (nla_put_u32(skb, FS_NL_A_DEV_MAJOR, MAJOR(sb->s_dev))
+	||  nla_put_u32(skb, FS_NL_A_DEV_MINOR, MINOR(sb->s_dev)))
+		return -EINVAL;
+
+	if (nla_put_u64(skb, FS_NL_A_CAUSED_ID, pid_vnr(task_pid(current))))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int create_thresh_msg(struct sk_buff *skb, void *data)
+{
+	struct fs_trace_entry *en = (struct fs_trace_entry *)data;
+	int ret;
+
+	ret = create_common_msg(skb, data);
+	if (!ret)
+		ret = nla_put_u64(skb, FS_NL_A_DATA, en->th.avail_space);
+	return ret;
+}
+
+static void fs_event_send(struct fs_trace_entry *en, unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64));
+
+	fs_netlink_send_event(size, event_id, create_common_msg, en);
+}
+
+static void fs_event_send_thresh(struct fs_trace_entry *en,
+				  unsigned int event_id)
+{
+	size_t size = nla_total_size(sizeof(u32)) * 2 +
+		      nla_total_size(sizeof(u64)) * 2;
+
+	fs_netlink_send_event(size, event_id, create_thresh_msg, en);
+}
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+	if (atomic_read(&en->active) && (en->notify & FS_EVENT_GENERIC))
+		fs_event_send(en, event_id);
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_notify);
+
+void fs_event_alloc_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+	s64 count;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+	/*
+	 * we shouldn't drop below 0 here,
+	 * unless there is a sync issue somewhere (?)
+	 */
+	count = en->th.avail_space - ncount;
+	en->th.avail_space = count < 0 ? 0 : count;
+
+	if (en->th.avail_space > en->th.lrange)
+		/* Not 'even' close - leave */
+		goto leave;
+
+	if (en->th.avail_space > en->th.urange) {
+		/* Close enough - the lower range has been reached */
+		if (!(en->th.state & THRESH_LR_BEYOND)) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRBELOW);
+			en->th.state &= ~THRESH_LR_BELOW;
+			en->th.state |= THRESH_LR_BEYOND;
+		}
+		goto leave;
+	}
+	if (!(en->th.state & THRESH_UR_BEYOND)) {
+		fs_event_send_thresh(en, FS_THR_URBELOW);
+		en->th.state &=  ~THRESH_UR_BELOW;
+		en->th.state |= THRESH_UR_BEYOND;
+	}
+
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_alloc_space);
+
+void fs_event_free_space(struct super_block *sb, u64 ncount)
+{
+	struct fs_trace_entry *en;
+
+	en = fs_trace_entry_get_rcu(sb);
+	if (!en)
+		return;
+
+	spin_lock(&en->lock);
+
+	if (!atomic_read(&en->active) || !(en->notify & FS_EVENT_THRESH))
+		goto leave;
+
+	en->th.avail_space += ncount;
+
+	if (en->th.avail_space > en->th.lrange) {
+		if (!(en->th.state & THRESH_LR_BELOW)
+		&& en->th.state & THRESH_LR_BEYOND) {
+			/* Send notification */
+			fs_event_send_thresh(en, FS_THR_LRABOVE);
+			en->th.state &= ~(THRESH_LR_BEYOND|THRESH_UR_BEYOND);
+			en->th.state |= THRESH_LR_BELOW;
+			goto leave;
+		}
+	}
+	if (en->th.avail_space > en->th.urange) {
+		if (!(en->th.state & THRESH_UR_BELOW)
+		&& en->th.state & THRESH_UR_BEYOND) {
+			/* Notify */
+			fs_event_send_thresh(en, FS_THR_URABOVE);
+			en->th.state &= ~THRESH_UR_BEYOND;
+			en->th.state |= THRESH_UR_BELOW;
+		}
+	}
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+}
+EXPORT_SYMBOL(fs_event_free_space);
+
+void fs_event_mount_dropped(struct vfsmount *mnt)
+{
+	/*
+	 * The mount is dropped but the super might not get released
+	 * at once so there is very small chance some notifications
+	 * will come through.
+	 * Note that the mount being dropped here might belong to a different
+	 * namespace - if this is the case, just ignore it.
+	 */
+	struct fs_trace_entry  *en = fs_trace_entry_get_rcu(mnt->mnt_sb);
+	struct vfsmount *en_mnt;
+
+	if (!en || !atomic_read(&en->active))
+		return;
+	/*
+	 * The entry once set, does not change the mountpoint it's being
+	 * pinned to, so no need to take the lock here.
+	 */
+	en_mnt = en->mnt_path.mnt;
+	if (!(real_mount(mnt)->mnt_ns != (real_mount(en_mnt))->mnt_ns))
+		fs_remove_trace_entry(mnt->mnt_sb);
+	fs_trace_entry_put(en);
+}
+
+static int fs_new_trace_entry(struct path *path, struct fs_event_thresh *thresh,
+				unsigned int nmask)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	struct mount *r_mnt;
+
+	en = kmem_cache_zalloc(fs_trace_cachep, GFP_KERNEL);
+	if (unlikely(!en))
+		return -ENOMEM;
+	/*
+	 * Note that no reference is being taken here for the path as it would
+	 * make the unmount unnecessarily puzzling (due to an extra 'valid'
+	 * reference for the mnt).
+	 * This is *rather* safe as the notification on mount being dropped
+	 * will get called prior to releasing the super block - so right
+	 * in time to perform appropriate clean-up
+	 */
+	r_mnt = real_mount(path->mnt);
+
+	en->mnt_path.dentry = r_mnt->mnt.mnt_root;
+	en->mnt_path.mnt = &r_mnt->mnt;
+
+	sb = path->mnt->mnt_sb;
+	en->sb = sb;
+	/*
+	 * Increase the refcount for sb to mark it's being relied on.
+	 * Note that the reference to path is taken by the caller, so it
+	 * is safe to assume there is at least single active reference
+	 * to super as well.
+	 */
+	atomic_inc(&sb->s_active);
+
+	nmask &= sb->s_etrace.events_cap_mask;
+	if (!nmask)
+		goto leave;
+
+	spin_lock_init(&en->lock);
+	INIT_LIST_HEAD(&en->node);
+
+	en->notify = nmask;
+	memcpy(&en->th, thresh, offsetof(struct fs_event_thresh, state));
+	if (nmask & FS_EVENT_THRESH)
+		fs_trace_query_data(sb, en);
+
+	kref_init(&en->count);
+
+	if (rcu_access_pointer(sb->s_etrace.e_priv) != NULL) {
+		struct fs_trace_entry *prev_en;
+
+		prev_en = fs_trace_entry_get_rcu(sb);
+		if (prev_en) {
+			WARN_ON(prev_en);
+			fs_trace_entry_put(prev_en);
+			goto leave;
+		}
+	}
+	atomic_set(&en->active, 1);
+
+	mutex_lock(&fs_trace_lock);
+	list_add(&en->node, &fs_trace_list);
+	mutex_unlock(&fs_trace_lock);
+
+	rcu_assign_pointer(sb->s_etrace.e_priv, en);
+	synchronize_rcu();
+
+	return 0;
+leave:
+	deactivate_super(sb);
+	kmem_cache_free(fs_trace_cachep, en);
+	return -EINVAL;
+}
+
+static int fs_update_trace_entry(struct path *path,
+				  struct fs_event_thresh *thresh,
+				  unsigned int nmask)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	int extend = nmask & FS_TRACE_ADD;
+	int ret = -EINVAL;
+
+	en = fs_trace_entry_get_rcu(path->mnt->mnt_sb);
+	if (!en)
+		return (extend) ? fs_new_trace_entry(path, thresh, nmask)
+				: -EINVAL;
+
+	if (!atomic_read(&en->active))
+		return -EINVAL;
+
+	nmask &= ~FS_TRACE_ADD;
+
+	spin_lock(&en->lock);
+	sb  = en->sb;
+	if (!sb || !(nmask & sb->s_etrace.events_cap_mask))
+		goto leave;
+
+	if (nmask & FS_EVENT_THRESH) {
+		if (extend) {
+			/* Get the current state */
+			if (!(en->notify & FS_EVENT_THRESH))
+				if (fs_trace_query_data(sb, en))
+					goto leave;
+
+			if (thresh->state & THRESH_LR_ON) {
+				en->th.lrange = thresh->lrange;
+				en->th.state &= ~THRESH_LR_ON;
+			}
+
+			if (thresh->state & THRESH_UR_ON) {
+				en->th.urange = thresh->urange;
+				en->th.state &= ~THRESH_UR_ON;
+			}
+		} else {
+			memset(&en->th, 0, sizeof(en->th));
+		}
+	}
+
+	if (extend)
+		en->notify |= nmask;
+	else
+		en->notify &= ~nmask;
+	ret = 0;
+leave:
+	spin_unlock(&en->lock);
+	fs_trace_entry_put(en);
+	return ret;
+}
+
+static int fs_parse_trace_request(int argc, char **argv)
+{
+	struct fs_event_thresh thresh = {0};
+	struct path path;
+	substring_t args[MAX_OPT_ARGS];
+	unsigned int nmask = FS_TRACE_ADD;
+	int token;
+	char *s;
+	int ret = -EINVAL;
+
+	if (!argc) {
+		fs_remove_all_traces();
+		return 0;
+	}
+
+	s = *(argv);
+	if (*s == '!') {
+		/* Clear the trace entry */
+		nmask &= ~FS_TRACE_ADD;
+		++s;
+	}
+
+	if (kern_path_mountpoint(AT_FDCWD, s, &path, LOOKUP_FOLLOW))
+		return -EINVAL;
+
+	if (!(--argc)) {
+		if (!(nmask & FS_TRACE_ADD))
+			ret = fs_remove_trace_entry(path.mnt->mnt_sb);
+		goto leave;
+	}
+
+repeat:
+	args[0].to = args[0].from = NULL;
+	token = match_token(*(++argv), fs_etypes, args);
+	if (!token && !nmask)
+		goto leave;
+
+	nmask |= token & FS_EVENTS_ALL;
+	--argc;
+	if ((token & FS_EVENT_THRESH)  && (nmask & FS_TRACE_ADD)) {
+		/*
+		 * Get the threshold config data:
+		 * lower range
+		 * upper range
+		 */
+		if (!argc)
+			goto leave;
+
+		ret = kstrtoull(*(++argv), 10, &thresh.lrange);
+		if (ret)
+			goto leave;
+		thresh.state |= THRESH_LR_ON;
+		if ((--argc)) {
+			ret = kstrtoull(*(++argv), 10, &thresh.urange);
+			if (ret)
+				goto leave;
+			thresh.state |= THRESH_UR_ON;
+			--argc;
+		}
+		/* The thresholds are based on number of available blocks */
+		if (thresh.lrange < thresh.urange) {
+			ret = -EINVAL;
+			goto leave;
+		}
+	}
+	if (argc)
+		goto repeat;
+
+	ret = fs_update_trace_entry(&path, &thresh, nmask);
+leave:
+	path_put(&path);
+	return ret;
+}
+
+#define DEFAULT_BUF_SIZE PAGE_SIZE
+
+static ssize_t fs_trace_write(struct file *file, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	char **argv;
+	char *kern_buf, *next, *cfg;
+	size_t size, dcount = 0;
+	int argc;
+
+	if (!count)
+		return 0;
+
+	kern_buf = kmalloc(DEFAULT_BUF_SIZE, GFP_KERNEL);
+	if (!kern_buf)
+		return -ENOMEM;
+
+	while (dcount < count) {
+
+		size = count - dcount;
+		if (size >= DEFAULT_BUF_SIZE)
+			size = DEFAULT_BUF_SIZE - 1;
+		if (copy_from_user(kern_buf, buffer + dcount, size)) {
+			dcount = -EINVAL;
+			goto leave;
+		}
+
+		kern_buf[size] = '\0';
+
+		next = cfg = kern_buf;
+
+		do {
+			next = strchr(cfg, ';');
+			if (next)
+				*next = '\0';
+
+			argv = argv_split(GFP_KERNEL, cfg, &argc);
+			if (!argv) {
+				dcount = -ENOMEM;
+				goto leave;
+			}
+
+			if (fs_parse_trace_request(argc, argv)) {
+				dcount = -EINVAL;
+				argv_free(argv);
+				goto leave;
+			}
+
+			argv_free(argv);
+			if (next)
+				cfg = ++next;
+
+		} while (next);
+		dcount += size;
+	}
+leave:
+	kfree(kern_buf);
+	return dcount;
+}
+
+static void *fs_trace_seq_start(struct seq_file *m, loff_t *pos)
+{
+	mutex_lock(&fs_trace_lock);
+	return seq_list_start(&fs_trace_list, *pos);
+}
+
+static void *fs_trace_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &fs_trace_list, pos);
+}
+
+static void fs_trace_seq_stop(struct seq_file *m, void *v)
+{
+	mutex_unlock(&fs_trace_lock);
+}
+
+static int fs_trace_seq_show(struct seq_file *m, void *v)
+{
+	struct fs_trace_entry *en;
+	struct super_block *sb;
+	struct mount *r_mnt;
+	const struct match_token *match;
+	unsigned int nmask;
+
+	en = list_entry(v, struct fs_trace_entry, node);
+	/* Do not show the entries outside current mount namespace */
+	r_mnt = real_mount(en->mnt_path.mnt);
+	if (r_mnt->mnt_ns != current->nsproxy->mnt_ns) {
+		if (!__is_local_mountpoint(r_mnt->mnt_mountpoint))
+			return 0;
+	}
+
+	sb = en->sb;
+
+	seq_path(m, &en->mnt_path, "\t\n\\");
+	seq_putc(m, ' ');
+
+	seq_escape(m, sb->s_type->name, " \t\n\\");
+	if (sb->s_subtype && sb->s_subtype[0]) {
+		seq_putc(m, '.');
+		seq_escape(m, sb->s_subtype, " \t\n\\");
+	}
+
+	seq_putc(m, ' ');
+	if (sb->s_op->show_devname) {
+		sb->s_op->show_devname(m, en->mnt_path.mnt->mnt_root);
+	} else {
+		seq_escape(m, r_mnt->mnt_devname ? r_mnt->mnt_devname : "none",
+				" \t\n\\");
+	}
+	seq_puts(m, " (");
+
+	nmask = en->notify;
+	for (match = fs_etypes; match->pattern; ++match) {
+		if (match->token & nmask) {
+			seq_puts(m, match->pattern);
+			nmask &= ~match->token;
+			if (nmask)
+				seq_putc(m, ',');
+		}
+	}
+	seq_printf(m, " %llu %llu", en->th.lrange, en->th.urange);
+	seq_puts(m, ")\n");
+	return 0;
+}
+
+static const struct seq_operations fs_trace_seq_ops = {
+	.start	= fs_trace_seq_start,
+	.next	= fs_trace_seq_next,
+	.stop	= fs_trace_seq_stop,
+	.show	= fs_trace_seq_show,
+};
+
+static int fs_trace_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &fs_trace_seq_ops);
+}
+
+static const struct file_operations fs_trace_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fs_trace_open,
+	.write		= fs_trace_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int fs_trace_init(void)
+{
+	fs_trace_cachep = KMEM_CACHE(fs_trace_entry, 0);
+	if (!fs_trace_cachep)
+		return -EINVAL;
+	init_waitqueue_head(&trace_wq);
+	return 0;
+}
+
+/* VFS support */
+static int fs_trace_fill_super(struct super_block *sb, void *data, int silen)
+{
+	int ret;
+	static struct tree_descr desc[] = {
+		[2] = {
+			.name	= "config",
+			.ops	= &fs_trace_fops,
+			.mode	= S_IWUSR | S_IRUGO,
+		},
+		{""},
+	};
+
+	ret = simple_fill_super(sb, 0x7246332, desc);
Please use a define for a magic number.
quoted
+	return !ret ? fs_trace_init() : ret;
+}
+
+static struct dentry *fs_trace_do_mount(struct file_system_type *fs_type,
+		 int ntype, const char *dev_name, void *data)
+{
+	return mount_single(fs_type, ntype, data, fs_trace_fill_super);
+}
+
+static void fs_trace_kill_super(struct super_block *sb)
+{
+	/*
+	 * The rcu_barrier here will/should make sure all call_rcu
+	 * callbacks are completed - still there might be some active
+	 * trace objects in use which can make calling the
+	 * kmem_cache_destroy unsafe. So we wait until all traces
+	 * are finally released.
+	 */
+	fs_remove_all_traces();
+	rcu_barrier();
+	wait_event(trace_wq, !atomic_read(&stray_traces));
+
+	kmem_cache_destroy(fs_trace_cachep);
+	kill_litter_super(sb);
+}
+
+static struct kset	*fs_trace_kset;
+
+static struct file_system_type fs_trace_fstype = {
+	.name		= "fstrace",
+	.mount		= fs_trace_do_mount,
+	.kill_sb	= fs_trace_kill_super,
+};
+
+static void __init fs_trace_vfs_init(void)
+{
+	fs_trace_kset = kset_create_and_add("events", NULL, fs_kobj);
+
+	if (!fs_trace_kset)
+		return;
+
+	if (!register_filesystem(&fs_trace_fstype)) {
+		if (!fs_event_netlink_register())
+			return;
+		unregister_filesystem(&fs_trace_fstype);
+	}
+	kset_unregister(fs_trace_kset);
+}
+
+static int __init fs_trace_evens_init(void)
+{
+	fs_trace_vfs_init();
+	return 0;
+};
+module_init(fs_trace_evens_init);
+
diff --git a/fs/events/fs_event.h b/fs/events/fs_event.h
new file mode 100644
index 0000000..23f24c8
--- /dev/null
+++ b/fs/events/fs_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __GENERIC_FS_EVENTS_H
+#define __GENERIC_FS_EVENTS_H
+
+int  fs_event_netlink_register(void);
+void fs_event_netlink_unregister(void);
+
+#endif /* __GENERIC_FS_EVENTS_H */
diff --git a/fs/events/fs_event_netlink.c b/fs/events/fs_event_netlink.c
new file mode 100644
index 0000000..0c97eb7
--- /dev/null
+++ b/fs/events/fs_event_netlink.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include "fs_event.h"
+
+static const struct genl_multicast_group fs_event_mcgroups[] = {
+	{ .name = FS_EVENTS_MCAST_GRP_NAME, },
+};
+
+static struct genl_family fs_event_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= FS_EVENTS_FAMILY_NAME,
+	.version	= 1,
+	.maxattr	= FS_NL_A_MAX,
+	.mcgrps		= fs_event_mcgroups,
+	.n_mcgrps	= ARRAY_SIZE(fs_event_mcgroups),
+};
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb,  void *data),
+			  void *cbdata)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret = 0;
+
+	if (!size || !compose_msg)
+		return -EINVAL;
+
+	/* Skip if there are no listeners */
+	if (!genl_has_listeners(&fs_event_family, &init_net, 0))
+		return 0;
+
+	if (event_id != FS_EVENT_NONE)
+		size += nla_total_size(sizeof(u32));
+	size += nla_total_size(sizeof(u64));
+	skb = genlmsg_new(size, GFP_NOWAIT);
+
+	if (!skb) {
+		pr_debug("Failed to allocate new FS generic netlink message\n");
+		return -ENOMEM;
+	}
+
+	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+			&fs_event_family, 0, FS_NL_C_EVENT);
+	if (!msg_head)
+		goto cleanup;
+
+	if (event_id != FS_EVENT_NONE)
+		if (nla_put_u32(skb, FS_NL_A_EVENT_ID, event_id))
+			goto cancel;
+
+	ret = compose_msg(skb, cbdata);
+	if (ret)
+		goto cancel;
+
+	genlmsg_end(skb, msg_head);
+	ret = genlmsg_multicast(&fs_event_family, skb, 0, 0, GFP_NOWAIT);
+	if (ret && ret != -ENOBUFS && ret != -ESRCH)
+		goto cleanup;
+
+	return ret;
+
+cancel:
+	genlmsg_cancel(skb, msg_head);
+cleanup:
+	nlmsg_free(skb);
+	return ret;
+}
+EXPORT_SYMBOL(fs_netlink_send_event);
+
+int fs_event_netlink_register(void)
+{
+	int ret;
+
+	ret = genl_register_family(&fs_event_family);
+	if (ret)
+		pr_err("Failed to register FS netlink interface\n");
+	return ret;
+}
+
+void fs_event_netlink_unregister(void)
+{
+	genl_unregister_family(&fs_event_family);
+}
diff --git a/fs/namespace.c b/fs/namespace.c
index 82ef140..ec6e2ef 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1031,6 +1031,7 @@ static void cleanup_mnt(struct mount *mnt)
 	if (unlikely(mnt->mnt_pins.first))
 		mnt_pin_kill(mnt);
 	fsnotify_vfsmount_delete(&mnt->mnt);
+	fs_event_mount_dropped(&mnt->mnt);
 	dput(mnt->mnt.mnt_root);
 	deactivate_super(mnt->mnt.mnt_sb);
 	mnt_free_id(mnt);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b4d71b5..b7dadd9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -263,6 +263,10 @@ struct iattr {
  * Includes for diskquotas.
  */
 #include <linux/quota.h>
+/*
+ * Include for Generic File System Events Interface
+ */
+#include <linux/fs_event.h>
 
 /*
  * Maximum number of layers of fs stack.  Needs to be limited to
@@ -1253,7 +1257,7 @@ struct super_block {
 	struct hlist_node	s_instances;
 	unsigned int		s_quota_types;	/* Bitmask of supported quota types */
 	struct quota_info	s_dquot;	/* Diskquota specific options */
-
+	struct fs_trace_info	s_etrace;
 	struct sb_writers	s_writers;
 
 	char s_id[32];				/* Informational name */
diff --git a/include/linux/fs_event.h b/include/linux/fs_event.h
new file mode 100644
index 0000000..83e22dd
--- /dev/null
+++ b/include/linux/fs_event.h
@@ -0,0 +1,72 @@
+/*
+ * Generic File System Events Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef _LINUX_GENERIC_FS_EVETS_
+#define _LINUX_GENERIC_FS_EVETS_
EVETS?

Also the define name usually corresponds to the header filename.
quoted
+#include <net/netlink.h>
+#include <uapi/linux/fs_event.h>
+
+/*
+ * Currently supported event types
+ */
+#define FS_EVENT_GENERIC 0x001
+#define FS_EVENT_THRESH	 0x002
+
+#define FS_EVENTS_ALL  (FS_EVENT_GENERIC | FS_EVENT_THRESH)
+
+struct fs_trace_operations {
+	void (*query)(struct super_block *, u64 *);
+};
+
+struct fs_trace_info {
+	void	__rcu	*e_priv;		 /* READ ONLY */
+	unsigned int	events_cap_mask; /* Supported notifications */
+	const struct fs_trace_operations *ops;
+};
+
+#ifdef CONFIG_FS_EVENTS
+
+void fs_event_notify(struct super_block *sb, unsigned int event_id);
+void fs_event_alloc_space(struct super_block *sb, u64 ncount);
+void fs_event_free_space(struct super_block *sb, u64 ncount);
+void fs_event_mount_dropped(struct vfsmount *mnt);
+
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msg)(struct sk_buff *skb, void *data),
+			  void *cbdata);
+
+#else /* CONFIG_FS_EVENTS */
+
+static inline
+void fs_event_notify(struct super_block *sb, unsigned int event_id) {};
+static inline
+void fs_event_alloc_space(struct super_block *sb, u64 ncount) {};
+static inline
+void fs_event_free_space(struct super_block *sb, u64 ncount) {};
+static inline
+void fs_event_mount_dropped(struct vfsmount *mnt) {};
+
+static inline
+int fs_netlink_send_event(size_t size, unsigned int event_id,
+			  int (*compose_msig)(struct sk_buff *skb, void *data),
+			  void *cbdata)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_FS_EVENTS */
+
+#endif /* _LINUX_GENERIC_FS_EVENTS_ */
+
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 68ceb97..dae0fab 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -129,6 +129,7 @@ header-y += firewire-constants.h
 header-y += flat.h
 header-y += fou.h
 header-y += fs.h
+header-y += fs_event.h
 header-y += fsl_hypervisor.h
 header-y += fuse.h
 header-y += futex.h
diff --git a/include/uapi/linux/fs_event.h b/include/uapi/linux/fs_event.h
new file mode 100644
index 0000000..d8b07da
--- /dev/null
+++ b/include/uapi/linux/fs_event.h
@@ -0,0 +1,58 @@
+/*
+ * Generic netlink support for Generic File System Events Interface
+ *
+ * Copyright(c) 2015 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef _UAPI_LINUX_GENERIC_FS_EVENTS_
+#define _UAPI_LINUX_GENERIC_FS_EVENTS_
Define name usually corresponds to the header filename.
quoted
+#define FS_EVENTS_FAMILY_NAME	 "fs_event"
+#define FS_EVENTS_MCAST_GRP_NAME "fs_event_mc_grp"
+
+/*
+ * Generic netlink attribute types
+ */
+enum {
+	FS_NL_A_NONE,
+	FS_NL_A_EVENT_ID,
+	FS_NL_A_DEV_MAJOR,
+	FS_NL_A_DEV_MINOR,
+	FS_NL_A_CAUSED_ID,
+	FS_NL_A_DATA,
+	__FS_NL_A_MAX,
+};
+#define FS_NL_A_MAX (__FS_NL_A_MAX - 1)
+/*
+ * Generic netlink commands
+ */
+#define FS_NL_C_EVENT		1
+
+/*
+ * Supported set of FS events
+ */
+enum {
+	FS_EVENT_NONE,
+	FS_WARN_ENOSPC,		/* No space left to reserve data blks */
+	FS_WARN_ENOSPC_META,	/* No space left for metadata */
+	FS_THR_LRBELOW,		/* The threshold lower range has been reached */
+	FS_THR_LRABOVE,		/* The threshold lower range re-activcated*/
+	FS_THR_URBELOW,
+	FS_THR_URABOVE,
+	FS_ERR_REMOUNT_RO,	/* The file system has been remounted as RO */
+	FS_ERR_CORRUPTED	/* Critical error - fs corrupted */
+
+};
+
+#endif /* _UAPI_LINUX_GENERIC_FS_EVENTS_ */
+
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
Thanks for Your comments.

Best Regards
Beata

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help