18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* -*- mode: c; c-basic-offset: 8; -*-
38c2ecf20Sopenharmony_ci * vim: noexpandtab sw=8 ts=8 sts=0:
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * filecheck.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Code which implements online file check.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 2016 SuSE.  All rights reserved.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/list.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/kmod.h>
178c2ecf20Sopenharmony_ci#include <linux/fs.h>
188c2ecf20Sopenharmony_ci#include <linux/kobject.h>
198c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
208c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
218c2ecf20Sopenharmony_ci#include <cluster/masklog.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "ocfs2.h"
248c2ecf20Sopenharmony_ci#include "ocfs2_fs.h"
258c2ecf20Sopenharmony_ci#include "stackglue.h"
268c2ecf20Sopenharmony_ci#include "inode.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "filecheck.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* File check error strings,
328c2ecf20Sopenharmony_ci * must correspond with error number in header file.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic const char * const ocfs2_filecheck_errs[] = {
358c2ecf20Sopenharmony_ci	"SUCCESS",
368c2ecf20Sopenharmony_ci	"FAILED",
378c2ecf20Sopenharmony_ci	"INPROGRESS",
388c2ecf20Sopenharmony_ci	"READONLY",
398c2ecf20Sopenharmony_ci	"INJBD",
408c2ecf20Sopenharmony_ci	"INVALIDINO",
418c2ecf20Sopenharmony_ci	"BLOCKECC",
428c2ecf20Sopenharmony_ci	"BLOCKNO",
438c2ecf20Sopenharmony_ci	"VALIDFLAG",
448c2ecf20Sopenharmony_ci	"GENERATION",
458c2ecf20Sopenharmony_ci	"UNSUPPORTED"
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct ocfs2_filecheck_entry {
498c2ecf20Sopenharmony_ci	struct list_head fe_list;
508c2ecf20Sopenharmony_ci	unsigned long fe_ino;
518c2ecf20Sopenharmony_ci	unsigned int fe_type;
528c2ecf20Sopenharmony_ci	unsigned int fe_done:1;
538c2ecf20Sopenharmony_ci	unsigned int fe_status:31;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct ocfs2_filecheck_args {
578c2ecf20Sopenharmony_ci	unsigned int fa_type;
588c2ecf20Sopenharmony_ci	union {
598c2ecf20Sopenharmony_ci		unsigned long fa_ino;
608c2ecf20Sopenharmony_ci		unsigned int fa_len;
618c2ecf20Sopenharmony_ci	};
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const char *
658c2ecf20Sopenharmony_ciocfs2_filecheck_error(int errno)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	if (!errno)
688c2ecf20Sopenharmony_ci		return ocfs2_filecheck_errs[errno];
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
718c2ecf20Sopenharmony_ci	       errno > OCFS2_FILECHECK_ERR_END);
728c2ecf20Sopenharmony_ci	return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
768c2ecf20Sopenharmony_ci					struct kobj_attribute *attr,
778c2ecf20Sopenharmony_ci					char *buf);
788c2ecf20Sopenharmony_cistatic ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
798c2ecf20Sopenharmony_ci					struct kobj_attribute *attr,
808c2ecf20Sopenharmony_ci					const char *buf, size_t count);
818c2ecf20Sopenharmony_cistatic struct kobj_attribute ocfs2_filecheck_attr_chk =
828c2ecf20Sopenharmony_ci					__ATTR(check, S_IRUSR | S_IWUSR,
838c2ecf20Sopenharmony_ci					ocfs2_filecheck_attr_show,
848c2ecf20Sopenharmony_ci					ocfs2_filecheck_attr_store);
858c2ecf20Sopenharmony_cistatic struct kobj_attribute ocfs2_filecheck_attr_fix =
868c2ecf20Sopenharmony_ci					__ATTR(fix, S_IRUSR | S_IWUSR,
878c2ecf20Sopenharmony_ci					ocfs2_filecheck_attr_show,
888c2ecf20Sopenharmony_ci					ocfs2_filecheck_attr_store);
898c2ecf20Sopenharmony_cistatic struct kobj_attribute ocfs2_filecheck_attr_set =
908c2ecf20Sopenharmony_ci					__ATTR(set, S_IRUSR | S_IWUSR,
918c2ecf20Sopenharmony_ci					ocfs2_filecheck_attr_show,
928c2ecf20Sopenharmony_ci					ocfs2_filecheck_attr_store);
938c2ecf20Sopenharmony_cistatic struct attribute *ocfs2_filecheck_attrs[] = {
948c2ecf20Sopenharmony_ci	&ocfs2_filecheck_attr_chk.attr,
958c2ecf20Sopenharmony_ci	&ocfs2_filecheck_attr_fix.attr,
968c2ecf20Sopenharmony_ci	&ocfs2_filecheck_attr_set.attr,
978c2ecf20Sopenharmony_ci	NULL
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void ocfs2_filecheck_release(struct kobject *kobj)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_sysfs_entry *entry = container_of(kobj,
1038c2ecf20Sopenharmony_ci				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	complete(&entry->fs_kobj_unregister);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic ssize_t
1098c2ecf20Sopenharmony_ciocfs2_filecheck_show(struct kobject *kobj, struct attribute *attr, char *buf)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	ssize_t ret = -EIO;
1128c2ecf20Sopenharmony_ci	struct kobj_attribute *kattr = container_of(attr,
1138c2ecf20Sopenharmony_ci					struct kobj_attribute, attr);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	kobject_get(kobj);
1168c2ecf20Sopenharmony_ci	if (kattr->show)
1178c2ecf20Sopenharmony_ci		ret = kattr->show(kobj, kattr, buf);
1188c2ecf20Sopenharmony_ci	kobject_put(kobj);
1198c2ecf20Sopenharmony_ci	return ret;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic ssize_t
1238c2ecf20Sopenharmony_ciocfs2_filecheck_store(struct kobject *kobj, struct attribute *attr,
1248c2ecf20Sopenharmony_ci			const char *buf, size_t count)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	ssize_t ret = -EIO;
1278c2ecf20Sopenharmony_ci	struct kobj_attribute *kattr = container_of(attr,
1288c2ecf20Sopenharmony_ci					struct kobj_attribute, attr);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	kobject_get(kobj);
1318c2ecf20Sopenharmony_ci	if (kattr->store)
1328c2ecf20Sopenharmony_ci		ret = kattr->store(kobj, kattr, buf, count);
1338c2ecf20Sopenharmony_ci	kobject_put(kobj);
1348c2ecf20Sopenharmony_ci	return ret;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic const struct sysfs_ops ocfs2_filecheck_ops = {
1388c2ecf20Sopenharmony_ci	.show = ocfs2_filecheck_show,
1398c2ecf20Sopenharmony_ci	.store = ocfs2_filecheck_store,
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic struct kobj_type ocfs2_ktype_filecheck = {
1438c2ecf20Sopenharmony_ci	.default_attrs = ocfs2_filecheck_attrs,
1448c2ecf20Sopenharmony_ci	.sysfs_ops = &ocfs2_filecheck_ops,
1458c2ecf20Sopenharmony_ci	.release = ocfs2_filecheck_release,
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic void
1498c2ecf20Sopenharmony_ciocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_entry *p;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	spin_lock(&entry->fs_fcheck->fc_lock);
1548c2ecf20Sopenharmony_ci	while (!list_empty(&entry->fs_fcheck->fc_head)) {
1558c2ecf20Sopenharmony_ci		p = list_first_entry(&entry->fs_fcheck->fc_head,
1568c2ecf20Sopenharmony_ci				     struct ocfs2_filecheck_entry, fe_list);
1578c2ecf20Sopenharmony_ci		list_del(&p->fe_list);
1588c2ecf20Sopenharmony_ci		BUG_ON(!p->fe_done); /* To free a undone file check entry */
1598c2ecf20Sopenharmony_ci		kfree(p);
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	spin_unlock(&entry->fs_fcheck->fc_lock);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	kfree(entry->fs_fcheck);
1648c2ecf20Sopenharmony_ci	entry->fs_fcheck = NULL;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciint ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	int ret;
1708c2ecf20Sopenharmony_ci	struct ocfs2_filecheck *fcheck;
1718c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
1748c2ecf20Sopenharmony_ci	if (!fcheck)
1758c2ecf20Sopenharmony_ci		return -ENOMEM;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fcheck->fc_head);
1788c2ecf20Sopenharmony_ci	spin_lock_init(&fcheck->fc_lock);
1798c2ecf20Sopenharmony_ci	fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
1808c2ecf20Sopenharmony_ci	fcheck->fc_size = 0;
1818c2ecf20Sopenharmony_ci	fcheck->fc_done = 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	entry->fs_kobj.kset = osb->osb_dev_kset;
1848c2ecf20Sopenharmony_ci	init_completion(&entry->fs_kobj_unregister);
1858c2ecf20Sopenharmony_ci	ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck,
1868c2ecf20Sopenharmony_ci					NULL, "filecheck");
1878c2ecf20Sopenharmony_ci	if (ret) {
1888c2ecf20Sopenharmony_ci		kobject_put(&entry->fs_kobj);
1898c2ecf20Sopenharmony_ci		kfree(fcheck);
1908c2ecf20Sopenharmony_ci		return ret;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	entry->fs_fcheck = fcheck;
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_civoid ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	if (!osb->osb_fc_ent.fs_fcheck)
2008c2ecf20Sopenharmony_ci		return;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	kobject_del(&osb->osb_fc_ent.fs_kobj);
2038c2ecf20Sopenharmony_ci	kobject_put(&osb->osb_fc_ent.fs_kobj);
2048c2ecf20Sopenharmony_ci	wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
2058c2ecf20Sopenharmony_ci	ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int
2098c2ecf20Sopenharmony_ciocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
2108c2ecf20Sopenharmony_ci			      unsigned int count);
2118c2ecf20Sopenharmony_cistatic int
2128c2ecf20Sopenharmony_ciocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
2138c2ecf20Sopenharmony_ci			   unsigned int len)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	spin_lock(&ent->fs_fcheck->fc_lock);
2218c2ecf20Sopenharmony_ci	if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
2228c2ecf20Sopenharmony_ci		mlog(ML_NOTICE,
2238c2ecf20Sopenharmony_ci		"Cannot set online file check maximum entry number "
2248c2ecf20Sopenharmony_ci		"to %u due to too many pending entries(%u)\n",
2258c2ecf20Sopenharmony_ci		len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
2268c2ecf20Sopenharmony_ci		ret = -EBUSY;
2278c2ecf20Sopenharmony_ci	} else {
2288c2ecf20Sopenharmony_ci		if (len < ent->fs_fcheck->fc_size)
2298c2ecf20Sopenharmony_ci			BUG_ON(!ocfs2_filecheck_erase_entries(ent,
2308c2ecf20Sopenharmony_ci				ent->fs_fcheck->fc_size - len));
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		ent->fs_fcheck->fc_max = len;
2338c2ecf20Sopenharmony_ci		ret = 0;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	spin_unlock(&ent->fs_fcheck->fc_lock);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return ret;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci#define OCFS2_FILECHECK_ARGS_LEN	24
2418c2ecf20Sopenharmony_cistatic int
2428c2ecf20Sopenharmony_ciocfs2_filecheck_args_get_long(const char *buf, size_t count,
2438c2ecf20Sopenharmony_ci			      unsigned long *val)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	char buffer[OCFS2_FILECHECK_ARGS_LEN];
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	memcpy(buffer, buf, count);
2488c2ecf20Sopenharmony_ci	buffer[count] = '\0';
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (kstrtoul(buffer, 0, val))
2518c2ecf20Sopenharmony_ci		return 1;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int
2578c2ecf20Sopenharmony_ciocfs2_filecheck_type_parse(const char *name, unsigned int *type)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	if (!strncmp(name, "fix", 4))
2608c2ecf20Sopenharmony_ci		*type = OCFS2_FILECHECK_TYPE_FIX;
2618c2ecf20Sopenharmony_ci	else if (!strncmp(name, "check", 6))
2628c2ecf20Sopenharmony_ci		*type = OCFS2_FILECHECK_TYPE_CHK;
2638c2ecf20Sopenharmony_ci	else if (!strncmp(name, "set", 4))
2648c2ecf20Sopenharmony_ci		*type = OCFS2_FILECHECK_TYPE_SET;
2658c2ecf20Sopenharmony_ci	else
2668c2ecf20Sopenharmony_ci		return 1;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int
2728c2ecf20Sopenharmony_ciocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
2738c2ecf20Sopenharmony_ci			   struct ocfs2_filecheck_args *args)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	unsigned long val = 0;
2768c2ecf20Sopenharmony_ci	unsigned int type;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* too short/long args length */
2798c2ecf20Sopenharmony_ci	if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
2808c2ecf20Sopenharmony_ci		return 1;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (ocfs2_filecheck_type_parse(name, &type))
2838c2ecf20Sopenharmony_ci		return 1;
2848c2ecf20Sopenharmony_ci	if (ocfs2_filecheck_args_get_long(buf, count, &val))
2858c2ecf20Sopenharmony_ci		return 1;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (val <= 0)
2888c2ecf20Sopenharmony_ci		return 1;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	args->fa_type = type;
2918c2ecf20Sopenharmony_ci	if (type == OCFS2_FILECHECK_TYPE_SET)
2928c2ecf20Sopenharmony_ci		args->fa_len = (unsigned int)val;
2938c2ecf20Sopenharmony_ci	else
2948c2ecf20Sopenharmony_ci		args->fa_ino = val;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return 0;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
3008c2ecf20Sopenharmony_ci				    struct kobj_attribute *attr,
3018c2ecf20Sopenharmony_ci				    char *buf)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
3058c2ecf20Sopenharmony_ci	unsigned int type;
3068c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_entry *p;
3078c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
3088c2ecf20Sopenharmony_ci				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
3118c2ecf20Sopenharmony_ci		return -EINVAL;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (type == OCFS2_FILECHECK_TYPE_SET) {
3148c2ecf20Sopenharmony_ci		spin_lock(&ent->fs_fcheck->fc_lock);
3158c2ecf20Sopenharmony_ci		total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
3168c2ecf20Sopenharmony_ci		spin_unlock(&ent->fs_fcheck->fc_lock);
3178c2ecf20Sopenharmony_ci		goto exit;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
3218c2ecf20Sopenharmony_ci	total += ret;
3228c2ecf20Sopenharmony_ci	remain -= ret;
3238c2ecf20Sopenharmony_ci	spin_lock(&ent->fs_fcheck->fc_lock);
3248c2ecf20Sopenharmony_ci	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
3258c2ecf20Sopenharmony_ci		if (p->fe_type != type)
3268c2ecf20Sopenharmony_ci			continue;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
3298c2ecf20Sopenharmony_ci			       p->fe_ino, p->fe_done,
3308c2ecf20Sopenharmony_ci			       ocfs2_filecheck_error(p->fe_status));
3318c2ecf20Sopenharmony_ci		if (ret >= remain) {
3328c2ecf20Sopenharmony_ci			/* snprintf() didn't fit */
3338c2ecf20Sopenharmony_ci			total = -E2BIG;
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci		total += ret;
3378c2ecf20Sopenharmony_ci		remain -= ret;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	spin_unlock(&ent->fs_fcheck->fc_lock);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ciexit:
3428c2ecf20Sopenharmony_ci	return total;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic inline int
3468c2ecf20Sopenharmony_ciocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
3478c2ecf20Sopenharmony_ci				unsigned long ino)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_entry *p;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
3528c2ecf20Sopenharmony_ci		if (!p->fe_done) {
3538c2ecf20Sopenharmony_ci			if (p->fe_ino == ino)
3548c2ecf20Sopenharmony_ci				return 1;
3558c2ecf20Sopenharmony_ci		}
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic inline int
3628c2ecf20Sopenharmony_ciocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_entry *p;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
3678c2ecf20Sopenharmony_ci		if (p->fe_done) {
3688c2ecf20Sopenharmony_ci			list_del(&p->fe_list);
3698c2ecf20Sopenharmony_ci			kfree(p);
3708c2ecf20Sopenharmony_ci			ent->fs_fcheck->fc_size--;
3718c2ecf20Sopenharmony_ci			ent->fs_fcheck->fc_done--;
3728c2ecf20Sopenharmony_ci			return 1;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int
3808c2ecf20Sopenharmony_ciocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
3818c2ecf20Sopenharmony_ci			      unsigned int count)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	unsigned int i = 0;
3848c2ecf20Sopenharmony_ci	unsigned int ret = 0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	while (i++ < count) {
3878c2ecf20Sopenharmony_ci		if (ocfs2_filecheck_erase_entry(ent))
3888c2ecf20Sopenharmony_ci			ret++;
3898c2ecf20Sopenharmony_ci		else
3908c2ecf20Sopenharmony_ci			break;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return (ret == count ? 1 : 0);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic void
3978c2ecf20Sopenharmony_ciocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
3988c2ecf20Sopenharmony_ci			   struct ocfs2_filecheck_entry *entry)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	spin_lock(&ent->fs_fcheck->fc_lock);
4018c2ecf20Sopenharmony_ci	entry->fe_done = 1;
4028c2ecf20Sopenharmony_ci	ent->fs_fcheck->fc_done++;
4038c2ecf20Sopenharmony_ci	spin_unlock(&ent->fs_fcheck->fc_lock);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic unsigned int
4078c2ecf20Sopenharmony_ciocfs2_filecheck_handle(struct ocfs2_super *osb,
4088c2ecf20Sopenharmony_ci		       unsigned long ino, unsigned int flags)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
4118c2ecf20Sopenharmony_ci	struct inode *inode = NULL;
4128c2ecf20Sopenharmony_ci	int rc;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	inode = ocfs2_iget(osb, ino, flags, 0);
4158c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
4168c2ecf20Sopenharmony_ci		rc = (int)(-(long)inode);
4178c2ecf20Sopenharmony_ci		if (rc >= OCFS2_FILECHECK_ERR_START &&
4188c2ecf20Sopenharmony_ci		    rc < OCFS2_FILECHECK_ERR_END)
4198c2ecf20Sopenharmony_ci			ret = rc;
4208c2ecf20Sopenharmony_ci		else
4218c2ecf20Sopenharmony_ci			ret = OCFS2_FILECHECK_ERR_FAILED;
4228c2ecf20Sopenharmony_ci	} else
4238c2ecf20Sopenharmony_ci		iput(inode);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return ret;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void
4298c2ecf20Sopenharmony_ciocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
4308c2ecf20Sopenharmony_ci			     struct ocfs2_filecheck_entry *entry)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
4338c2ecf20Sopenharmony_ci						osb_fc_ent);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
4368c2ecf20Sopenharmony_ci		entry->fe_status = ocfs2_filecheck_handle(osb,
4378c2ecf20Sopenharmony_ci				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
4388c2ecf20Sopenharmony_ci	else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
4398c2ecf20Sopenharmony_ci		entry->fe_status = ocfs2_filecheck_handle(osb,
4408c2ecf20Sopenharmony_ci				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
4418c2ecf20Sopenharmony_ci	else
4428c2ecf20Sopenharmony_ci		entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	ocfs2_filecheck_done_entry(ent, entry);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
4488c2ecf20Sopenharmony_ci				     struct kobj_attribute *attr,
4498c2ecf20Sopenharmony_ci				     const char *buf, size_t count)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	ssize_t ret = 0;
4528c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_args args;
4538c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_entry *entry;
4548c2ecf20Sopenharmony_ci	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
4558c2ecf20Sopenharmony_ci				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (count == 0)
4588c2ecf20Sopenharmony_ci		return count;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
4618c2ecf20Sopenharmony_ci		return -EINVAL;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
4648c2ecf20Sopenharmony_ci		ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
4658c2ecf20Sopenharmony_ci		goto exit;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
4698c2ecf20Sopenharmony_ci	if (!entry) {
4708c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4718c2ecf20Sopenharmony_ci		goto exit;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	spin_lock(&ent->fs_fcheck->fc_lock);
4758c2ecf20Sopenharmony_ci	if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
4768c2ecf20Sopenharmony_ci		ret = -EEXIST;
4778c2ecf20Sopenharmony_ci		kfree(entry);
4788c2ecf20Sopenharmony_ci	} else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
4798c2ecf20Sopenharmony_ci		(ent->fs_fcheck->fc_done == 0)) {
4808c2ecf20Sopenharmony_ci		mlog(ML_NOTICE,
4818c2ecf20Sopenharmony_ci		"Cannot do more file check "
4828c2ecf20Sopenharmony_ci		"since file check queue(%u) is full now\n",
4838c2ecf20Sopenharmony_ci		ent->fs_fcheck->fc_max);
4848c2ecf20Sopenharmony_ci		ret = -EAGAIN;
4858c2ecf20Sopenharmony_ci		kfree(entry);
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
4888c2ecf20Sopenharmony_ci		    (ent->fs_fcheck->fc_done > 0)) {
4898c2ecf20Sopenharmony_ci			/* Delete the oldest entry which was done,
4908c2ecf20Sopenharmony_ci			 * make sure the entry size in list does
4918c2ecf20Sopenharmony_ci			 * not exceed maximum value
4928c2ecf20Sopenharmony_ci			 */
4938c2ecf20Sopenharmony_ci			BUG_ON(!ocfs2_filecheck_erase_entry(ent));
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		entry->fe_ino = args.fa_ino;
4978c2ecf20Sopenharmony_ci		entry->fe_type = args.fa_type;
4988c2ecf20Sopenharmony_ci		entry->fe_done = 0;
4998c2ecf20Sopenharmony_ci		entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
5008c2ecf20Sopenharmony_ci		list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
5018c2ecf20Sopenharmony_ci		ent->fs_fcheck->fc_size++;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci	spin_unlock(&ent->fs_fcheck->fc_lock);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (!ret)
5068c2ecf20Sopenharmony_ci		ocfs2_filecheck_handle_entry(ent, entry);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ciexit:
5098c2ecf20Sopenharmony_ci	return (!ret ? count : ret);
5108c2ecf20Sopenharmony_ci}
511