// SPDX-License-Identifier: GPL-2.0-or-later
/* FS-Cache object state machine handler
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* See Documentation/filesystems/caching/object.rst for a description of the
* object state machine and the in-kernel representations.
*/
#define FSCACHE_DEBUG_LEVEL COOKIE
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
#include "internal.h"
static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *, int);
static const struct fscache_state *fscache_kill_dependents(struct fscache_object *, int);
static const struct fscache_state *fscache_drop_object(struct fscache_object *, int);
static const struct fscache_state *fscache_initialise_object(struct fscache_object *, int);
static const struct fscache_state *fscache_invalidate_object(struct fscache_object *, int);
static const struct fscache_state *fscache_jumpstart_dependents(struct fscache_object *, int);
static const struct fscache_state *fscache_kill_object(struct fscache_object *, int);
static const struct fscache_state *fscache_lookup_failure(struct fscache_object *, int);
static const struct fscache_state *fscache_look_up_object(struct fscache_object *, int);
static const struct fscache_state *fscache_object_available(struct fscache_object *, int);
static const struct fscache_state *fscache_parent_ready(struct fscache_object *, int);
static const struct fscache_state *fscache_update_object(struct fscache_object *, int);
static const struct fscache_state *fscache_object_dead(struct fscache_object *, int);
#define __STATE_NAME(n) fscache_osm_##n
#define STATE(n) (&__STATE_NAME(n))
/*
* Define a work state. Work states are execution states. No event processing
* is performed by them. The function attached to a work state returns a
* pointer indicating the next state to which the state machine should
* transition. Returning NO_TRANSIT repeats the current state, but goes back
* to the scheduler first.
*/
#define WORK_STATE(n, sn, f) \
const struct fscache_state __STATE_NAME(n) = { \
.name = #n, \
.short_name = sn, \
.work = f \
}
/*
* Returns from work states.
*/
#define transit_to(state) ({ prefetch(&STATE(state)->work); STATE(state); })
#define NO_TRANSIT ((struct fscache_state *)NULL)
/*
* Define a wait state. Wait states are event processing states. No execution
* is performed by them. Wait states are just tables of "if event X occurs,
* clear it and transition to state Y". The dispatcher returns to the
* scheduler if none of the events in which the wait state has an interest are
* currently pending.
*/
#define WAIT_STATE(n, sn, ...) \
const struct fscache_state __STATE_NAME(n) = { \
.name = #n, \
.short_name = sn, \
.work = NULL, \
.transitions = { __VA_ARGS__, { 0, NULL } } \
}
#define TRANSIT_TO(state, emask) \
{ .events = (emask), .transit_to = STATE(state) }
/*
* The object state machine.
*/
static WORK_STATE(INIT_OBJECT, "INIT", fscache_initialise_object);
static WORK_STATE(PARENT_READY, "PRDY", fscache_parent_ready);
static WORK_STATE(ABORT_INIT, "ABRT", fscache_abort_initialisation);
static WORK_STATE(LOOK_UP_OBJECT, "LOOK", fscache_look_up_object);
static WORK_STATE(CREATE_OBJECT, "CRTO", fscache_look_up_object);
static WORK_STATE(OBJECT_AVAILABLE, "AVBL", fscache_object_available);
static WORK_STATE(JUMPSTART_DEPS, "JUMP", fscache_jumpstart_dependents);
static WORK_STATE(INVALIDATE_OBJECT, "INVL", fscache_invalidate_object);
static WORK_STATE(UPDATE_OBJECT, "UPDT", fscache_update_object);
static WORK_STATE(LOOKUP_FAILURE, "LCFL", fscache_lookup_failure);
static WORK_STATE(KILL_OBJECT, "KILL", fscache_kill_object);
static WORK_STATE(KILL_DEPENDENTS, "KDEP", fscache_kill_dependents);
static WORK_STATE(DROP_OBJECT, "DROP", fscache_drop_object);
static WORK_STATE(OBJECT_DEAD, "DEAD", fscache_object_dead);
static WAIT_STATE(WAIT_FOR_INIT, "?INI",
TRANSIT_TO(INIT_OBJECT, 1 << FSCACHE_OBJECT_EV_NEW_CHILD));
static WAIT_STATE(WAIT_FOR_PARENT, "?PRN",
TRANSIT_TO(PARENT_READY, 1 << FSCACHE_OBJECT_EV_PARENT_READY));
static WAIT_STATE(WAIT_FOR_CMD, "?CMD",
TRANSIT_TO(INVALIDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_INVALIDATE),
TRANSIT_TO(UPDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_UPDATE),
TRANSIT_TO(JUMPSTART_DEPS, 1 << FSCACHE_OBJECT_EV_NEW_CHILD));
static WAIT_STATE(WAIT_FOR_CLEARANCE, "?CLR",
TRANSIT_TO(KILL_OBJECT, 1 << FSCACHE_OBJECT_EV_CLEARED));
/*
* Out-of-band event transition tables. These are for handling unexpected
* events, such as an I/O error. If an OOB event occurs, the state machine
* clears and disables the event and forces a transition to the nominated work
* state (acurrently executing work states will complete first).
*
* In such a situation, object->state remembers the state the machine should
* have been in/gone to and returning NO_TRANSIT returns to that.
*/
static const struct fscache_transition fscache_osm_init_oob[] = {
TRANSIT_TO(ABORT_INIT,
(1 << FSCACHE_OBJECT_EV_ERROR) |
(1 << FSCACHE_OBJECT_EV_KILL)),
{ 0, NULL }
};
static const struct fscache_transition fscache_osm_lookup_oob[] = {
TRANSIT_TO(LOOKUP_FAILURE,
(1 << FSCACHE_OBJECT_EV_ERROR) |
(1 << FSCACHE_OBJECT_EV_KILL)),
{ 0, NULL }
};
static const struct fscache_transition fscache_osm_run_oob[] = {
TRANSIT_TO(KILL_OBJECT,
(1 << FSCACHE_OBJECT_EV_ERROR) |
(1 << FSCACHE_OBJECT_EV_KILL)),
{ 0