18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* FS-Cache worker operation management routines
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * See Documentation/filesystems/caching/operations.rst
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define FSCACHE_DEBUG_LEVEL OPERATION
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include "internal.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciatomic_t fscache_op_debug_id;
178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fscache_op_debug_id);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic void fscache_operation_dummy_cancel(struct fscache_operation *op)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/**
248c2ecf20Sopenharmony_ci * fscache_operation_init - Do basic initialisation of an operation
258c2ecf20Sopenharmony_ci * @op: The operation to initialise
268c2ecf20Sopenharmony_ci * @release: The release function to assign
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Do basic initialisation of an operation.  The caller must still set flags,
298c2ecf20Sopenharmony_ci * object and processor if needed.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_civoid fscache_operation_init(struct fscache_cookie *cookie,
328c2ecf20Sopenharmony_ci			    struct fscache_operation *op,
338c2ecf20Sopenharmony_ci			    fscache_operation_processor_t processor,
348c2ecf20Sopenharmony_ci			    fscache_operation_cancel_t cancel,
358c2ecf20Sopenharmony_ci			    fscache_operation_release_t release)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	INIT_WORK(&op->work, fscache_op_work_func);
388c2ecf20Sopenharmony_ci	atomic_set(&op->usage, 1);
398c2ecf20Sopenharmony_ci	op->state = FSCACHE_OP_ST_INITIALISED;
408c2ecf20Sopenharmony_ci	op->debug_id = atomic_inc_return(&fscache_op_debug_id);
418c2ecf20Sopenharmony_ci	op->processor = processor;
428c2ecf20Sopenharmony_ci	op->cancel = cancel ?: fscache_operation_dummy_cancel;
438c2ecf20Sopenharmony_ci	op->release = release;
448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&op->pend_link);
458c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_op_initialised);
468c2ecf20Sopenharmony_ci	trace_fscache_op(cookie, op, fscache_op_init);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fscache_operation_init);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/**
518c2ecf20Sopenharmony_ci * fscache_enqueue_operation - Enqueue an operation for processing
528c2ecf20Sopenharmony_ci * @op: The operation to enqueue
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Enqueue an operation for processing by the FS-Cache thread pool.
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * This will get its own ref on the object.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_civoid fscache_enqueue_operation(struct fscache_operation *op)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie = op->object->cookie;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	_enter("{OBJ%x OP%x,%u}",
638c2ecf20Sopenharmony_ci	       op->object->debug_id, op->debug_id, atomic_read(&op->usage));
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ASSERT(list_empty(&op->pend_link));
668c2ecf20Sopenharmony_ci	ASSERT(op->processor != NULL);
678c2ecf20Sopenharmony_ci	ASSERT(fscache_object_is_available(op->object));
688c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&op->usage), >, 0);
698c2ecf20Sopenharmony_ci	ASSERTIFCMP(op->state != FSCACHE_OP_ST_IN_PROGRESS,
708c2ecf20Sopenharmony_ci		    op->state, ==,  FSCACHE_OP_ST_CANCELLED);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_op_enqueue);
738c2ecf20Sopenharmony_ci	switch (op->flags & FSCACHE_OP_TYPE) {
748c2ecf20Sopenharmony_ci	case FSCACHE_OP_ASYNC:
758c2ecf20Sopenharmony_ci		trace_fscache_op(cookie, op, fscache_op_enqueue_async);
768c2ecf20Sopenharmony_ci		_debug("queue async");
778c2ecf20Sopenharmony_ci		atomic_inc(&op->usage);
788c2ecf20Sopenharmony_ci		if (!queue_work(fscache_op_wq, &op->work))
798c2ecf20Sopenharmony_ci			fscache_put_operation(op);
808c2ecf20Sopenharmony_ci		break;
818c2ecf20Sopenharmony_ci	case FSCACHE_OP_MYTHREAD:
828c2ecf20Sopenharmony_ci		trace_fscache_op(cookie, op, fscache_op_enqueue_mythread);
838c2ecf20Sopenharmony_ci		_debug("queue for caller's attention");
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	default:
868c2ecf20Sopenharmony_ci		pr_err("Unexpected op type %lx", op->flags);
878c2ecf20Sopenharmony_ci		BUG();
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fscache_enqueue_operation);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci * start an op running
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic void fscache_run_op(struct fscache_object *object,
978c2ecf20Sopenharmony_ci			   struct fscache_operation *op)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	op->state = FSCACHE_OP_ST_IN_PROGRESS;
1028c2ecf20Sopenharmony_ci	object->n_in_progress++;
1038c2ecf20Sopenharmony_ci	if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
1048c2ecf20Sopenharmony_ci		wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
1058c2ecf20Sopenharmony_ci	if (op->processor)
1068c2ecf20Sopenharmony_ci		fscache_enqueue_operation(op);
1078c2ecf20Sopenharmony_ci	else
1088c2ecf20Sopenharmony_ci		trace_fscache_op(object->cookie, op, fscache_op_run);
1098c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_op_run);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * report an unexpected submission
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void fscache_report_unexpected_submission(struct fscache_object *object,
1168c2ecf20Sopenharmony_ci						 struct fscache_operation *op,
1178c2ecf20Sopenharmony_ci						 const struct fscache_state *ostate)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	static bool once_only;
1208c2ecf20Sopenharmony_ci	struct fscache_operation *p;
1218c2ecf20Sopenharmony_ci	unsigned n;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (once_only)
1248c2ecf20Sopenharmony_ci		return;
1258c2ecf20Sopenharmony_ci	once_only = true;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	kdebug("unexpected submission OP%x [OBJ%x %s]",
1288c2ecf20Sopenharmony_ci	       op->debug_id, object->debug_id, object->state->name);
1298c2ecf20Sopenharmony_ci	kdebug("objstate=%s [%s]", object->state->name, ostate->name);
1308c2ecf20Sopenharmony_ci	kdebug("objflags=%lx", object->flags);
1318c2ecf20Sopenharmony_ci	kdebug("objevent=%lx [%lx]", object->events, object->event_mask);
1328c2ecf20Sopenharmony_ci	kdebug("ops=%u inp=%u exc=%u",
1338c2ecf20Sopenharmony_ci	       object->n_ops, object->n_in_progress, object->n_exclusive);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (!list_empty(&object->pending_ops)) {
1368c2ecf20Sopenharmony_ci		n = 0;
1378c2ecf20Sopenharmony_ci		list_for_each_entry(p, &object->pending_ops, pend_link) {
1388c2ecf20Sopenharmony_ci			ASSERTCMP(p->object, ==, object);
1398c2ecf20Sopenharmony_ci			kdebug("%p %p", op->processor, op->release);
1408c2ecf20Sopenharmony_ci			n++;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		kdebug("n=%u", n);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	dump_stack();
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * submit an exclusive operation for an object
1518c2ecf20Sopenharmony_ci * - other ops are excluded from running simultaneously with this one
1528c2ecf20Sopenharmony_ci * - this gets any extra refs it needs on an op
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_ciint fscache_submit_exclusive_op(struct fscache_object *object,
1558c2ecf20Sopenharmony_ci				struct fscache_operation *op)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	const struct fscache_state *ostate;
1588c2ecf20Sopenharmony_ci	unsigned long flags;
1598c2ecf20Sopenharmony_ci	int ret;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	_enter("{OBJ%x OP%x},", object->debug_id, op->debug_id);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	trace_fscache_op(object->cookie, op, fscache_op_submit_ex);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED);
1668c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&op->usage), >, 0);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	spin_lock(&object->lock);
1698c2ecf20Sopenharmony_ci	ASSERTCMP(object->n_ops, >=, object->n_in_progress);
1708c2ecf20Sopenharmony_ci	ASSERTCMP(object->n_ops, >=, object->n_exclusive);
1718c2ecf20Sopenharmony_ci	ASSERT(list_empty(&op->pend_link));
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	ostate = object->state;
1748c2ecf20Sopenharmony_ci	smp_rmb();
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	op->state = FSCACHE_OP_ST_PENDING;
1778c2ecf20Sopenharmony_ci	flags = READ_ONCE(object->flags);
1788c2ecf20Sopenharmony_ci	if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) {
1798c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_rejected);
1808c2ecf20Sopenharmony_ci		op->cancel(op);
1818c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
1828c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
1838c2ecf20Sopenharmony_ci	} else if (unlikely(fscache_cache_is_broken(object))) {
1848c2ecf20Sopenharmony_ci		op->cancel(op);
1858c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
1868c2ecf20Sopenharmony_ci		ret = -EIO;
1878c2ecf20Sopenharmony_ci	} else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) {
1888c2ecf20Sopenharmony_ci		op->object = object;
1898c2ecf20Sopenharmony_ci		object->n_ops++;
1908c2ecf20Sopenharmony_ci		object->n_exclusive++;	/* reads and writes must wait */
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		if (object->n_in_progress > 0) {
1938c2ecf20Sopenharmony_ci			atomic_inc(&op->usage);
1948c2ecf20Sopenharmony_ci			list_add_tail(&op->pend_link, &object->pending_ops);
1958c2ecf20Sopenharmony_ci			fscache_stat(&fscache_n_op_pend);
1968c2ecf20Sopenharmony_ci		} else if (!list_empty(&object->pending_ops)) {
1978c2ecf20Sopenharmony_ci			atomic_inc(&op->usage);
1988c2ecf20Sopenharmony_ci			list_add_tail(&op->pend_link, &object->pending_ops);
1998c2ecf20Sopenharmony_ci			fscache_stat(&fscache_n_op_pend);
2008c2ecf20Sopenharmony_ci			fscache_start_operations(object);
2018c2ecf20Sopenharmony_ci		} else {
2028c2ecf20Sopenharmony_ci			ASSERTCMP(object->n_in_progress, ==, 0);
2038c2ecf20Sopenharmony_ci			fscache_run_op(object, op);
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		/* need to issue a new write op after this */
2078c2ecf20Sopenharmony_ci		clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);
2088c2ecf20Sopenharmony_ci		ret = 0;
2098c2ecf20Sopenharmony_ci	} else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) {
2108c2ecf20Sopenharmony_ci		op->object = object;
2118c2ecf20Sopenharmony_ci		object->n_ops++;
2128c2ecf20Sopenharmony_ci		object->n_exclusive++;	/* reads and writes must wait */
2138c2ecf20Sopenharmony_ci		atomic_inc(&op->usage);
2148c2ecf20Sopenharmony_ci		list_add_tail(&op->pend_link, &object->pending_ops);
2158c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_pend);
2168c2ecf20Sopenharmony_ci		ret = 0;
2178c2ecf20Sopenharmony_ci	} else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) {
2188c2ecf20Sopenharmony_ci		op->cancel(op);
2198c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
2208c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
2218c2ecf20Sopenharmony_ci	} else {
2228c2ecf20Sopenharmony_ci		fscache_report_unexpected_submission(object, op, ostate);
2238c2ecf20Sopenharmony_ci		op->cancel(op);
2248c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
2258c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	spin_unlock(&object->lock);
2298c2ecf20Sopenharmony_ci	return ret;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/*
2338c2ecf20Sopenharmony_ci * submit an operation for an object
2348c2ecf20Sopenharmony_ci * - objects may be submitted only in the following states:
2358c2ecf20Sopenharmony_ci *   - during object creation (write ops may be submitted)
2368c2ecf20Sopenharmony_ci *   - whilst the object is active
2378c2ecf20Sopenharmony_ci *   - after an I/O error incurred in one of the two above states (op rejected)
2388c2ecf20Sopenharmony_ci * - this gets any extra refs it needs on an op
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_ciint fscache_submit_op(struct fscache_object *object,
2418c2ecf20Sopenharmony_ci		      struct fscache_operation *op)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	const struct fscache_state *ostate;
2448c2ecf20Sopenharmony_ci	unsigned long flags;
2458c2ecf20Sopenharmony_ci	int ret;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	_enter("{OBJ%x OP%x},{%u}",
2488c2ecf20Sopenharmony_ci	       object->debug_id, op->debug_id, atomic_read(&op->usage));
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	trace_fscache_op(object->cookie, op, fscache_op_submit);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED);
2538c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&op->usage), >, 0);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	spin_lock(&object->lock);
2568c2ecf20Sopenharmony_ci	ASSERTCMP(object->n_ops, >=, object->n_in_progress);
2578c2ecf20Sopenharmony_ci	ASSERTCMP(object->n_ops, >=, object->n_exclusive);
2588c2ecf20Sopenharmony_ci	ASSERT(list_empty(&op->pend_link));
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	ostate = object->state;
2618c2ecf20Sopenharmony_ci	smp_rmb();
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	op->state = FSCACHE_OP_ST_PENDING;
2648c2ecf20Sopenharmony_ci	flags = READ_ONCE(object->flags);
2658c2ecf20Sopenharmony_ci	if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) {
2668c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_rejected);
2678c2ecf20Sopenharmony_ci		op->cancel(op);
2688c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
2698c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
2708c2ecf20Sopenharmony_ci	} else if (unlikely(fscache_cache_is_broken(object))) {
2718c2ecf20Sopenharmony_ci		op->cancel(op);
2728c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
2738c2ecf20Sopenharmony_ci		ret = -EIO;
2748c2ecf20Sopenharmony_ci	} else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) {
2758c2ecf20Sopenharmony_ci		op->object = object;
2768c2ecf20Sopenharmony_ci		object->n_ops++;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		if (object->n_exclusive > 0) {
2798c2ecf20Sopenharmony_ci			atomic_inc(&op->usage);
2808c2ecf20Sopenharmony_ci			list_add_tail(&op->pend_link, &object->pending_ops);
2818c2ecf20Sopenharmony_ci			fscache_stat(&fscache_n_op_pend);
2828c2ecf20Sopenharmony_ci		} else if (!list_empty(&object->pending_ops)) {
2838c2ecf20Sopenharmony_ci			atomic_inc(&op->usage);
2848c2ecf20Sopenharmony_ci			list_add_tail(&op->pend_link, &object->pending_ops);
2858c2ecf20Sopenharmony_ci			fscache_stat(&fscache_n_op_pend);
2868c2ecf20Sopenharmony_ci			fscache_start_operations(object);
2878c2ecf20Sopenharmony_ci		} else {
2888c2ecf20Sopenharmony_ci			ASSERTCMP(object->n_exclusive, ==, 0);
2898c2ecf20Sopenharmony_ci			fscache_run_op(object, op);
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci		ret = 0;
2928c2ecf20Sopenharmony_ci	} else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) {
2938c2ecf20Sopenharmony_ci		op->object = object;
2948c2ecf20Sopenharmony_ci		object->n_ops++;
2958c2ecf20Sopenharmony_ci		atomic_inc(&op->usage);
2968c2ecf20Sopenharmony_ci		list_add_tail(&op->pend_link, &object->pending_ops);
2978c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_pend);
2988c2ecf20Sopenharmony_ci		ret = 0;
2998c2ecf20Sopenharmony_ci	} else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) {
3008c2ecf20Sopenharmony_ci		op->cancel(op);
3018c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
3028c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
3038c2ecf20Sopenharmony_ci	} else {
3048c2ecf20Sopenharmony_ci		fscache_report_unexpected_submission(object, op, ostate);
3058c2ecf20Sopenharmony_ci		ASSERT(!fscache_object_is_active(object));
3068c2ecf20Sopenharmony_ci		op->cancel(op);
3078c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
3088c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	spin_unlock(&object->lock);
3128c2ecf20Sopenharmony_ci	return ret;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/*
3168c2ecf20Sopenharmony_ci * queue an object for withdrawal on error, aborting all following asynchronous
3178c2ecf20Sopenharmony_ci * operations
3188c2ecf20Sopenharmony_ci */
3198c2ecf20Sopenharmony_civoid fscache_abort_object(struct fscache_object *object)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	_enter("{OBJ%x}", object->debug_id);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci/*
3278c2ecf20Sopenharmony_ci * Jump start the operation processing on an object.  The caller must hold
3288c2ecf20Sopenharmony_ci * object->lock.
3298c2ecf20Sopenharmony_ci */
3308c2ecf20Sopenharmony_civoid fscache_start_operations(struct fscache_object *object)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct fscache_operation *op;
3338c2ecf20Sopenharmony_ci	bool stop = false;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	while (!list_empty(&object->pending_ops) && !stop) {
3368c2ecf20Sopenharmony_ci		op = list_entry(object->pending_ops.next,
3378c2ecf20Sopenharmony_ci				struct fscache_operation, pend_link);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) {
3408c2ecf20Sopenharmony_ci			if (object->n_in_progress > 0)
3418c2ecf20Sopenharmony_ci				break;
3428c2ecf20Sopenharmony_ci			stop = true;
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci		list_del_init(&op->pend_link);
3458c2ecf20Sopenharmony_ci		fscache_run_op(object, op);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		/* the pending queue was holding a ref on the object */
3488c2ecf20Sopenharmony_ci		fscache_put_operation(op);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	ASSERTCMP(object->n_in_progress, <=, object->n_ops);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	_debug("woke %d ops on OBJ%x",
3548c2ecf20Sopenharmony_ci	       object->n_in_progress, object->debug_id);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/*
3588c2ecf20Sopenharmony_ci * cancel an operation that's pending on an object
3598c2ecf20Sopenharmony_ci */
3608c2ecf20Sopenharmony_ciint fscache_cancel_op(struct fscache_operation *op,
3618c2ecf20Sopenharmony_ci		      bool cancel_in_progress_op)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct fscache_object *object = op->object;
3648c2ecf20Sopenharmony_ci	bool put = false;
3658c2ecf20Sopenharmony_ci	int ret;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	_enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	trace_fscache_op(object->cookie, op, fscache_op_cancel);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ASSERTCMP(op->state, >=, FSCACHE_OP_ST_PENDING);
3728c2ecf20Sopenharmony_ci	ASSERTCMP(op->state, !=, FSCACHE_OP_ST_CANCELLED);
3738c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&op->usage), >, 0);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	spin_lock(&object->lock);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ret = -EBUSY;
3788c2ecf20Sopenharmony_ci	if (op->state == FSCACHE_OP_ST_PENDING) {
3798c2ecf20Sopenharmony_ci		ASSERT(!list_empty(&op->pend_link));
3808c2ecf20Sopenharmony_ci		list_del_init(&op->pend_link);
3818c2ecf20Sopenharmony_ci		put = true;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_cancelled);
3848c2ecf20Sopenharmony_ci		op->cancel(op);
3858c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
3868c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
3878c2ecf20Sopenharmony_ci			object->n_exclusive--;
3888c2ecf20Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
3898c2ecf20Sopenharmony_ci			wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
3908c2ecf20Sopenharmony_ci		ret = 0;
3918c2ecf20Sopenharmony_ci	} else if (op->state == FSCACHE_OP_ST_IN_PROGRESS && cancel_in_progress_op) {
3928c2ecf20Sopenharmony_ci		ASSERTCMP(object->n_in_progress, >, 0);
3938c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
3948c2ecf20Sopenharmony_ci			object->n_exclusive--;
3958c2ecf20Sopenharmony_ci		object->n_in_progress--;
3968c2ecf20Sopenharmony_ci		if (object->n_in_progress == 0)
3978c2ecf20Sopenharmony_ci			fscache_start_operations(object);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_cancelled);
4008c2ecf20Sopenharmony_ci		op->cancel(op);
4018c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
4028c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
4038c2ecf20Sopenharmony_ci			object->n_exclusive--;
4048c2ecf20Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
4058c2ecf20Sopenharmony_ci			wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
4068c2ecf20Sopenharmony_ci		ret = 0;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (put)
4108c2ecf20Sopenharmony_ci		fscache_put_operation(op);
4118c2ecf20Sopenharmony_ci	spin_unlock(&object->lock);
4128c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
4138c2ecf20Sopenharmony_ci	return ret;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/*
4178c2ecf20Sopenharmony_ci * Cancel all pending operations on an object
4188c2ecf20Sopenharmony_ci */
4198c2ecf20Sopenharmony_civoid fscache_cancel_all_ops(struct fscache_object *object)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct fscache_operation *op;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	_enter("OBJ%x", object->debug_id);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	spin_lock(&object->lock);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	while (!list_empty(&object->pending_ops)) {
4288c2ecf20Sopenharmony_ci		op = list_entry(object->pending_ops.next,
4298c2ecf20Sopenharmony_ci				struct fscache_operation, pend_link);
4308c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_cancelled);
4318c2ecf20Sopenharmony_ci		list_del_init(&op->pend_link);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		trace_fscache_op(object->cookie, op, fscache_op_cancel_all);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING);
4368c2ecf20Sopenharmony_ci		op->cancel(op);
4378c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
4408c2ecf20Sopenharmony_ci			object->n_exclusive--;
4418c2ecf20Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
4428c2ecf20Sopenharmony_ci			wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
4438c2ecf20Sopenharmony_ci		fscache_put_operation(op);
4448c2ecf20Sopenharmony_ci		cond_resched_lock(&object->lock);
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	spin_unlock(&object->lock);
4488c2ecf20Sopenharmony_ci	_leave("");
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/*
4528c2ecf20Sopenharmony_ci * Record the completion or cancellation of an in-progress operation.
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_civoid fscache_op_complete(struct fscache_operation *op, bool cancelled)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct fscache_object *object = op->object;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	_enter("OBJ%x", object->debug_id);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_IN_PROGRESS);
4618c2ecf20Sopenharmony_ci	ASSERTCMP(object->n_in_progress, >, 0);
4628c2ecf20Sopenharmony_ci	ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags),
4638c2ecf20Sopenharmony_ci		    object->n_exclusive, >, 0);
4648c2ecf20Sopenharmony_ci	ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags),
4658c2ecf20Sopenharmony_ci		    object->n_in_progress, ==, 1);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	spin_lock(&object->lock);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (!cancelled) {
4708c2ecf20Sopenharmony_ci		trace_fscache_op(object->cookie, op, fscache_op_completed);
4718c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_COMPLETE;
4728c2ecf20Sopenharmony_ci	} else {
4738c2ecf20Sopenharmony_ci		op->cancel(op);
4748c2ecf20Sopenharmony_ci		trace_fscache_op(object->cookie, op, fscache_op_cancelled);
4758c2ecf20Sopenharmony_ci		op->state = FSCACHE_OP_ST_CANCELLED;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
4798c2ecf20Sopenharmony_ci		object->n_exclusive--;
4808c2ecf20Sopenharmony_ci	object->n_in_progress--;
4818c2ecf20Sopenharmony_ci	if (object->n_in_progress == 0)
4828c2ecf20Sopenharmony_ci		fscache_start_operations(object);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	spin_unlock(&object->lock);
4858c2ecf20Sopenharmony_ci	_leave("");
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fscache_op_complete);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/*
4908c2ecf20Sopenharmony_ci * release an operation
4918c2ecf20Sopenharmony_ci * - queues pending ops if this is the last in-progress op
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_civoid fscache_put_operation(struct fscache_operation *op)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	struct fscache_object *object;
4968c2ecf20Sopenharmony_ci	struct fscache_cache *cache;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	_enter("{OBJ%x OP%x,%d}",
4998c2ecf20Sopenharmony_ci	       op->object ? op->object->debug_id : 0,
5008c2ecf20Sopenharmony_ci	       op->debug_id, atomic_read(&op->usage));
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&op->usage), >, 0);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (!atomic_dec_and_test(&op->usage))
5058c2ecf20Sopenharmony_ci		return;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	trace_fscache_op(op->object ? op->object->cookie : NULL, op, fscache_op_put);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	_debug("PUT OP");
5108c2ecf20Sopenharmony_ci	ASSERTIFCMP(op->state != FSCACHE_OP_ST_INITIALISED &&
5118c2ecf20Sopenharmony_ci		    op->state != FSCACHE_OP_ST_COMPLETE,
5128c2ecf20Sopenharmony_ci		    op->state, ==, FSCACHE_OP_ST_CANCELLED);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_op_release);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (op->release) {
5178c2ecf20Sopenharmony_ci		op->release(op);
5188c2ecf20Sopenharmony_ci		op->release = NULL;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	op->state = FSCACHE_OP_ST_DEAD;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	object = op->object;
5238c2ecf20Sopenharmony_ci	if (likely(object)) {
5248c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags))
5258c2ecf20Sopenharmony_ci			atomic_dec(&object->n_reads);
5268c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags))
5278c2ecf20Sopenharmony_ci			fscache_unuse_cookie(object);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		/* now... we may get called with the object spinlock held, so we
5308c2ecf20Sopenharmony_ci		 * complete the cleanup here only if we can immediately acquire the
5318c2ecf20Sopenharmony_ci		 * lock, and defer it otherwise */
5328c2ecf20Sopenharmony_ci		if (!spin_trylock(&object->lock)) {
5338c2ecf20Sopenharmony_ci			_debug("defer put");
5348c2ecf20Sopenharmony_ci			fscache_stat(&fscache_n_op_deferred_release);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci			cache = object->cache;
5378c2ecf20Sopenharmony_ci			spin_lock(&cache->op_gc_list_lock);
5388c2ecf20Sopenharmony_ci			list_add_tail(&op->pend_link, &cache->op_gc_list);
5398c2ecf20Sopenharmony_ci			spin_unlock(&cache->op_gc_list_lock);
5408c2ecf20Sopenharmony_ci			schedule_work(&cache->op_gc);
5418c2ecf20Sopenharmony_ci			_leave(" [defer]");
5428c2ecf20Sopenharmony_ci			return;
5438c2ecf20Sopenharmony_ci		}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		ASSERTCMP(object->n_ops, >, 0);
5468c2ecf20Sopenharmony_ci		object->n_ops--;
5478c2ecf20Sopenharmony_ci		if (object->n_ops == 0)
5488c2ecf20Sopenharmony_ci			fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		spin_unlock(&object->lock);
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	kfree(op);
5548c2ecf20Sopenharmony_ci	_leave(" [done]");
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fscache_put_operation);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci/*
5598c2ecf20Sopenharmony_ci * garbage collect operations that have had their release deferred
5608c2ecf20Sopenharmony_ci */
5618c2ecf20Sopenharmony_civoid fscache_operation_gc(struct work_struct *work)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct fscache_operation *op;
5648c2ecf20Sopenharmony_ci	struct fscache_object *object;
5658c2ecf20Sopenharmony_ci	struct fscache_cache *cache =
5668c2ecf20Sopenharmony_ci		container_of(work, struct fscache_cache, op_gc);
5678c2ecf20Sopenharmony_ci	int count = 0;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	_enter("");
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	do {
5728c2ecf20Sopenharmony_ci		spin_lock(&cache->op_gc_list_lock);
5738c2ecf20Sopenharmony_ci		if (list_empty(&cache->op_gc_list)) {
5748c2ecf20Sopenharmony_ci			spin_unlock(&cache->op_gc_list_lock);
5758c2ecf20Sopenharmony_ci			break;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		op = list_entry(cache->op_gc_list.next,
5798c2ecf20Sopenharmony_ci				struct fscache_operation, pend_link);
5808c2ecf20Sopenharmony_ci		list_del(&op->pend_link);
5818c2ecf20Sopenharmony_ci		spin_unlock(&cache->op_gc_list_lock);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		object = op->object;
5848c2ecf20Sopenharmony_ci		trace_fscache_op(object->cookie, op, fscache_op_gc);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		spin_lock(&object->lock);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		_debug("GC DEFERRED REL OBJ%x OP%x",
5898c2ecf20Sopenharmony_ci		       object->debug_id, op->debug_id);
5908c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_op_gc);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		ASSERTCMP(atomic_read(&op->usage), ==, 0);
5938c2ecf20Sopenharmony_ci		ASSERTCMP(op->state, ==, FSCACHE_OP_ST_DEAD);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		ASSERTCMP(object->n_ops, >, 0);
5968c2ecf20Sopenharmony_ci		object->n_ops--;
5978c2ecf20Sopenharmony_ci		if (object->n_ops == 0)
5988c2ecf20Sopenharmony_ci			fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		spin_unlock(&object->lock);
6018c2ecf20Sopenharmony_ci		kfree(op);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	} while (count++ < 20);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (!list_empty(&cache->op_gc_list))
6068c2ecf20Sopenharmony_ci		schedule_work(&cache->op_gc);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	_leave("");
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/*
6128c2ecf20Sopenharmony_ci * execute an operation using fs_op_wq to provide processing context -
6138c2ecf20Sopenharmony_ci * the caller holds a ref to this object, so we don't need to hold one
6148c2ecf20Sopenharmony_ci */
6158c2ecf20Sopenharmony_civoid fscache_op_work_func(struct work_struct *work)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct fscache_operation *op =
6188c2ecf20Sopenharmony_ci		container_of(work, struct fscache_operation, work);
6198c2ecf20Sopenharmony_ci	unsigned long start;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	_enter("{OBJ%x OP%x,%d}",
6228c2ecf20Sopenharmony_ci	       op->object->debug_id, op->debug_id, atomic_read(&op->usage));
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	trace_fscache_op(op->object->cookie, op, fscache_op_work);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ASSERT(op->processor != NULL);
6278c2ecf20Sopenharmony_ci	start = jiffies;
6288c2ecf20Sopenharmony_ci	op->processor(op);
6298c2ecf20Sopenharmony_ci	fscache_hist(fscache_ops_histogram, start);
6308c2ecf20Sopenharmony_ci	fscache_put_operation(op);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	_leave("");
6338c2ecf20Sopenharmony_ci}
634