#define XFS_HEALTH_MONITOR_DOMAIN_RTDEV (6)
#define XFS_HEALTH_MONITOR_DOMAIN_LOGDEV (7)
+/* file range events */
+#define XFS_HEALTH_MONITOR_DOMAIN_FILERANGE (8)
+
/* Health monitor event types */
/* status of the monitor itself */
/* media errors */
#define XFS_HEALTH_MONITOR_TYPE_MEDIA_ERROR (7)
+/* pagecache I/O to a file range failed */
+#define XFS_HEALTH_MONITOR_TYPE_BUFREAD (8)
+#define XFS_HEALTH_MONITOR_TYPE_BUFWRITE (9)
+
+/* direct I/O to a file range failed */
+#define XFS_HEALTH_MONITOR_TYPE_DIOREAD (10)
+#define XFS_HEALTH_MONITOR_TYPE_DIOWRITE (11)
+
+/* out of band media error reported for a file range */
+#define XFS_HEALTH_MONITOR_TYPE_DATALOST (12)
+
/* lost events */
struct xfs_health_monitor_lost {
__u64 count;
__u32 reasons;
};
+/* file range events */
+struct xfs_health_monitor_filerange {
+ __u64 pos;
+ __u64 len;
+ __u64 ino;
+ __u32 gen;
+ __u32 error;
+};
+
/* disk media errors */
struct xfs_health_monitor_media {
__u64 daddr;
struct xfs_health_monitor_inode inode;
struct xfs_health_monitor_shutdown shutdown;
struct xfs_health_monitor_media media;
+ struct xfs_health_monitor_filerange filerange;
} e;
/* zeroes */
#include "xfs_healthmon.h"
#include "xfs_fsops.h"
#include "xfs_notify_failure.h"
+#include "xfs_file.h"
#include <linux/anon_inodes.h>
#include <linux/eventpoll.h>
#include <linux/poll.h>
+#include <linux/fserror.h>
/*
* Live Health Monitoring
return true;
}
return false;
+
+ case XFS_HEALTHMON_BUFREAD:
+ case XFS_HEALTHMON_BUFWRITE:
+ case XFS_HEALTHMON_DIOREAD:
+ case XFS_HEALTHMON_DIOWRITE:
+ case XFS_HEALTHMON_DATALOST:
+ /* logically adjacent file ranges can merge */
+ if (existing->fino != new->fino || existing->fgen != new->fgen)
+ return false;
+
+ if (existing->fpos + existing->flen == new->fpos) {
+ existing->flen += new->flen;
+ return true;
+ }
+
+ if (new->fpos + new->flen == existing->fpos) {
+ existing->fpos = new->fpos;
+ existing->flen += new->flen;
+ return true;
+ }
+ return false;
}
return false;
xfs_healthmon_put(hm);
}
+static inline enum xfs_healthmon_type file_ioerr_type(enum fserror_type action)
+{
+ switch (action) {
+ case FSERR_BUFFERED_READ:
+ return XFS_HEALTHMON_BUFREAD;
+ case FSERR_BUFFERED_WRITE:
+ return XFS_HEALTHMON_BUFWRITE;
+ case FSERR_DIRECTIO_READ:
+ return XFS_HEALTHMON_DIOREAD;
+ case FSERR_DIRECTIO_WRITE:
+ return XFS_HEALTHMON_DIOWRITE;
+ case FSERR_DATA_LOST:
+ return XFS_HEALTHMON_DATALOST;
+ case FSERR_METADATA:
+ /* filtered out by xfs_fs_report_error */
+ break;
+ }
+
+ ASSERT(0);
+ return -1;
+}
+
+/* Add a file io error event to the reporting queue. */
+void
+xfs_healthmon_report_file_ioerror(
+ struct xfs_inode *ip,
+ const struct fserror_event *p)
+{
+ struct xfs_healthmon_event event = {
+ .type = file_ioerr_type(p->type),
+ .domain = XFS_HEALTHMON_FILERANGE,
+ .fino = ip->i_ino,
+ .fgen = VFS_I(ip)->i_generation,
+ .fpos = p->pos,
+ .flen = p->len,
+ /* send positive error number to userspace */
+ .error = -p->error,
+ };
+ struct xfs_healthmon *hm = xfs_healthmon_get(ip->i_mount);
+
+ if (!hm)
+ return;
+
+ trace_xfs_healthmon_report_file_ioerror(hm, p);
+
+ xfs_healthmon_push(hm, &event);
+ xfs_healthmon_put(hm);
+}
+
static inline void
xfs_healthmon_reset_outbuf(
struct xfs_healthmon *hm)
[XFS_HEALTHMON_DATADEV] = XFS_HEALTH_MONITOR_DOMAIN_DATADEV,
[XFS_HEALTHMON_RTDEV] = XFS_HEALTH_MONITOR_DOMAIN_RTDEV,
[XFS_HEALTHMON_LOGDEV] = XFS_HEALTH_MONITOR_DOMAIN_LOGDEV,
+ [XFS_HEALTHMON_FILERANGE] = XFS_HEALTH_MONITOR_DOMAIN_FILERANGE,
};
static const unsigned int type_map[] = {
[XFS_HEALTHMON_UNMOUNT] = XFS_HEALTH_MONITOR_TYPE_UNMOUNT,
[XFS_HEALTHMON_SHUTDOWN] = XFS_HEALTH_MONITOR_TYPE_SHUTDOWN,
[XFS_HEALTHMON_MEDIA_ERROR] = XFS_HEALTH_MONITOR_TYPE_MEDIA_ERROR,
+ [XFS_HEALTHMON_BUFREAD] = XFS_HEALTH_MONITOR_TYPE_BUFREAD,
+ [XFS_HEALTHMON_BUFWRITE] = XFS_HEALTH_MONITOR_TYPE_BUFWRITE,
+ [XFS_HEALTHMON_DIOREAD] = XFS_HEALTH_MONITOR_TYPE_DIOREAD,
+ [XFS_HEALTHMON_DIOWRITE] = XFS_HEALTH_MONITOR_TYPE_DIOWRITE,
+ [XFS_HEALTHMON_DATALOST] = XFS_HEALTH_MONITOR_TYPE_DATALOST,
};
/* Render event as a V0 structure */
hme.e.media.daddr = event->daddr;
hme.e.media.bbcount = event->bbcount;
break;
+ case XFS_HEALTHMON_FILERANGE:
+ hme.e.filerange.ino = event->fino;
+ hme.e.filerange.gen = event->fgen;
+ hme.e.filerange.pos = event->fpos;
+ hme.e.filerange.len = event->flen;
+ hme.e.filerange.error = abs(event->error);
+ break;
default:
break;
}
/* media errors */
XFS_HEALTHMON_MEDIA_ERROR,
+
+ /* file range events */
+ XFS_HEALTHMON_BUFREAD,
+ XFS_HEALTHMON_BUFWRITE,
+ XFS_HEALTHMON_DIOREAD,
+ XFS_HEALTHMON_DIOWRITE,
+ XFS_HEALTHMON_DATALOST,
};
enum xfs_healthmon_domain {
XFS_HEALTHMON_DATADEV,
XFS_HEALTHMON_RTDEV,
XFS_HEALTHMON_LOGDEV,
+
+ /* file range events */
+ XFS_HEALTHMON_FILERANGE,
};
struct xfs_healthmon_event {
xfs_daddr_t daddr;
uint64_t bbcount;
};
+ /* file range events */
+ struct {
+ xfs_ino_t fino;
+ loff_t fpos;
+ uint64_t flen;
+ uint32_t fgen;
+ int error;
+ };
};
};
void xfs_healthmon_report_media(struct xfs_mount *mp, enum xfs_device fdev,
xfs_daddr_t daddr, uint64_t bbcount);
+void xfs_healthmon_report_file_ioerror(struct xfs_inode *ip,
+ const struct fserror_event *p);
+
long xfs_ioc_health_monitor(struct file *file,
struct xfs_health_monitor __user *arg);
#include "xfs_parent.h"
#include "xfs_rtalloc.h"
#include "xfs_zone_alloc.h"
+#include "xfs_healthmon.h"
#include "scrub/stats.h"
#include "scrub/rcbag_btree.h"
#include <linux/magic.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
+#include <linux/fserror.h>
static const struct super_operations xfs_super_operations;
return 0;
}
+static void
+xfs_fs_report_error(
+ const struct fserror_event *event)
+{
+ /* healthmon already knows about non-inode and metadata errors */
+ if (event->inode && event->type != FSERR_METADATA)
+ xfs_healthmon_report_file_ioerror(XFS_I(event->inode), event);
+}
+
static const struct super_operations xfs_super_operations = {
.alloc_inode = xfs_fs_alloc_inode,
.destroy_inode = xfs_fs_destroy_inode,
.free_cached_objects = xfs_fs_free_cached_objects,
.shutdown = xfs_fs_shutdown,
.show_stats = xfs_fs_show_stats,
+ .report_error = xfs_fs_report_error,
};
static int
#include "xfs_health.h"
#include "xfs_healthmon.h"
#include "xfs_notify_failure.h"
+#include "xfs_file.h"
+#include <linux/fserror.h>
/*
* We include this last to have the helpers above available for the trace
struct xfs_open_zone;
struct xfs_healthmon_event;
struct xfs_healthmon;
+struct fserror_event;
#define XFS_ATTR_FILTER_FLAGS \
{ XFS_ATTR_ROOT, "ROOT" }, \
__entry->offset = event->daddr;
__entry->length = event->bbcount;
break;
+ case XFS_HEALTHMON_FILERANGE:
+ __entry->ino = event->fino;
+ __entry->gen = event->fgen;
+ __entry->offset = event->fpos;
+ __entry->length = event->flen;
+ break;
}
),
TP_printk("dev %d:%d type %s domain %s mask 0x%x ino 0x%llx gen 0x%x offset 0x%llx len 0x%llx group 0x%x lost %llu",
__entry->bbcount)
);
+#define FS_ERROR_STRINGS \
+ { FSERR_BUFFERED_READ, "buffered_read" }, \
+ { FSERR_BUFFERED_WRITE, "buffered_write" }, \
+ { FSERR_DIRECTIO_READ, "directio_read" }, \
+ { FSERR_DIRECTIO_WRITE, "directio_write" }, \
+ { FSERR_DATA_LOST, "data_lost" }, \
+ { FSERR_METADATA, "metadata" }
+
+TRACE_DEFINE_ENUM(FSERR_BUFFERED_READ);
+TRACE_DEFINE_ENUM(FSERR_BUFFERED_WRITE);
+TRACE_DEFINE_ENUM(FSERR_DIRECTIO_READ);
+TRACE_DEFINE_ENUM(FSERR_DIRECTIO_WRITE);
+TRACE_DEFINE_ENUM(FSERR_DATA_LOST);
+TRACE_DEFINE_ENUM(FSERR_METADATA);
+
+TRACE_EVENT(xfs_healthmon_report_file_ioerror,
+ TP_PROTO(const struct xfs_healthmon *hm,
+ const struct fserror_event *p),
+ TP_ARGS(hm, p),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(unsigned int, type)
+ __field(unsigned long long, ino)
+ __field(unsigned int, gen)
+ __field(long long, pos)
+ __field(unsigned long long, len)
+ __field(int, error)
+ ),
+ TP_fast_assign(
+ __entry->dev = hm->dev;
+ __entry->type = p->type;
+ __entry->ino = XFS_I(p->inode)->i_ino;
+ __entry->gen = p->inode->i_generation;
+ __entry->pos = p->pos;
+ __entry->len = p->len;
+ __entry->error = p->error;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx gen 0x%x op %s pos 0x%llx bytecount 0x%llx error %d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->gen,
+ __print_symbolic(__entry->type, FS_ERROR_STRINGS),
+ __entry->pos,
+ __entry->len,
+ __entry->error)
+);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH