/* * execute an object */ static void fscache_object_slow_work_execute(struct slow_work *work) { struct fscache_object *object = container_of(work, struct fscache_object, work); unsigned long start; _enter("{OBJ%x}", object->debug_id); start = jiffies; fscache_object_state_machine(object); fscache_hist(fscache_objs_histogram, start); if (object->events & object->event_mask) fscache_enqueue_object(object); clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); }
/* * initialise an object * - check the specified object's parent to see if we can make use of it * immediately to do a creation * - we may need to start the process of creating a parent and we need to wait * for the parent's lookup and creation to complete if it's not there yet * - an object's cookie is pinned until we clear FSCACHE_COOKIE_CREATING on the * leaf-most cookies of the object and all its children */ static void fscache_initialise_object(struct fscache_object *object) { struct fscache_object *parent; _enter(""); ASSERT(object->cookie != NULL); ASSERT(object->cookie->parent != NULL); ASSERT(list_empty(&object->work.link)); if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | (1 << FSCACHE_OBJECT_EV_RELEASE) | (1 << FSCACHE_OBJECT_EV_RETIRE) | (1 << FSCACHE_OBJECT_EV_WITHDRAW))) { _debug("abort init %lx", object->events); spin_lock(&object->lock); object->state = FSCACHE_OBJECT_ABORT_INIT; spin_unlock(&object->lock); return; } spin_lock(&object->cookie->lock); spin_lock_nested(&object->cookie->parent->lock, 1); parent = object->parent; if (!parent) { _debug("no parent"); set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); } else { spin_lock(&object->lock); spin_lock_nested(&parent->lock, 1); _debug("parent %s", fscache_object_states[parent->state]); if (parent->state >= FSCACHE_OBJECT_DYING) { _debug("bad parent"); set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); } else if (parent->state < FSCACHE_OBJECT_AVAILABLE) { _debug("wait"); /* we may get woken up in this state by child objects * binding on to us, so we need to make sure we don't * add ourself to the list multiple times */ if (list_empty(&object->dep_link)) { fscache_stat(&fscache_n_cop_grab_object); object->cache->ops->grab_object(object); fscache_stat_d(&fscache_n_cop_grab_object); list_add(&object->dep_link, &parent->dependents); /* fscache_acquire_non_index_cookie() uses this * to wake the chain up */ if (parent->state == FSCACHE_OBJECT_INIT) fscache_enqueue_object(parent); } } else { _debug("go"); parent->n_ops++; parent->n_obj_ops++; object->lookup_jif = jiffies; object->state = FSCACHE_OBJECT_LOOKING_UP; set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); } spin_unlock(&parent->lock); spin_unlock(&object->lock); } spin_unlock(&object->cookie->parent->lock); spin_unlock(&object->cookie->lock); _leave(""); }
static void fscache_initialise_object(struct fscache_object *object) { struct fscache_object *parent; _enter(""); ASSERT(object->cookie != NULL); ASSERT(object->cookie->parent != NULL); if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | (1 << FSCACHE_OBJECT_EV_RELEASE) | (1 << FSCACHE_OBJECT_EV_RETIRE) | (1 << FSCACHE_OBJECT_EV_WITHDRAW))) { _debug("abort init %lx", object->events); spin_lock(&object->lock); object->state = FSCACHE_OBJECT_ABORT_INIT; spin_unlock(&object->lock); return; } spin_lock(&object->cookie->lock); spin_lock_nested(&object->cookie->parent->lock, 1); parent = object->parent; if (!parent) { _debug("no parent"); set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); } else { spin_lock(&object->lock); spin_lock_nested(&parent->lock, 1); _debug("parent %s", fscache_object_states[parent->state]); if (parent->state >= FSCACHE_OBJECT_DYING) { _debug("bad parent"); set_bit(FSCACHE_OBJECT_EV_WITHDRAW, &object->events); } else if (parent->state < FSCACHE_OBJECT_AVAILABLE) { _debug("wait"); if (list_empty(&object->dep_link)) { fscache_stat(&fscache_n_cop_grab_object); object->cache->ops->grab_object(object); fscache_stat_d(&fscache_n_cop_grab_object); list_add(&object->dep_link, &parent->dependents); if (parent->state == FSCACHE_OBJECT_INIT) fscache_enqueue_object(parent); } } else { _debug("go"); parent->n_ops++; parent->n_obj_ops++; object->lookup_jif = jiffies; object->state = FSCACHE_OBJECT_LOOKING_UP; set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); } spin_unlock(&parent->lock); spin_unlock(&object->lock); } spin_unlock(&object->cookie->parent->lock); spin_unlock(&object->cookie->lock); _leave(""); }
/* * Object state machine dispatcher. */ static void fscache_object_sm_dispatcher(struct fscache_object *object) { const struct fscache_transition *t; const struct fscache_state *state, *new_state; unsigned long events, event_mask; int event = -1; ASSERT(object != NULL); _enter("{OBJ%x,%s,%lx}", object->debug_id, object->state->name, object->events); event_mask = object->event_mask; restart: object->event_mask = 0; /* Mask normal event handling */ state = object->state; restart_masked: events = object->events; /* Handle any out-of-band events (typically an error) */ if (events & object->oob_event_mask) { _debug("{OBJ%x} oob %lx", object->debug_id, events & object->oob_event_mask); for (t = object->oob_table; t->events; t++) { if (events & t->events) { state = t->transit_to; ASSERT(state->work != NULL); event = fls(events & t->events) - 1; __clear_bit(event, &object->oob_event_mask); clear_bit(event, &object->events); goto execute_work_state; } } } /* Wait states are just transition tables */ if (!state->work) { if (events & event_mask) { for (t = state->transitions; t->events; t++) { if (events & t->events) { new_state = t->transit_to; event = fls(events & t->events) - 1; clear_bit(event, &object->events); _debug("{OBJ%x} ev %d: %s -> %s", object->debug_id, event, state->name, new_state->name); object->state = state = new_state; goto execute_work_state; } } /* The event mask didn't include all the tabled bits */ BUG(); } /* Randomly woke up */ goto unmask_events; } execute_work_state: _debug("{OBJ%x} exec %s", object->debug_id, state->name); new_state = state->work(object, event); event = -1; if (new_state == NO_TRANSIT) { _debug("{OBJ%x} %s notrans", object->debug_id, state->name); if (unlikely(state == STATE(OBJECT_DEAD))) { _leave(" [dead]"); return; } fscache_enqueue_object(object); event_mask = object->oob_event_mask; goto unmask_events; } _debug("{OBJ%x} %s -> %s", object->debug_id, state->name, new_state->name); object->state = state = new_state; if (state->work) { if (unlikely(state == STATE(OBJECT_DEAD))) { _leave(" [dead]"); return; } goto restart_masked; } /* Transited to wait state */ event_mask = object->oob_event_mask; for (t = state->transitions; t->events; t++) event_mask |= t->events; unmask_events: object->event_mask = event_mask; smp_mb(); events = object->events; if (events & event_mask) goto restart; _leave(" [msk %lx]", event_mask); }