|
@@ -20,6 +20,35 @@
|
|
|
atomic_t fscache_op_debug_id;
|
|
|
EXPORT_SYMBOL(fscache_op_debug_id);
|
|
|
|
|
|
+static void fscache_operation_dummy_cancel(struct fscache_operation *op)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fscache_operation_init - Do basic initialisation of an operation
|
|
|
+ * @op: The operation to initialise
|
|
|
+ * @release: The release function to assign
|
|
|
+ *
|
|
|
+ * Do basic initialisation of an operation. The caller must still set flags,
|
|
|
+ * object and processor if needed.
|
|
|
+ */
|
|
|
+void fscache_operation_init(struct fscache_operation *op,
|
|
|
+ fscache_operation_processor_t processor,
|
|
|
+ fscache_operation_cancel_t cancel,
|
|
|
+ fscache_operation_release_t release)
|
|
|
+{
|
|
|
+ INIT_WORK(&op->work, fscache_op_work_func);
|
|
|
+ atomic_set(&op->usage, 1);
|
|
|
+ op->state = FSCACHE_OP_ST_INITIALISED;
|
|
|
+ op->debug_id = atomic_inc_return(&fscache_op_debug_id);
|
|
|
+ op->processor = processor;
|
|
|
+ op->cancel = cancel ?: fscache_operation_dummy_cancel;
|
|
|
+ op->release = release;
|
|
|
+ INIT_LIST_HEAD(&op->pend_link);
|
|
|
+ fscache_stat(&fscache_n_op_initialised);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(fscache_operation_init);
|
|
|
+
|
|
|
/**
|
|
|
* fscache_enqueue_operation - Enqueue an operation for processing
|
|
|
* @op: The operation to enqueue
|
|
@@ -75,6 +104,43 @@ static void fscache_run_op(struct fscache_object *object,
|
|
|
fscache_stat(&fscache_n_op_run);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * report an unexpected submission
|
|
|
+ */
|
|
|
+static void fscache_report_unexpected_submission(struct fscache_object *object,
|
|
|
+ struct fscache_operation *op,
|
|
|
+ const struct fscache_state *ostate)
|
|
|
+{
|
|
|
+ static bool once_only;
|
|
|
+ struct fscache_operation *p;
|
|
|
+ unsigned n;
|
|
|
+
|
|
|
+ if (once_only)
|
|
|
+ return;
|
|
|
+ once_only = true;
|
|
|
+
|
|
|
+ kdebug("unexpected submission OP%x [OBJ%x %s]",
|
|
|
+ op->debug_id, object->debug_id, object->state->name);
|
|
|
+ kdebug("objstate=%s [%s]", object->state->name, ostate->name);
|
|
|
+ kdebug("objflags=%lx", object->flags);
|
|
|
+ kdebug("objevent=%lx [%lx]", object->events, object->event_mask);
|
|
|
+ kdebug("ops=%u inp=%u exc=%u",
|
|
|
+ object->n_ops, object->n_in_progress, object->n_exclusive);
|
|
|
+
|
|
|
+ if (!list_empty(&object->pending_ops)) {
|
|
|
+ n = 0;
|
|
|
+ list_for_each_entry(p, &object->pending_ops, pend_link) {
|
|
|
+ ASSERTCMP(p->object, ==, object);
|
|
|
+ kdebug("%p %p", op->processor, op->release);
|
|
|
+ n++;
|
|
|
+ }
|
|
|
+
|
|
|
+ kdebug("n=%u", n);
|
|
|
+ }
|
|
|
+
|
|
|
+ dump_stack();
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* submit an exclusive operation for an object
|
|
|
* - other ops are excluded from running simultaneously with this one
|
|
@@ -83,6 +149,8 @@ static void fscache_run_op(struct fscache_object *object,
|
|
|
int fscache_submit_exclusive_op(struct fscache_object *object,
|
|
|
struct fscache_operation *op)
|
|
|
{
|
|
|
+ const struct fscache_state *ostate;
|
|
|
+ unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
_enter("{OBJ%x OP%x},", object->debug_id, op->debug_id);
|
|
@@ -95,8 +163,21 @@ int fscache_submit_exclusive_op(struct fscache_object *object,
|
|
|
ASSERTCMP(object->n_ops, >=, object->n_exclusive);
|
|
|
ASSERT(list_empty(&op->pend_link));
|
|
|
|
|
|
+ ostate = object->state;
|
|
|
+ smp_rmb();
|
|
|
+
|
|
|
op->state = FSCACHE_OP_ST_PENDING;
|
|
|
- if (fscache_object_is_active(object)) {
|
|
|
+ flags = READ_ONCE(object->flags);
|
|
|
+ if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) {
|
|
|
+ fscache_stat(&fscache_n_op_rejected);
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ ret = -ENOBUFS;
|
|
|
+ } else if (unlikely(fscache_cache_is_broken(object))) {
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ ret = -EIO;
|
|
|
+ } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) {
|
|
|
op->object = object;
|
|
|
object->n_ops++;
|
|
|
object->n_exclusive++; /* reads and writes must wait */
|
|
@@ -118,7 +199,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object,
|
|
|
/* need to issue a new write op after this */
|
|
|
clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);
|
|
|
ret = 0;
|
|
|
- } else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) {
|
|
|
+ } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) {
|
|
|
op->object = object;
|
|
|
object->n_ops++;
|
|
|
object->n_exclusive++; /* reads and writes must wait */
|
|
@@ -126,55 +207,21 @@ int fscache_submit_exclusive_op(struct fscache_object *object,
|
|
|
list_add_tail(&op->pend_link, &object->pending_ops);
|
|
|
fscache_stat(&fscache_n_op_pend);
|
|
|
ret = 0;
|
|
|
+ } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) {
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ ret = -ENOBUFS;
|
|
|
} else {
|
|
|
- /* If we're in any other state, there must have been an I/O
|
|
|
- * error of some nature.
|
|
|
- */
|
|
|
- ASSERT(test_bit(FSCACHE_IOERROR, &object->cache->flags));
|
|
|
- ret = -EIO;
|
|
|
+ fscache_report_unexpected_submission(object, op, ostate);
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ ret = -ENOBUFS;
|
|
|
}
|
|
|
|
|
|
spin_unlock(&object->lock);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * report an unexpected submission
|
|
|
- */
|
|
|
-static void fscache_report_unexpected_submission(struct fscache_object *object,
|
|
|
- struct fscache_operation *op,
|
|
|
- const struct fscache_state *ostate)
|
|
|
-{
|
|
|
- static bool once_only;
|
|
|
- struct fscache_operation *p;
|
|
|
- unsigned n;
|
|
|
-
|
|
|
- if (once_only)
|
|
|
- return;
|
|
|
- once_only = true;
|
|
|
-
|
|
|
- kdebug("unexpected submission OP%x [OBJ%x %s]",
|
|
|
- op->debug_id, object->debug_id, object->state->name);
|
|
|
- kdebug("objstate=%s [%s]", object->state->name, ostate->name);
|
|
|
- kdebug("objflags=%lx", object->flags);
|
|
|
- kdebug("objevent=%lx [%lx]", object->events, object->event_mask);
|
|
|
- kdebug("ops=%u inp=%u exc=%u",
|
|
|
- object->n_ops, object->n_in_progress, object->n_exclusive);
|
|
|
-
|
|
|
- if (!list_empty(&object->pending_ops)) {
|
|
|
- n = 0;
|
|
|
- list_for_each_entry(p, &object->pending_ops, pend_link) {
|
|
|
- ASSERTCMP(p->object, ==, object);
|
|
|
- kdebug("%p %p", op->processor, op->release);
|
|
|
- n++;
|
|
|
- }
|
|
|
-
|
|
|
- kdebug("n=%u", n);
|
|
|
- }
|
|
|
-
|
|
|
- dump_stack();
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* submit an operation for an object
|
|
|
* - objects may be submitted only in the following states:
|
|
@@ -187,6 +234,7 @@ int fscache_submit_op(struct fscache_object *object,
|
|
|
struct fscache_operation *op)
|
|
|
{
|
|
|
const struct fscache_state *ostate;
|
|
|
+ unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
_enter("{OBJ%x OP%x},{%u}",
|
|
@@ -204,7 +252,17 @@ int fscache_submit_op(struct fscache_object *object,
|
|
|
smp_rmb();
|
|
|
|
|
|
op->state = FSCACHE_OP_ST_PENDING;
|
|
|
- if (fscache_object_is_active(object)) {
|
|
|
+ flags = READ_ONCE(object->flags);
|
|
|
+ if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) {
|
|
|
+ fscache_stat(&fscache_n_op_rejected);
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ ret = -ENOBUFS;
|
|
|
+ } else if (unlikely(fscache_cache_is_broken(object))) {
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ ret = -EIO;
|
|
|
+ } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) {
|
|
|
op->object = object;
|
|
|
object->n_ops++;
|
|
|
|
|
@@ -222,23 +280,21 @@ int fscache_submit_op(struct fscache_object *object,
|
|
|
fscache_run_op(object, op);
|
|
|
}
|
|
|
ret = 0;
|
|
|
- } else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) {
|
|
|
+ } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) {
|
|
|
op->object = object;
|
|
|
object->n_ops++;
|
|
|
atomic_inc(&op->usage);
|
|
|
list_add_tail(&op->pend_link, &object->pending_ops);
|
|
|
fscache_stat(&fscache_n_op_pend);
|
|
|
ret = 0;
|
|
|
- } else if (fscache_object_is_dying(object)) {
|
|
|
- fscache_stat(&fscache_n_op_rejected);
|
|
|
+ } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) {
|
|
|
+ op->cancel(op);
|
|
|
op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
ret = -ENOBUFS;
|
|
|
- } else if (!test_bit(FSCACHE_IOERROR, &object->cache->flags)) {
|
|
|
+ } else {
|
|
|
fscache_report_unexpected_submission(object, op, ostate);
|
|
|
ASSERT(!fscache_object_is_active(object));
|
|
|
- op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
- ret = -ENOBUFS;
|
|
|
- } else {
|
|
|
+ op->cancel(op);
|
|
|
op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
ret = -ENOBUFS;
|
|
|
}
|
|
@@ -293,9 +349,10 @@ void fscache_start_operations(struct fscache_object *object)
|
|
|
* cancel an operation that's pending on an object
|
|
|
*/
|
|
|
int fscache_cancel_op(struct fscache_operation *op,
|
|
|
- void (*do_cancel)(struct fscache_operation *))
|
|
|
+ bool cancel_in_progress_op)
|
|
|
{
|
|
|
struct fscache_object *object = op->object;
|
|
|
+ bool put = false;
|
|
|
int ret;
|
|
|
|
|
|
_enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id);
|
|
@@ -309,19 +366,37 @@ int fscache_cancel_op(struct fscache_operation *op,
|
|
|
ret = -EBUSY;
|
|
|
if (op->state == FSCACHE_OP_ST_PENDING) {
|
|
|
ASSERT(!list_empty(&op->pend_link));
|
|
|
- fscache_stat(&fscache_n_op_cancelled);
|
|
|
list_del_init(&op->pend_link);
|
|
|
- if (do_cancel)
|
|
|
- do_cancel(op);
|
|
|
+ put = true;
|
|
|
+
|
|
|
+ fscache_stat(&fscache_n_op_cancelled);
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
|
|
|
+ object->n_exclusive--;
|
|
|
+ if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
|
|
|
+ wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
|
|
|
+ ret = 0;
|
|
|
+ } else if (op->state == FSCACHE_OP_ST_IN_PROGRESS && cancel_in_progress_op) {
|
|
|
+ ASSERTCMP(object->n_in_progress, >, 0);
|
|
|
+ if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
|
|
|
+ object->n_exclusive--;
|
|
|
+ object->n_in_progress--;
|
|
|
+ if (object->n_in_progress == 0)
|
|
|
+ fscache_start_operations(object);
|
|
|
+
|
|
|
+ fscache_stat(&fscache_n_op_cancelled);
|
|
|
+ op->cancel(op);
|
|
|
op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
|
|
|
object->n_exclusive--;
|
|
|
if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
|
|
|
wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
|
|
|
- fscache_put_operation(op);
|
|
|
ret = 0;
|
|
|
}
|
|
|
|
|
|
+ if (put)
|
|
|
+ fscache_put_operation(op);
|
|
|
spin_unlock(&object->lock);
|
|
|
_leave(" = %d", ret);
|
|
|
return ret;
|
|
@@ -345,6 +420,7 @@ void fscache_cancel_all_ops(struct fscache_object *object)
|
|
|
list_del_init(&op->pend_link);
|
|
|
|
|
|
ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING);
|
|
|
+ op->cancel(op);
|
|
|
op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
|
|
|
if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
|
|
@@ -377,8 +453,12 @@ void fscache_op_complete(struct fscache_operation *op, bool cancelled)
|
|
|
|
|
|
spin_lock(&object->lock);
|
|
|
|
|
|
- op->state = cancelled ?
|
|
|
- FSCACHE_OP_ST_CANCELLED : FSCACHE_OP_ST_COMPLETE;
|
|
|
+ if (!cancelled) {
|
|
|
+ op->state = FSCACHE_OP_ST_COMPLETE;
|
|
|
+ } else {
|
|
|
+ op->cancel(op);
|
|
|
+ op->state = FSCACHE_OP_ST_CANCELLED;
|
|
|
+ }
|
|
|
|
|
|
if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
|
|
|
object->n_exclusive--;
|
|
@@ -409,9 +489,9 @@ void fscache_put_operation(struct fscache_operation *op)
|
|
|
return;
|
|
|
|
|
|
_debug("PUT OP");
|
|
|
- ASSERTIFCMP(op->state != FSCACHE_OP_ST_COMPLETE,
|
|
|
+ ASSERTIFCMP(op->state != FSCACHE_OP_ST_INITIALISED &&
|
|
|
+ op->state != FSCACHE_OP_ST_COMPLETE,
|
|
|
op->state, ==, FSCACHE_OP_ST_CANCELLED);
|
|
|
- op->state = FSCACHE_OP_ST_DEAD;
|
|
|
|
|
|
fscache_stat(&fscache_n_op_release);
|
|
|
|
|
@@ -419,37 +499,39 @@ void fscache_put_operation(struct fscache_operation *op)
|
|
|
op->release(op);
|
|
|
op->release = NULL;
|
|
|
}
|
|
|
+ op->state = FSCACHE_OP_ST_DEAD;
|
|
|
|
|
|
object = op->object;
|
|
|
+ if (likely(object)) {
|
|
|
+ if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags))
|
|
|
+ atomic_dec(&object->n_reads);
|
|
|
+ if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags))
|
|
|
+ fscache_unuse_cookie(object);
|
|
|
+
|
|
|
+ /* now... we may get called with the object spinlock held, so we
|
|
|
+ * complete the cleanup here only if we can immediately acquire the
|
|
|
+ * lock, and defer it otherwise */
|
|
|
+ if (!spin_trylock(&object->lock)) {
|
|
|
+ _debug("defer put");
|
|
|
+ fscache_stat(&fscache_n_op_deferred_release);
|
|
|
+
|
|
|
+ cache = object->cache;
|
|
|
+ spin_lock(&cache->op_gc_list_lock);
|
|
|
+ list_add_tail(&op->pend_link, &cache->op_gc_list);
|
|
|
+ spin_unlock(&cache->op_gc_list_lock);
|
|
|
+ schedule_work(&cache->op_gc);
|
|
|
+ _leave(" [defer]");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags))
|
|
|
- atomic_dec(&object->n_reads);
|
|
|
- if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags))
|
|
|
- fscache_unuse_cookie(object);
|
|
|
-
|
|
|
- /* now... we may get called with the object spinlock held, so we
|
|
|
- * complete the cleanup here only if we can immediately acquire the
|
|
|
- * lock, and defer it otherwise */
|
|
|
- if (!spin_trylock(&object->lock)) {
|
|
|
- _debug("defer put");
|
|
|
- fscache_stat(&fscache_n_op_deferred_release);
|
|
|
+ ASSERTCMP(object->n_ops, >, 0);
|
|
|
+ object->n_ops--;
|
|
|
+ if (object->n_ops == 0)
|
|
|
+ fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED);
|
|
|
|
|
|
- cache = object->cache;
|
|
|
- spin_lock(&cache->op_gc_list_lock);
|
|
|
- list_add_tail(&op->pend_link, &cache->op_gc_list);
|
|
|
- spin_unlock(&cache->op_gc_list_lock);
|
|
|
- schedule_work(&cache->op_gc);
|
|
|
- _leave(" [defer]");
|
|
|
- return;
|
|
|
+ spin_unlock(&object->lock);
|
|
|
}
|
|
|
|
|
|
- ASSERTCMP(object->n_ops, >, 0);
|
|
|
- object->n_ops--;
|
|
|
- if (object->n_ops == 0)
|
|
|
- fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED);
|
|
|
-
|
|
|
- spin_unlock(&object->lock);
|
|
|
-
|
|
|
kfree(op);
|
|
|
_leave(" [done]");
|
|
|
}
|