diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 3bf6fd85c634..0c7667d3f5d1 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -709,6 +709,55 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
 	return fsid;
 }
 
+static void fanotify_insert_error_event(struct fsnotify_group *group,
+					struct fsnotify_event *fsn_event)
+
+{
+	struct fanotify_event *event = FANOTIFY_E(fsn_event);
+
+	if (!fanotify_is_error_event(event->mask))
+		return;
+
+	/*
+	 * Prevent the mark from going away while an outstanding error
+	 * event is queued.  The reference is released by
+	 * fanotify_dequeue_first_event.
+	 */
+	fsnotify_get_mark(&FANOTIFY_EE(event)->sb_mark->fsn_mark);
+
+}
+
+static int fanotify_handle_error_event(struct fsnotify_iter_info *iter_info,
+				       struct fsnotify_group *group,
+				       const struct fs_error_report *report)
+{
+	struct fanotify_sb_mark *sb_mark =
+		FANOTIFY_SB_MARK(fsnotify_iter_sb_mark(iter_info));
+	struct fanotify_error_event *fee = sb_mark->fee_slot;
+
+	spin_lock(&group->notification_lock);
+	if (fee->err_count++) {
+		spin_unlock(&group->notification_lock);
+		return 0;
+	}
+	spin_unlock(&group->notification_lock);
+
+	fee->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR;
+
+	if (fsnotify_insert_event(group, &fee->fae.fse,
+				  NULL, fanotify_insert_error_event)) {
+		/*
+		 *  Even if an error occurred, an overflow event is
+		 *  queued. Just reset the error count and succeed.
+		 */
+		spin_lock(&group->notification_lock);
+		fanotify_reset_error_slot(fee);
+		spin_unlock(&group->notification_lock);
+	}
+
+	return 0;
+}
+
 /*
  * Add an event to hash table for faster merge.
  */
@@ -762,7 +811,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
 	BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
 	BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR);
 
-	BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
+	BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20);
 
 	mask = fanotify_group_event_mask(group, iter_info, mask, data,
 					 data_type, dir);
@@ -787,6 +836,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
 			return 0;
 	}
 
+	if (fanotify_is_error_event(mask))
+		return fanotify_handle_error_event(iter_info, group, data);
+
 	event = fanotify_alloc_event(group, mask, data, data_type, dir,
 				     file_name, &fsid);
 	ret = -ENOMEM;
@@ -857,10 +909,13 @@ static void fanotify_free_name_event(struct fanotify_event *event)
 
 static void fanotify_free_error_event(struct fanotify_event *event)
 {
+	struct fanotify_error_event *fee = FANOTIFY_EE(event);
+
 	/*
 	 * The actual event is tied to a mark, and is released on mark
 	 * removal
 	 */
+	fsnotify_put_mark(&fee->sb_mark->fsn_mark);
 }
 
 static void fanotify_free_event(struct fsnotify_event *fsn_event)
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 3f03333df32f..eeb4a85af74e 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -220,6 +220,8 @@ FANOTIFY_NE(struct fanotify_event *event)
 
 struct fanotify_error_event {
 	struct fanotify_event fae;
+	u32 err_count; /* Suppressed errors count */
+
 	struct fanotify_sb_mark *sb_mark; /* Back reference to the mark. */
 };
 
@@ -320,6 +322,11 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
 	return container_of(fse, struct fanotify_event, fse);
 }
 
+static inline bool fanotify_is_error_event(u32 mask)
+{
+	return mask & FAN_FS_ERROR;
+}
+
 static inline bool fanotify_event_has_path(struct fanotify_event *event)
 {
 	return event->type == FANOTIFY_EVENT_TYPE_PATH ||
@@ -349,6 +356,7 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event)
 static inline bool fanotify_is_hashed_event(u32 mask)
 {
 	return !(fanotify_is_perm_event(mask) ||
+		 fanotify_is_error_event(mask) ||
 		 fsnotify_is_overflow_event(mask));
 }
 
@@ -358,3 +366,16 @@ static inline unsigned int fanotify_event_hash_bucket(
 {
 	return event->hash & FANOTIFY_HTABLE_MASK;
 }
+
+/*
+ * Reset the FAN_FS_ERROR event slot
+ *
+ * This is used to restore the error event slot to a a zeroed state,
+ * where it can be used for a new incoming error.  It does not
+ * initialize the event, but clear only the required data to free the
+ * slot.
+ */
+static inline void fanotify_reset_error_slot(struct fanotify_error_event *fee)
+{
+	fee->err_count = 0;
+}
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index b77030386d7f..3fff0c994dc8 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -167,6 +167,19 @@ static void fanotify_unhash_event(struct fsnotify_group *group,
 	hlist_del_init(&event->merge_list);
 }
 
+static struct fanotify_event *fanotify_dup_error_to_stack(
+				struct fanotify_error_event *fee,
+				struct fanotify_error_event *error_on_stack)
+{
+	fanotify_init_event(&error_on_stack->fae, 0, FS_ERROR);
+
+	error_on_stack->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR;
+	error_on_stack->err_count = fee->err_count;
+	error_on_stack->sb_mark = fee->sb_mark;
+
+	return &error_on_stack->fae;
+}
+
 /*
  * Get an fanotify notification event if one exists and is small
  * enough to fit in "count". Return an error pointer if the count
@@ -174,7 +187,9 @@ static void fanotify_unhash_event(struct fsnotify_group *group,
  * updated accordingly.
  */
 static struct fanotify_event *get_one_event(struct fsnotify_group *group,
-					    size_t count)
+				    size_t count,
+				    struct fanotify_error_event *error_on_stack)
+
 {
 	size_t event_size;
 	struct fanotify_event *event = NULL;
@@ -205,6 +220,16 @@ static struct fanotify_event *get_one_event(struct fsnotify_group *group,
 		FANOTIFY_PERM(event)->state = FAN_EVENT_REPORTED;
 	if (fanotify_is_hashed_event(event->mask))
 		fanotify_unhash_event(group, event);
+
+	if (fanotify_is_error_event(event->mask)) {
+		/*
+		 * Error events are returned as a copy of the error
+		 * slot.  The actual error slot is reused.
+		 */
+		fanotify_dup_error_to_stack(FANOTIFY_EE(event), error_on_stack);
+		fanotify_reset_error_slot(FANOTIFY_EE(event));
+		event = &error_on_stack->fae;
+	}
 out:
 	spin_unlock(&group->notification_lock);
 	return event;
@@ -564,6 +589,7 @@ static __poll_t fanotify_poll(struct file *file, poll_table *wait)
 static ssize_t fanotify_read(struct file *file, char __user *buf,
 			     size_t count, loff_t *pos)
 {
+	struct fanotify_error_event error_on_stack;
 	struct fsnotify_group *group;
 	struct fanotify_event *event;
 	char __user *start;
@@ -582,7 +608,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
 		 * in case there are lots of available events.
 		 */
 		cond_resched();
-		event = get_one_event(group, count);
+		event = get_one_event(group, count, &error_on_stack);
 		if (IS_ERR(event)) {
 			ret = PTR_ERR(event);
 			break;
@@ -1031,6 +1057,10 @@ static int fanotify_add_mark(struct fsnotify_group *group,
 			fanotify_init_event(&fee->fae, 0, FS_ERROR);
 			fee->sb_mark = sb_mark;
 			sb_mark->fee_slot = fee;
+
+			/* Mark the error slot ready to receive events. */
+			fanotify_reset_error_slot(fee);
+
 		}
 	}
 
@@ -1459,6 +1489,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 		fsid = &__fsid;
 	}
 
+	if (mask & FAN_FS_ERROR && mark_type != FAN_MARK_FILESYSTEM) {
+		ret = -EINVAL;
+		goto path_put_and_out;
+	}
+
 	/* inode held in place by reference to path; group by fget on fd */
 	if (mark_type == FAN_MARK_INODE)
 		inode = path.dentry->d_inode;
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index c05d45bde8b8..c4d49308b2d0 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -88,9 +88,13 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
 #define FANOTIFY_INODE_EVENTS	(FANOTIFY_DIRENT_EVENTS | \
 				 FAN_ATTRIB | FAN_MOVE_SELF | FAN_DELETE_SELF)
 
+/* Events that can only be reported with data type FSNOTIFY_EVENT_ERROR */
+#define FANOTIFY_ERROR_EVENTS	(FAN_FS_ERROR)
+
 /* Events that user can request to be notified on */
 #define FANOTIFY_EVENTS		(FANOTIFY_PATH_EVENTS | \
-				 FANOTIFY_INODE_EVENTS)
+				 FANOTIFY_INODE_EVENTS | \
+				 FANOTIFY_ERROR_EVENTS)
 
 /* Events that require a permission response from user */
 #define FANOTIFY_PERM_EVENTS	(FAN_OPEN_PERM | FAN_ACCESS_PERM | \
