Blame view

fs/fscache/operation.c 16.3 KB
952efe7b7   David Howells   FS-Cache: Add and...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /* FS-Cache worker operation management routines
   *
   * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   *
   * See Documentation/filesystems/caching/operations.txt
   */
  
  #define FSCACHE_DEBUG_LEVEL OPERATION
  #include <linux/module.h>
440f0affe   David Howells   FS-Cache: Annotat...
16
  #include <linux/seq_file.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
952efe7b7   David Howells   FS-Cache: Add and...
18
19
20
21
  #include "internal.h"
  
  atomic_t fscache_op_debug_id;
  EXPORT_SYMBOL(fscache_op_debug_id);
d3b97ca4a   David Howells   FS-Cache: The ope...
22
23
24
  static void fscache_operation_dummy_cancel(struct fscache_operation *op)
  {
  }
952efe7b7   David Howells   FS-Cache: Add and...
25
  /**
1339ec98e   David Howells   FS-Cache: Out of ...
26
27
28
29
30
31
32
33
34
   * 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,
d3b97ca4a   David Howells   FS-Cache: The ope...
35
  			    fscache_operation_cancel_t cancel,
1339ec98e   David Howells   FS-Cache: Out of ...
36
37
38
39
40
41
42
  			    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;
d3b97ca4a   David Howells   FS-Cache: The ope...
43
  	op->cancel = cancel ?: fscache_operation_dummy_cancel;
1339ec98e   David Howells   FS-Cache: Out of ...
44
45
  	op->release = release;
  	INIT_LIST_HEAD(&op->pend_link);
03cdd0e4b   David Howells   FS-Cache: Count t...
46
  	fscache_stat(&fscache_n_op_initialised);
1339ec98e   David Howells   FS-Cache: Out of ...
47
48
49
50
  }
  EXPORT_SYMBOL(fscache_operation_init);
  
  /**
952efe7b7   David Howells   FS-Cache: Add and...
51
52
53
54
55
56
57
58
59
60
61
   * fscache_enqueue_operation - Enqueue an operation for processing
   * @op: The operation to enqueue
   *
   * Enqueue an operation for processing by the FS-Cache thread pool.
   *
   * This will get its own ref on the object.
   */
  void fscache_enqueue_operation(struct fscache_operation *op)
  {
  	_enter("{OBJ%x OP%x,%u}",
  	       op->object->debug_id, op->debug_id, atomic_read(&op->usage));
5753c4418   David Howells   FS-Cache: Permit ...
62
  	ASSERT(list_empty(&op->pend_link));
952efe7b7   David Howells   FS-Cache: Add and...
63
  	ASSERT(op->processor != NULL);
493f7bc11   David Howells   FS-Cache: Wrap ch...
64
  	ASSERT(fscache_object_is_available(op->object));
952efe7b7   David Howells   FS-Cache: Add and...
65
  	ASSERTCMP(atomic_read(&op->usage), >, 0);
9f10523f8   David Howells   FS-Cache: Fix ope...
66
  	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_IN_PROGRESS);
952efe7b7   David Howells   FS-Cache: Add and...
67

5753c4418   David Howells   FS-Cache: Permit ...
68
69
  	fscache_stat(&fscache_n_op_enqueue);
  	switch (op->flags & FSCACHE_OP_TYPE) {
8af7c1243   Tejun Heo   fscache: convert ...
70
71
  	case FSCACHE_OP_ASYNC:
  		_debug("queue async");
5753c4418   David Howells   FS-Cache: Permit ...
72
  		atomic_inc(&op->usage);
8af7c1243   Tejun Heo   fscache: convert ...
73
  		if (!queue_work(fscache_op_wq, &op->work))
5753c4418   David Howells   FS-Cache: Permit ...
74
75
  			fscache_put_operation(op);
  		break;
5753c4418   David Howells   FS-Cache: Permit ...
76
77
78
79
  	case FSCACHE_OP_MYTHREAD:
  		_debug("queue for caller's attention");
  		break;
  	default:
36dfd116e   Fabian Frederick   fs/fscache: conve...
80
  		pr_err("Unexpected op type %lx", op->flags);
5753c4418   David Howells   FS-Cache: Permit ...
81
82
  		BUG();
  		break;
952efe7b7   David Howells   FS-Cache: Add and...
83
84
85
86
87
88
89
90
91
92
  	}
  }
  EXPORT_SYMBOL(fscache_enqueue_operation);
  
  /*
   * start an op running
   */
  static void fscache_run_op(struct fscache_object *object,
  			   struct fscache_operation *op)
  {
9f10523f8   David Howells   FS-Cache: Fix ope...
93
94
95
  	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING);
  
  	op->state = FSCACHE_OP_ST_IN_PROGRESS;
952efe7b7   David Howells   FS-Cache: Add and...
96
97
98
99
100
101
102
103
104
  	object->n_in_progress++;
  	if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
  		wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
  	if (op->processor)
  		fscache_enqueue_operation(op);
  	fscache_stat(&fscache_n_op_run);
  }
  
  /*
3c3059841   David Howells   FS-Cache: Move fs...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
   * 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();
  }
  
  /*
952efe7b7   David Howells   FS-Cache: Add and...
142
143
144
145
146
147
148
   * submit an exclusive operation for an object
   * - other ops are excluded from running simultaneously with this one
   * - this gets any extra refs it needs on an op
   */
  int fscache_submit_exclusive_op(struct fscache_object *object,
  				struct fscache_operation *op)
  {
30ceec628   David Howells   FS-Cache: When su...
149
150
  	const struct fscache_state *ostate;
  	unsigned long flags;
8d76349d3   David Howells   FS-Cache: Exclusi...
151
  	int ret;
952efe7b7   David Howells   FS-Cache: Add and...
152
  	_enter("{OBJ%x OP%x},", object->debug_id, op->debug_id);
9f10523f8   David Howells   FS-Cache: Fix ope...
153
154
  	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED);
  	ASSERTCMP(atomic_read(&op->usage), >, 0);
952efe7b7   David Howells   FS-Cache: Add and...
155
156
157
  	spin_lock(&object->lock);
  	ASSERTCMP(object->n_ops, >=, object->n_in_progress);
  	ASSERTCMP(object->n_ops, >=, object->n_exclusive);
5753c4418   David Howells   FS-Cache: Permit ...
158
  	ASSERT(list_empty(&op->pend_link));
952efe7b7   David Howells   FS-Cache: Add and...
159

30ceec628   David Howells   FS-Cache: When su...
160
161
  	ostate = object->state;
  	smp_rmb();
9f10523f8   David Howells   FS-Cache: Fix ope...
162
  	op->state = FSCACHE_OP_ST_PENDING;
30ceec628   David Howells   FS-Cache: When su...
163
164
165
  	flags = READ_ONCE(object->flags);
  	if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) {
  		fscache_stat(&fscache_n_op_rejected);
d3b97ca4a   David Howells   FS-Cache: The ope...
166
  		op->cancel(op);
30ceec628   David Howells   FS-Cache: When su...
167
168
169
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -ENOBUFS;
  	} else if (unlikely(fscache_cache_is_broken(object))) {
d3b97ca4a   David Howells   FS-Cache: The ope...
170
  		op->cancel(op);
30ceec628   David Howells   FS-Cache: When su...
171
172
173
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -EIO;
  	} else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) {
952efe7b7   David Howells   FS-Cache: Add and...
174
175
176
  		op->object = object;
  		object->n_ops++;
  		object->n_exclusive++;	/* reads and writes must wait */
9f10523f8   David Howells   FS-Cache: Fix ope...
177
  		if (object->n_in_progress > 0) {
952efe7b7   David Howells   FS-Cache: Add and...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  			atomic_inc(&op->usage);
  			list_add_tail(&op->pend_link, &object->pending_ops);
  			fscache_stat(&fscache_n_op_pend);
  		} else if (!list_empty(&object->pending_ops)) {
  			atomic_inc(&op->usage);
  			list_add_tail(&op->pend_link, &object->pending_ops);
  			fscache_stat(&fscache_n_op_pend);
  			fscache_start_operations(object);
  		} else {
  			ASSERTCMP(object->n_in_progress, ==, 0);
  			fscache_run_op(object, op);
  		}
  
  		/* need to issue a new write op after this */
  		clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);
8d76349d3   David Howells   FS-Cache: Exclusi...
193
  		ret = 0;
30ceec628   David Howells   FS-Cache: When su...
194
  	} else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) {
952efe7b7   David Howells   FS-Cache: Add and...
195
196
197
198
199
200
  		op->object = object;
  		object->n_ops++;
  		object->n_exclusive++;	/* reads and writes must wait */
  		atomic_inc(&op->usage);
  		list_add_tail(&op->pend_link, &object->pending_ops);
  		fscache_stat(&fscache_n_op_pend);
8d76349d3   David Howells   FS-Cache: Exclusi...
201
  		ret = 0;
6515d1dbf   David Howells   FS-Cache: Handle ...
202
  	} else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) {
d3b97ca4a   David Howells   FS-Cache: The ope...
203
  		op->cancel(op);
6515d1dbf   David Howells   FS-Cache: Handle ...
204
205
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -ENOBUFS;
952efe7b7   David Howells   FS-Cache: Add and...
206
  	} else {
30ceec628   David Howells   FS-Cache: When su...
207
  		fscache_report_unexpected_submission(object, op, ostate);
d3b97ca4a   David Howells   FS-Cache: The ope...
208
  		op->cancel(op);
30ceec628   David Howells   FS-Cache: When su...
209
210
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -ENOBUFS;
952efe7b7   David Howells   FS-Cache: Add and...
211
212
213
  	}
  
  	spin_unlock(&object->lock);
8d76349d3   David Howells   FS-Cache: Exclusi...
214
  	return ret;
952efe7b7   David Howells   FS-Cache: Add and...
215
216
217
  }
  
  /*
952efe7b7   David Howells   FS-Cache: Add and...
218
219
220
221
222
223
224
225
226
227
   * submit an operation for an object
   * - objects may be submitted only in the following states:
   *   - during object creation (write ops may be submitted)
   *   - whilst the object is active
   *   - after an I/O error incurred in one of the two above states (op rejected)
   * - this gets any extra refs it needs on an op
   */
  int fscache_submit_op(struct fscache_object *object,
  		      struct fscache_operation *op)
  {
caaef6900   David Howells   FS-Cache: Fix obj...
228
  	const struct fscache_state *ostate;
30ceec628   David Howells   FS-Cache: When su...
229
  	unsigned long flags;
952efe7b7   David Howells   FS-Cache: Add and...
230
231
232
233
  	int ret;
  
  	_enter("{OBJ%x OP%x},{%u}",
  	       object->debug_id, op->debug_id, atomic_read(&op->usage));
9f10523f8   David Howells   FS-Cache: Fix ope...
234
  	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED);
952efe7b7   David Howells   FS-Cache: Add and...
235
236
237
238
239
  	ASSERTCMP(atomic_read(&op->usage), >, 0);
  
  	spin_lock(&object->lock);
  	ASSERTCMP(object->n_ops, >=, object->n_in_progress);
  	ASSERTCMP(object->n_ops, >=, object->n_exclusive);
5753c4418   David Howells   FS-Cache: Permit ...
240
  	ASSERT(list_empty(&op->pend_link));
952efe7b7   David Howells   FS-Cache: Add and...
241
242
243
  
  	ostate = object->state;
  	smp_rmb();
9f10523f8   David Howells   FS-Cache: Fix ope...
244
  	op->state = FSCACHE_OP_ST_PENDING;
30ceec628   David Howells   FS-Cache: When su...
245
246
247
  	flags = READ_ONCE(object->flags);
  	if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) {
  		fscache_stat(&fscache_n_op_rejected);
d3b97ca4a   David Howells   FS-Cache: The ope...
248
  		op->cancel(op);
30ceec628   David Howells   FS-Cache: When su...
249
250
251
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -ENOBUFS;
  	} else if (unlikely(fscache_cache_is_broken(object))) {
d3b97ca4a   David Howells   FS-Cache: The ope...
252
  		op->cancel(op);
30ceec628   David Howells   FS-Cache: When su...
253
254
255
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -EIO;
  	} else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) {
952efe7b7   David Howells   FS-Cache: Add and...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  		op->object = object;
  		object->n_ops++;
  
  		if (object->n_exclusive > 0) {
  			atomic_inc(&op->usage);
  			list_add_tail(&op->pend_link, &object->pending_ops);
  			fscache_stat(&fscache_n_op_pend);
  		} else if (!list_empty(&object->pending_ops)) {
  			atomic_inc(&op->usage);
  			list_add_tail(&op->pend_link, &object->pending_ops);
  			fscache_stat(&fscache_n_op_pend);
  			fscache_start_operations(object);
  		} else {
  			ASSERTCMP(object->n_exclusive, ==, 0);
  			fscache_run_op(object, op);
  		}
  		ret = 0;
30ceec628   David Howells   FS-Cache: When su...
273
  	} else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) {
952efe7b7   David Howells   FS-Cache: Add and...
274
275
276
277
278
279
  		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;
6515d1dbf   David Howells   FS-Cache: Handle ...
280
  	} else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) {
d3b97ca4a   David Howells   FS-Cache: The ope...
281
  		op->cancel(op);
6515d1dbf   David Howells   FS-Cache: Handle ...
282
283
  		op->state = FSCACHE_OP_ST_CANCELLED;
  		ret = -ENOBUFS;
30ceec628   David Howells   FS-Cache: When su...
284
  	} else {
952efe7b7   David Howells   FS-Cache: Add and...
285
286
  		fscache_report_unexpected_submission(object, op, ostate);
  		ASSERT(!fscache_object_is_active(object));
d3b97ca4a   David Howells   FS-Cache: The ope...
287
  		op->cancel(op);
9f10523f8   David Howells   FS-Cache: Fix ope...
288
  		op->state = FSCACHE_OP_ST_CANCELLED;
952efe7b7   David Howells   FS-Cache: Add and...
289
  		ret = -ENOBUFS;
952efe7b7   David Howells   FS-Cache: Add and...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  	}
  
  	spin_unlock(&object->lock);
  	return ret;
  }
  
  /*
   * queue an object for withdrawal on error, aborting all following asynchronous
   * operations
   */
  void fscache_abort_object(struct fscache_object *object)
  {
  	_enter("{OBJ%x}", object->debug_id);
  
  	fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR);
  }
  
  /*
dcfae32f8   David Howells   FS-Cache: Don't u...
308
309
   * Jump start the operation processing on an object.  The caller must hold
   * object->lock.
952efe7b7   David Howells   FS-Cache: Add and...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
   */
  void fscache_start_operations(struct fscache_object *object)
  {
  	struct fscache_operation *op;
  	bool stop = false;
  
  	while (!list_empty(&object->pending_ops) && !stop) {
  		op = list_entry(object->pending_ops.next,
  				struct fscache_operation, pend_link);
  
  		if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) {
  			if (object->n_in_progress > 0)
  				break;
  			stop = true;
  		}
  		list_del_init(&op->pend_link);
5753c4418   David Howells   FS-Cache: Permit ...
326
  		fscache_run_op(object, op);
952efe7b7   David Howells   FS-Cache: Add and...
327
328
329
330
331
332
333
334
335
336
337
338
  
  		/* the pending queue was holding a ref on the object */
  		fscache_put_operation(op);
  	}
  
  	ASSERTCMP(object->n_in_progress, <=, object->n_ops);
  
  	_debug("woke %d ops on OBJ%x",
  	       object->n_in_progress, object->debug_id);
  }
  
  /*
5753c4418   David Howells   FS-Cache: Permit ...
339
340
   * cancel an operation that's pending on an object
   */
91c7fbbf6   David Howells   FS-Cache: Clear r...
341
  int fscache_cancel_op(struct fscache_operation *op,
418b7eb9e   David Howells   FS-Cache: Permit ...
342
  		      bool cancel_in_progress_op)
5753c4418   David Howells   FS-Cache: Permit ...
343
344
  {
  	struct fscache_object *object = op->object;
418b7eb9e   David Howells   FS-Cache: Permit ...
345
  	bool put = false;
5753c4418   David Howells   FS-Cache: Permit ...
346
347
348
  	int ret;
  
  	_enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id);
9f10523f8   David Howells   FS-Cache: Fix ope...
349
350
351
  	ASSERTCMP(op->state, >=, FSCACHE_OP_ST_PENDING);
  	ASSERTCMP(op->state, !=, FSCACHE_OP_ST_CANCELLED);
  	ASSERTCMP(atomic_read(&op->usage), >, 0);
5753c4418   David Howells   FS-Cache: Permit ...
352
353
354
  	spin_lock(&object->lock);
  
  	ret = -EBUSY;
9f10523f8   David Howells   FS-Cache: Fix ope...
355
356
  	if (op->state == FSCACHE_OP_ST_PENDING) {
  		ASSERT(!list_empty(&op->pend_link));
5753c4418   David Howells   FS-Cache: Permit ...
357
  		list_del_init(&op->pend_link);
418b7eb9e   David Howells   FS-Cache: Permit ...
358
  		put = true;
d3b97ca4a   David Howells   FS-Cache: The ope...
359

418b7eb9e   David Howells   FS-Cache: Permit ...
360
  		fscache_stat(&fscache_n_op_cancelled);
d3b97ca4a   David Howells   FS-Cache: The ope...
361
  		op->cancel(op);
418b7eb9e   David Howells   FS-Cache: Permit ...
362
363
364
365
366
367
368
  		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) {
73c04a47b   David Howells   FS-Cache: Fix can...
369
370
371
372
373
374
  		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);
418b7eb9e   David Howells   FS-Cache: Permit ...
375
  		fscache_stat(&fscache_n_op_cancelled);
d3b97ca4a   David Howells   FS-Cache: The ope...
376
  		op->cancel(op);
9f10523f8   David Howells   FS-Cache: Fix ope...
377
  		op->state = FSCACHE_OP_ST_CANCELLED;
5753c4418   David Howells   FS-Cache: Permit ...
378
379
380
381
  		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);
5753c4418   David Howells   FS-Cache: Permit ...
382
383
  		ret = 0;
  	}
418b7eb9e   David Howells   FS-Cache: Permit ...
384
385
  	if (put)
  		fscache_put_operation(op);
5753c4418   David Howells   FS-Cache: Permit ...
386
387
388
389
390
391
  	spin_unlock(&object->lock);
  	_leave(" = %d", ret);
  	return ret;
  }
  
  /*
ef778e7ae   David Howells   FS-Cache: Provide...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
   * Cancel all pending operations on an object
   */
  void fscache_cancel_all_ops(struct fscache_object *object)
  {
  	struct fscache_operation *op;
  
  	_enter("OBJ%x", object->debug_id);
  
  	spin_lock(&object->lock);
  
  	while (!list_empty(&object->pending_ops)) {
  		op = list_entry(object->pending_ops.next,
  				struct fscache_operation, pend_link);
  		fscache_stat(&fscache_n_op_cancelled);
  		list_del_init(&op->pend_link);
  
  		ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING);
d3b97ca4a   David Howells   FS-Cache: The ope...
409
  		op->cancel(op);
ef778e7ae   David Howells   FS-Cache: Provide...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  		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);
  		cond_resched_lock(&object->lock);
  	}
  
  	spin_unlock(&object->lock);
  	_leave("");
  }
  
  /*
1f372dff1   David Howells   FS-Cache: Mark ca...
425
   * Record the completion or cancellation of an in-progress operation.
9f10523f8   David Howells   FS-Cache: Fix ope...
426
   */
1f372dff1   David Howells   FS-Cache: Mark ca...
427
  void fscache_op_complete(struct fscache_operation *op, bool cancelled)
9f10523f8   David Howells   FS-Cache: Fix ope...
428
429
430
431
432
433
434
435
436
437
438
439
440
  {
  	struct fscache_object *object = op->object;
  
  	_enter("OBJ%x", object->debug_id);
  
  	ASSERTCMP(op->state, ==, FSCACHE_OP_ST_IN_PROGRESS);
  	ASSERTCMP(object->n_in_progress, >, 0);
  	ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags),
  		    object->n_exclusive, >, 0);
  	ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags),
  		    object->n_in_progress, ==, 1);
  
  	spin_lock(&object->lock);
d3b97ca4a   David Howells   FS-Cache: The ope...
441
442
443
444
445
446
  	if (!cancelled) {
  		op->state = FSCACHE_OP_ST_COMPLETE;
  	} else {
  		op->cancel(op);
  		op->state = FSCACHE_OP_ST_CANCELLED;
  	}
9f10523f8   David Howells   FS-Cache: Fix ope...
447
448
449
450
451
452
453
454
455
456
457
458
459
  
  	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);
  
  	spin_unlock(&object->lock);
  	_leave("");
  }
  EXPORT_SYMBOL(fscache_op_complete);
  
  /*
952efe7b7   David Howells   FS-Cache: Add and...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
   * release an operation
   * - queues pending ops if this is the last in-progress op
   */
  void fscache_put_operation(struct fscache_operation *op)
  {
  	struct fscache_object *object;
  	struct fscache_cache *cache;
  
  	_enter("{OBJ%x OP%x,%d}",
  	       op->object->debug_id, op->debug_id, atomic_read(&op->usage));
  
  	ASSERTCMP(atomic_read(&op->usage), >, 0);
  
  	if (!atomic_dec_and_test(&op->usage))
  		return;
  
  	_debug("PUT OP");
a39caadf0   David Howells   FS-Cache: Put an ...
477
478
  	ASSERTIFCMP(op->state != FSCACHE_OP_ST_INITIALISED &&
  		    op->state != FSCACHE_OP_ST_COMPLETE,
9f10523f8   David Howells   FS-Cache: Fix ope...
479
  		    op->state, ==, FSCACHE_OP_ST_CANCELLED);
952efe7b7   David Howells   FS-Cache: Add and...
480
481
482
483
484
485
486
  
  	fscache_stat(&fscache_n_op_release);
  
  	if (op->release) {
  		op->release(op);
  		op->release = NULL;
  	}
4a47132ff   David Howells   FS-Cache: Retain ...
487
  	op->state = FSCACHE_OP_ST_DEAD;
952efe7b7   David Howells   FS-Cache: Add and...
488
489
  
  	object = op->object;
a39caadf0   David Howells   FS-Cache: Put an ...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
  	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;
  		}
952efe7b7   David Howells   FS-Cache: Add and...
511

a39caadf0   David Howells   FS-Cache: Put an ...
512
513
514
515
  		ASSERTCMP(object->n_ops, >, 0);
  		object->n_ops--;
  		if (object->n_ops == 0)
  			fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED);
952efe7b7   David Howells   FS-Cache: Add and...
516

a39caadf0   David Howells   FS-Cache: Put an ...
517
  		spin_unlock(&object->lock);
952efe7b7   David Howells   FS-Cache: Add and...
518
  	}
952efe7b7   David Howells   FS-Cache: Add and...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
  	kfree(op);
  	_leave(" [done]");
  }
  EXPORT_SYMBOL(fscache_put_operation);
  
  /*
   * garbage collect operations that have had their release deferred
   */
  void fscache_operation_gc(struct work_struct *work)
  {
  	struct fscache_operation *op;
  	struct fscache_object *object;
  	struct fscache_cache *cache =
  		container_of(work, struct fscache_cache, op_gc);
  	int count = 0;
  
  	_enter("");
  
  	do {
  		spin_lock(&cache->op_gc_list_lock);
  		if (list_empty(&cache->op_gc_list)) {
  			spin_unlock(&cache->op_gc_list_lock);
  			break;
  		}
  
  		op = list_entry(cache->op_gc_list.next,
  				struct fscache_operation, pend_link);
  		list_del(&op->pend_link);
  		spin_unlock(&cache->op_gc_list_lock);
  
  		object = op->object;
9f10523f8   David Howells   FS-Cache: Fix ope...
550
  		spin_lock(&object->lock);
952efe7b7   David Howells   FS-Cache: Add and...
551
552
553
554
555
556
  
  		_debug("GC DEFERRED REL OBJ%x OP%x",
  		       object->debug_id, op->debug_id);
  		fscache_stat(&fscache_n_op_gc);
  
  		ASSERTCMP(atomic_read(&op->usage), ==, 0);
9f10523f8   David Howells   FS-Cache: Fix ope...
557
  		ASSERTCMP(op->state, ==, FSCACHE_OP_ST_DEAD);
952efe7b7   David Howells   FS-Cache: Add and...
558
559
560
561
562
563
564
  
  		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);
9f10523f8   David Howells   FS-Cache: Fix ope...
565
  		kfree(op);
952efe7b7   David Howells   FS-Cache: Add and...
566
567
568
569
570
571
572
573
574
575
  
  	} while (count++ < 20);
  
  	if (!list_empty(&cache->op_gc_list))
  		schedule_work(&cache->op_gc);
  
  	_leave("");
  }
  
  /*
8af7c1243   Tejun Heo   fscache: convert ...
576
577
   * execute an operation using fs_op_wq to provide processing context -
   * the caller holds a ref to this object, so we don't need to hold one
952efe7b7   David Howells   FS-Cache: Add and...
578
   */
8af7c1243   Tejun Heo   fscache: convert ...
579
  void fscache_op_work_func(struct work_struct *work)
952efe7b7   David Howells   FS-Cache: Add and...
580
581
  {
  	struct fscache_operation *op =
8af7c1243   Tejun Heo   fscache: convert ...
582
  		container_of(work, struct fscache_operation, work);
952efe7b7   David Howells   FS-Cache: Add and...
583
584
585
586
587
588
589
590
591
  	unsigned long start;
  
  	_enter("{OBJ%x OP%x,%d}",
  	       op->object->debug_id, op->debug_id, atomic_read(&op->usage));
  
  	ASSERT(op->processor != NULL);
  	start = jiffies;
  	op->processor(op);
  	fscache_hist(fscache_ops_histogram, start);
8af7c1243   Tejun Heo   fscache: convert ...
592
  	fscache_put_operation(op);
952efe7b7   David Howells   FS-Cache: Add and...
593
594
595
  
  	_leave("");
  }