123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- /*
- * Copyright (c) 2014 Red Hat, Inc.
- * 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 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include "xfs.h"
- #include "xfs_shared.h"
- #include "xfs_format.h"
- #include "xfs_log_format.h"
- #include "xfs_trans_resv.h"
- #include "xfs_sysfs.h"
- #include "xfs_log.h"
- #include "xfs_log_priv.h"
- #include "xfs_stats.h"
- #include "xfs_mount.h"
- struct xfs_sysfs_attr {
- struct attribute attr;
- ssize_t (*show)(struct kobject *kobject, char *buf);
- ssize_t (*store)(struct kobject *kobject, const char *buf,
- size_t count);
- };
- static inline struct xfs_sysfs_attr *
- to_attr(struct attribute *attr)
- {
- return container_of(attr, struct xfs_sysfs_attr, attr);
- }
- #define XFS_SYSFS_ATTR_RW(name) \
- static struct xfs_sysfs_attr xfs_sysfs_attr_##name = __ATTR_RW(name)
- #define XFS_SYSFS_ATTR_RO(name) \
- static struct xfs_sysfs_attr xfs_sysfs_attr_##name = __ATTR_RO(name)
- #define XFS_SYSFS_ATTR_WO(name) \
- static struct xfs_sysfs_attr xfs_sysfs_attr_##name = __ATTR_WO(name)
- #define ATTR_LIST(name) &xfs_sysfs_attr_##name.attr
- STATIC ssize_t
- xfs_sysfs_object_show(
- struct kobject *kobject,
- struct attribute *attr,
- char *buf)
- {
- struct xfs_sysfs_attr *xfs_attr = to_attr(attr);
- return xfs_attr->show ? xfs_attr->show(kobject, buf) : 0;
- }
- STATIC ssize_t
- xfs_sysfs_object_store(
- struct kobject *kobject,
- struct attribute *attr,
- const char *buf,
- size_t count)
- {
- struct xfs_sysfs_attr *xfs_attr = to_attr(attr);
- return xfs_attr->store ? xfs_attr->store(kobject, buf, count) : 0;
- }
- static const struct sysfs_ops xfs_sysfs_ops = {
- .show = xfs_sysfs_object_show,
- .store = xfs_sysfs_object_store,
- };
- /*
- * xfs_mount kobject. The mp kobject also serves as the per-mount parent object
- * that is identified by the fsname under sysfs.
- */
- static inline struct xfs_mount *
- to_mp(struct kobject *kobject)
- {
- struct xfs_kobj *kobj = to_kobj(kobject);
- return container_of(kobj, struct xfs_mount, m_kobj);
- }
- #ifdef DEBUG
- STATIC ssize_t
- fail_writes_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- struct xfs_mount *mp = to_mp(kobject);
- int ret;
- int val;
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- if (val == 1)
- mp->m_fail_writes = true;
- else if (val == 0)
- mp->m_fail_writes = false;
- else
- return -EINVAL;
- return count;
- }
- STATIC ssize_t
- fail_writes_show(
- struct kobject *kobject,
- char *buf)
- {
- struct xfs_mount *mp = to_mp(kobject);
- return snprintf(buf, PAGE_SIZE, "%d\n", mp->m_fail_writes ? 1 : 0);
- }
- XFS_SYSFS_ATTR_RW(fail_writes);
- #endif /* DEBUG */
- static struct attribute *xfs_mp_attrs[] = {
- #ifdef DEBUG
- ATTR_LIST(fail_writes),
- #endif
- NULL,
- };
- struct kobj_type xfs_mp_ktype = {
- .release = xfs_sysfs_release,
- .sysfs_ops = &xfs_sysfs_ops,
- .default_attrs = xfs_mp_attrs,
- };
- #ifdef DEBUG
- /* debug */
- STATIC ssize_t
- log_recovery_delay_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- int ret;
- int val;
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- if (val < 0 || val > 60)
- return -EINVAL;
- xfs_globals.log_recovery_delay = val;
- return count;
- }
- STATIC ssize_t
- log_recovery_delay_show(
- struct kobject *kobject,
- char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%d\n", xfs_globals.log_recovery_delay);
- }
- XFS_SYSFS_ATTR_RW(log_recovery_delay);
- static struct attribute *xfs_dbg_attrs[] = {
- ATTR_LIST(log_recovery_delay),
- NULL,
- };
- struct kobj_type xfs_dbg_ktype = {
- .release = xfs_sysfs_release,
- .sysfs_ops = &xfs_sysfs_ops,
- .default_attrs = xfs_dbg_attrs,
- };
- #endif /* DEBUG */
- /* stats */
- static inline struct xstats *
- to_xstats(struct kobject *kobject)
- {
- struct xfs_kobj *kobj = to_kobj(kobject);
- return container_of(kobj, struct xstats, xs_kobj);
- }
- STATIC ssize_t
- stats_show(
- struct kobject *kobject,
- char *buf)
- {
- struct xstats *stats = to_xstats(kobject);
- return xfs_stats_format(stats->xs_stats, buf);
- }
- XFS_SYSFS_ATTR_RO(stats);
- STATIC ssize_t
- stats_clear_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- int ret;
- int val;
- struct xstats *stats = to_xstats(kobject);
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- if (val != 1)
- return -EINVAL;
- xfs_stats_clearall(stats->xs_stats);
- return count;
- }
- XFS_SYSFS_ATTR_WO(stats_clear);
- static struct attribute *xfs_stats_attrs[] = {
- ATTR_LIST(stats),
- ATTR_LIST(stats_clear),
- NULL,
- };
- struct kobj_type xfs_stats_ktype = {
- .release = xfs_sysfs_release,
- .sysfs_ops = &xfs_sysfs_ops,
- .default_attrs = xfs_stats_attrs,
- };
- /* xlog */
- static inline struct xlog *
- to_xlog(struct kobject *kobject)
- {
- struct xfs_kobj *kobj = to_kobj(kobject);
- return container_of(kobj, struct xlog, l_kobj);
- }
- STATIC ssize_t
- log_head_lsn_show(
- struct kobject *kobject,
- char *buf)
- {
- int cycle;
- int block;
- struct xlog *log = to_xlog(kobject);
- spin_lock(&log->l_icloglock);
- cycle = log->l_curr_cycle;
- block = log->l_curr_block;
- spin_unlock(&log->l_icloglock);
- return snprintf(buf, PAGE_SIZE, "%d:%d\n", cycle, block);
- }
- XFS_SYSFS_ATTR_RO(log_head_lsn);
- STATIC ssize_t
- log_tail_lsn_show(
- struct kobject *kobject,
- char *buf)
- {
- int cycle;
- int block;
- struct xlog *log = to_xlog(kobject);
- xlog_crack_atomic_lsn(&log->l_tail_lsn, &cycle, &block);
- return snprintf(buf, PAGE_SIZE, "%d:%d\n", cycle, block);
- }
- XFS_SYSFS_ATTR_RO(log_tail_lsn);
- STATIC ssize_t
- reserve_grant_head_show(
- struct kobject *kobject,
- char *buf)
- {
- int cycle;
- int bytes;
- struct xlog *log = to_xlog(kobject);
- xlog_crack_grant_head(&log->l_reserve_head.grant, &cycle, &bytes);
- return snprintf(buf, PAGE_SIZE, "%d:%d\n", cycle, bytes);
- }
- XFS_SYSFS_ATTR_RO(reserve_grant_head);
- STATIC ssize_t
- write_grant_head_show(
- struct kobject *kobject,
- char *buf)
- {
- int cycle;
- int bytes;
- struct xlog *log = to_xlog(kobject);
- xlog_crack_grant_head(&log->l_write_head.grant, &cycle, &bytes);
- return snprintf(buf, PAGE_SIZE, "%d:%d\n", cycle, bytes);
- }
- XFS_SYSFS_ATTR_RO(write_grant_head);
- #ifdef DEBUG
- STATIC ssize_t
- log_badcrc_factor_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- struct xlog *log = to_xlog(kobject);
- int ret;
- uint32_t val;
- ret = kstrtouint(buf, 0, &val);
- if (ret)
- return ret;
- log->l_badcrc_factor = val;
- return count;
- }
- STATIC ssize_t
- log_badcrc_factor_show(
- struct kobject *kobject,
- char *buf)
- {
- struct xlog *log = to_xlog(kobject);
- return snprintf(buf, PAGE_SIZE, "%d\n", log->l_badcrc_factor);
- }
- XFS_SYSFS_ATTR_RW(log_badcrc_factor);
- #endif /* DEBUG */
- static struct attribute *xfs_log_attrs[] = {
- ATTR_LIST(log_head_lsn),
- ATTR_LIST(log_tail_lsn),
- ATTR_LIST(reserve_grant_head),
- ATTR_LIST(write_grant_head),
- #ifdef DEBUG
- ATTR_LIST(log_badcrc_factor),
- #endif
- NULL,
- };
- struct kobj_type xfs_log_ktype = {
- .release = xfs_sysfs_release,
- .sysfs_ops = &xfs_sysfs_ops,
- .default_attrs = xfs_log_attrs,
- };
- /*
- * Metadata IO error configuration
- *
- * The sysfs structure here is:
- * ...xfs/<dev>/error/<class>/<errno>/<error_attrs>
- *
- * where <class> allows us to discriminate between data IO and metadata IO,
- * and any other future type of IO (e.g. special inode or directory error
- * handling) we care to support.
- */
- static inline struct xfs_error_cfg *
- to_error_cfg(struct kobject *kobject)
- {
- struct xfs_kobj *kobj = to_kobj(kobject);
- return container_of(kobj, struct xfs_error_cfg, kobj);
- }
- static inline struct xfs_mount *
- err_to_mp(struct kobject *kobject)
- {
- struct xfs_kobj *kobj = to_kobj(kobject);
- return container_of(kobj, struct xfs_mount, m_error_kobj);
- }
- static ssize_t
- max_retries_show(
- struct kobject *kobject,
- char *buf)
- {
- int retries;
- struct xfs_error_cfg *cfg = to_error_cfg(kobject);
- if (cfg->max_retries == XFS_ERR_RETRY_FOREVER)
- retries = -1;
- else
- retries = cfg->max_retries;
- return snprintf(buf, PAGE_SIZE, "%d\n", retries);
- }
- static ssize_t
- max_retries_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- struct xfs_error_cfg *cfg = to_error_cfg(kobject);
- int ret;
- int val;
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- if (val < -1)
- return -EINVAL;
- if (val == -1)
- cfg->max_retries = XFS_ERR_RETRY_FOREVER;
- else
- cfg->max_retries = val;
- return count;
- }
- XFS_SYSFS_ATTR_RW(max_retries);
- static ssize_t
- retry_timeout_seconds_show(
- struct kobject *kobject,
- char *buf)
- {
- int timeout;
- struct xfs_error_cfg *cfg = to_error_cfg(kobject);
- if (cfg->retry_timeout == XFS_ERR_RETRY_FOREVER)
- timeout = -1;
- else
- timeout = jiffies_to_msecs(cfg->retry_timeout) / MSEC_PER_SEC;
- return snprintf(buf, PAGE_SIZE, "%d\n", timeout);
- }
- static ssize_t
- retry_timeout_seconds_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- struct xfs_error_cfg *cfg = to_error_cfg(kobject);
- int ret;
- int val;
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- /* 1 day timeout maximum, -1 means infinite */
- if (val < -1 || val > 86400)
- return -EINVAL;
- if (val == -1)
- cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
- else {
- cfg->retry_timeout = msecs_to_jiffies(val * MSEC_PER_SEC);
- ASSERT(msecs_to_jiffies(val * MSEC_PER_SEC) < LONG_MAX);
- }
- return count;
- }
- XFS_SYSFS_ATTR_RW(retry_timeout_seconds);
- static ssize_t
- fail_at_unmount_show(
- struct kobject *kobject,
- char *buf)
- {
- struct xfs_mount *mp = err_to_mp(kobject);
- return snprintf(buf, PAGE_SIZE, "%d\n", mp->m_fail_unmount);
- }
- static ssize_t
- fail_at_unmount_store(
- struct kobject *kobject,
- const char *buf,
- size_t count)
- {
- struct xfs_mount *mp = err_to_mp(kobject);
- int ret;
- int val;
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- if (val < 0 || val > 1)
- return -EINVAL;
- mp->m_fail_unmount = val;
- return count;
- }
- XFS_SYSFS_ATTR_RW(fail_at_unmount);
- static struct attribute *xfs_error_attrs[] = {
- ATTR_LIST(max_retries),
- ATTR_LIST(retry_timeout_seconds),
- NULL,
- };
- static struct kobj_type xfs_error_cfg_ktype = {
- .release = xfs_sysfs_release,
- .sysfs_ops = &xfs_sysfs_ops,
- .default_attrs = xfs_error_attrs,
- };
- static struct kobj_type xfs_error_ktype = {
- .release = xfs_sysfs_release,
- .sysfs_ops = &xfs_sysfs_ops,
- };
- /*
- * Error initialization tables. These need to be ordered in the same
- * order as the enums used to index the array. All class init tables need to
- * define a "default" behaviour as the first entry, all other entries can be
- * empty.
- */
- struct xfs_error_init {
- char *name;
- int max_retries;
- int retry_timeout; /* in seconds */
- };
- static const struct xfs_error_init xfs_error_meta_init[XFS_ERR_ERRNO_MAX] = {
- { .name = "default",
- .max_retries = XFS_ERR_RETRY_FOREVER,
- .retry_timeout = XFS_ERR_RETRY_FOREVER,
- },
- { .name = "EIO",
- .max_retries = XFS_ERR_RETRY_FOREVER,
- .retry_timeout = XFS_ERR_RETRY_FOREVER,
- },
- { .name = "ENOSPC",
- .max_retries = XFS_ERR_RETRY_FOREVER,
- .retry_timeout = XFS_ERR_RETRY_FOREVER,
- },
- { .name = "ENODEV",
- .max_retries = 0, /* We can't recover from devices disappearing */
- .retry_timeout = 0,
- },
- };
- static int
- xfs_error_sysfs_init_class(
- struct xfs_mount *mp,
- int class,
- const char *parent_name,
- struct xfs_kobj *parent_kobj,
- const struct xfs_error_init init[])
- {
- struct xfs_error_cfg *cfg;
- int error;
- int i;
- ASSERT(class < XFS_ERR_CLASS_MAX);
- error = xfs_sysfs_init(parent_kobj, &xfs_error_ktype,
- &mp->m_error_kobj, parent_name);
- if (error)
- return error;
- for (i = 0; i < XFS_ERR_ERRNO_MAX; i++) {
- cfg = &mp->m_error_cfg[class][i];
- error = xfs_sysfs_init(&cfg->kobj, &xfs_error_cfg_ktype,
- parent_kobj, init[i].name);
- if (error)
- goto out_error;
- cfg->max_retries = init[i].max_retries;
- if (init[i].retry_timeout == XFS_ERR_RETRY_FOREVER)
- cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
- else
- cfg->retry_timeout = msecs_to_jiffies(
- init[i].retry_timeout * MSEC_PER_SEC);
- }
- return 0;
- out_error:
- /* unwind the entries that succeeded */
- for (i--; i >= 0; i--) {
- cfg = &mp->m_error_cfg[class][i];
- xfs_sysfs_del(&cfg->kobj);
- }
- xfs_sysfs_del(parent_kobj);
- return error;
- }
- int
- xfs_error_sysfs_init(
- struct xfs_mount *mp)
- {
- int error;
- /* .../xfs/<dev>/error/ */
- error = xfs_sysfs_init(&mp->m_error_kobj, &xfs_error_ktype,
- &mp->m_kobj, "error");
- if (error)
- return error;
- error = sysfs_create_file(&mp->m_error_kobj.kobject,
- ATTR_LIST(fail_at_unmount));
- if (error)
- goto out_error;
- /* .../xfs/<dev>/error/metadata/ */
- error = xfs_error_sysfs_init_class(mp, XFS_ERR_METADATA,
- "metadata", &mp->m_error_meta_kobj,
- xfs_error_meta_init);
- if (error)
- goto out_error;
- return 0;
- out_error:
- xfs_sysfs_del(&mp->m_error_kobj);
- return error;
- }
- void
- xfs_error_sysfs_del(
- struct xfs_mount *mp)
- {
- struct xfs_error_cfg *cfg;
- int i, j;
- for (i = 0; i < XFS_ERR_CLASS_MAX; i++) {
- for (j = 0; j < XFS_ERR_ERRNO_MAX; j++) {
- cfg = &mp->m_error_cfg[i][j];
- xfs_sysfs_del(&cfg->kobj);
- }
- }
- xfs_sysfs_del(&mp->m_error_meta_kobj);
- xfs_sysfs_del(&mp->m_error_kobj);
- }
- struct xfs_error_cfg *
- xfs_error_get_cfg(
- struct xfs_mount *mp,
- int error_class,
- int error)
- {
- struct xfs_error_cfg *cfg;
- if (error < 0)
- error = -error;
- switch (error) {
- case EIO:
- cfg = &mp->m_error_cfg[error_class][XFS_ERR_EIO];
- break;
- case ENOSPC:
- cfg = &mp->m_error_cfg[error_class][XFS_ERR_ENOSPC];
- break;
- case ENODEV:
- cfg = &mp->m_error_cfg[error_class][XFS_ERR_ENODEV];
- break;
- default:
- cfg = &mp->m_error_cfg[error_class][XFS_ERR_DEFAULT];
- break;
- }
- return cfg;
- }
|