Merge tag 'for-f2fs-v4.8-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeu...
[cascardo/linux.git] / fs / fscache / operation.c
index 9761df4..de67745 100644 (file)
 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
@@ -30,6 +34,7 @@ EXPORT_SYMBOL(fscache_op_debug_id);
  */
 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);
@@ -37,6 +42,7 @@ void fscache_operation_init(struct fscache_operation *op,
        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);
@@ -164,9 +170,11 @@ int fscache_submit_exclusive_op(struct fscache_object *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)) {
@@ -200,10 +208,12 @@ int fscache_submit_exclusive_op(struct fscache_object *object,
                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 {
                fscache_report_unexpected_submission(object, op, ostate);
+               op->cancel(op);
                op->state = FSCACHE_OP_ST_CANCELLED;
                ret = -ENOBUFS;
        }
@@ -245,9 +255,11 @@ int fscache_submit_op(struct fscache_object *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)) {
@@ -276,11 +288,13 @@ int fscache_submit_op(struct fscache_object *object,
                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 {
                fscache_report_unexpected_submission(object, op, ostate);
                ASSERT(!fscache_object_is_active(object));
+               op->cancel(op);
                op->state = FSCACHE_OP_ST_CANCELLED;
                ret = -ENOBUFS;
        }
@@ -335,7 +349,6 @@ 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;
@@ -355,9 +368,9 @@ int fscache_cancel_op(struct fscache_operation *op,
                ASSERT(!list_empty(&op->pend_link));
                list_del_init(&op->pend_link);
                put = true;
+
                fscache_stat(&fscache_n_op_cancelled);
-               if (do_cancel)
-                       do_cancel(op);
+               op->cancel(op);
                op->state = FSCACHE_OP_ST_CANCELLED;
                if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
                        object->n_exclusive--;
@@ -365,9 +378,15 @@ int fscache_cancel_op(struct fscache_operation *op,
                        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);
-               if (do_cancel)
-                       do_cancel(op);
+               op->cancel(op);
                op->state = FSCACHE_OP_ST_CANCELLED;
                if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
                        object->n_exclusive--;
@@ -401,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))
@@ -433,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--;
@@ -465,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);
 
@@ -475,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]");
 }