]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
952efe7b DH |
2 | /* FS-Cache worker operation management routines |
3 | * | |
4 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells ([email protected]) | |
6 | * | |
09eac7c5 | 7 | * See Documentation/filesystems/caching/operations.rst |
952efe7b DH |
8 | */ |
9 | ||
10 | #define FSCACHE_DEBUG_LEVEL OPERATION | |
11 | #include <linux/module.h> | |
440f0aff | 12 | #include <linux/seq_file.h> |
5a0e3ad6 | 13 | #include <linux/slab.h> |
952efe7b DH |
14 | #include "internal.h" |
15 | ||
16 | atomic_t fscache_op_debug_id; | |
17 | EXPORT_SYMBOL(fscache_op_debug_id); | |
18 | ||
d3b97ca4 DH |
19 | static void fscache_operation_dummy_cancel(struct fscache_operation *op) |
20 | { | |
21 | } | |
22 | ||
1339ec98 DH |
23 | /** |
24 | * fscache_operation_init - Do basic initialisation of an operation | |
25 | * @op: The operation to initialise | |
26 | * @release: The release function to assign | |
27 | * | |
28 | * Do basic initialisation of an operation. The caller must still set flags, | |
29 | * object and processor if needed. | |
30 | */ | |
08c2e3d0 DH |
31 | void fscache_operation_init(struct fscache_cookie *cookie, |
32 | struct fscache_operation *op, | |
1339ec98 | 33 | fscache_operation_processor_t processor, |
d3b97ca4 | 34 | fscache_operation_cancel_t cancel, |
1339ec98 DH |
35 | fscache_operation_release_t release) |
36 | { | |
37 | INIT_WORK(&op->work, fscache_op_work_func); | |
38 | atomic_set(&op->usage, 1); | |
39 | op->state = FSCACHE_OP_ST_INITIALISED; | |
40 | op->debug_id = atomic_inc_return(&fscache_op_debug_id); | |
41 | op->processor = processor; | |
d3b97ca4 | 42 | op->cancel = cancel ?: fscache_operation_dummy_cancel; |
1339ec98 DH |
43 | op->release = release; |
44 | INIT_LIST_HEAD(&op->pend_link); | |
03cdd0e4 | 45 | fscache_stat(&fscache_n_op_initialised); |
08c2e3d0 | 46 | trace_fscache_op(cookie, op, fscache_op_init); |
1339ec98 DH |
47 | } |
48 | EXPORT_SYMBOL(fscache_operation_init); | |
49 | ||
952efe7b DH |
50 | /** |
51 | * fscache_enqueue_operation - Enqueue an operation for processing | |
52 | * @op: The operation to enqueue | |
53 | * | |
54 | * Enqueue an operation for processing by the FS-Cache thread pool. | |
55 | * | |
56 | * This will get its own ref on the object. | |
57 | */ | |
58 | void fscache_enqueue_operation(struct fscache_operation *op) | |
59 | { | |
08c2e3d0 DH |
60 | struct fscache_cookie *cookie = op->object->cookie; |
61 | ||
952efe7b DH |
62 | _enter("{OBJ%x OP%x,%u}", |
63 | op->object->debug_id, op->debug_id, atomic_read(&op->usage)); | |
64 | ||
5753c441 | 65 | ASSERT(list_empty(&op->pend_link)); |
952efe7b | 66 | ASSERT(op->processor != NULL); |
493f7bc1 | 67 | ASSERT(fscache_object_is_available(op->object)); |
952efe7b | 68 | ASSERTCMP(atomic_read(&op->usage), >, 0); |
d0eb06af KKM |
69 | ASSERTIFCMP(op->state != FSCACHE_OP_ST_IN_PROGRESS, |
70 | op->state, ==, FSCACHE_OP_ST_CANCELLED); | |
952efe7b | 71 | |
5753c441 DH |
72 | fscache_stat(&fscache_n_op_enqueue); |
73 | switch (op->flags & FSCACHE_OP_TYPE) { | |
8af7c124 | 74 | case FSCACHE_OP_ASYNC: |
08c2e3d0 | 75 | trace_fscache_op(cookie, op, fscache_op_enqueue_async); |
8af7c124 | 76 | _debug("queue async"); |
5753c441 | 77 | atomic_inc(&op->usage); |
8af7c124 | 78 | if (!queue_work(fscache_op_wq, &op->work)) |
5753c441 DH |
79 | fscache_put_operation(op); |
80 | break; | |
5753c441 | 81 | case FSCACHE_OP_MYTHREAD: |
08c2e3d0 | 82 | trace_fscache_op(cookie, op, fscache_op_enqueue_mythread); |
5753c441 DH |
83 | _debug("queue for caller's attention"); |
84 | break; | |
85 | default: | |
36dfd116 | 86 | pr_err("Unexpected op type %lx", op->flags); |
5753c441 DH |
87 | BUG(); |
88 | break; | |
952efe7b DH |
89 | } |
90 | } | |
91 | EXPORT_SYMBOL(fscache_enqueue_operation); | |
92 | ||
93 | /* | |
94 | * start an op running | |
95 | */ | |
96 | static void fscache_run_op(struct fscache_object *object, | |
97 | struct fscache_operation *op) | |
98 | { | |
9f10523f DH |
99 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING); |
100 | ||
101 | op->state = FSCACHE_OP_ST_IN_PROGRESS; | |
952efe7b DH |
102 | object->n_in_progress++; |
103 | if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) | |
104 | wake_up_bit(&op->flags, FSCACHE_OP_WAITING); | |
105 | if (op->processor) | |
106 | fscache_enqueue_operation(op); | |
08c2e3d0 DH |
107 | else |
108 | trace_fscache_op(object->cookie, op, fscache_op_run); | |
952efe7b DH |
109 | fscache_stat(&fscache_n_op_run); |
110 | } | |
111 | ||
3c305984 DH |
112 | /* |
113 | * report an unexpected submission | |
114 | */ | |
115 | static void fscache_report_unexpected_submission(struct fscache_object *object, | |
116 | struct fscache_operation *op, | |
117 | const struct fscache_state *ostate) | |
118 | { | |
119 | static bool once_only; | |
120 | struct fscache_operation *p; | |
121 | unsigned n; | |
122 | ||
123 | if (once_only) | |
124 | return; | |
125 | once_only = true; | |
126 | ||
127 | kdebug("unexpected submission OP%x [OBJ%x %s]", | |
128 | op->debug_id, object->debug_id, object->state->name); | |
129 | kdebug("objstate=%s [%s]", object->state->name, ostate->name); | |
130 | kdebug("objflags=%lx", object->flags); | |
131 | kdebug("objevent=%lx [%lx]", object->events, object->event_mask); | |
132 | kdebug("ops=%u inp=%u exc=%u", | |
133 | object->n_ops, object->n_in_progress, object->n_exclusive); | |
134 | ||
135 | if (!list_empty(&object->pending_ops)) { | |
136 | n = 0; | |
137 | list_for_each_entry(p, &object->pending_ops, pend_link) { | |
138 | ASSERTCMP(p->object, ==, object); | |
139 | kdebug("%p %p", op->processor, op->release); | |
140 | n++; | |
141 | } | |
142 | ||
143 | kdebug("n=%u", n); | |
144 | } | |
145 | ||
146 | dump_stack(); | |
147 | } | |
148 | ||
952efe7b DH |
149 | /* |
150 | * submit an exclusive operation for an object | |
151 | * - other ops are excluded from running simultaneously with this one | |
152 | * - this gets any extra refs it needs on an op | |
153 | */ | |
154 | int fscache_submit_exclusive_op(struct fscache_object *object, | |
155 | struct fscache_operation *op) | |
156 | { | |
30ceec62 DH |
157 | const struct fscache_state *ostate; |
158 | unsigned long flags; | |
8d76349d DH |
159 | int ret; |
160 | ||
952efe7b DH |
161 | _enter("{OBJ%x OP%x},", object->debug_id, op->debug_id); |
162 | ||
08c2e3d0 DH |
163 | trace_fscache_op(object->cookie, op, fscache_op_submit_ex); |
164 | ||
9f10523f DH |
165 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED); |
166 | ASSERTCMP(atomic_read(&op->usage), >, 0); | |
167 | ||
952efe7b DH |
168 | spin_lock(&object->lock); |
169 | ASSERTCMP(object->n_ops, >=, object->n_in_progress); | |
170 | ASSERTCMP(object->n_ops, >=, object->n_exclusive); | |
5753c441 | 171 | ASSERT(list_empty(&op->pend_link)); |
952efe7b | 172 | |
30ceec62 DH |
173 | ostate = object->state; |
174 | smp_rmb(); | |
175 | ||
9f10523f | 176 | op->state = FSCACHE_OP_ST_PENDING; |
30ceec62 DH |
177 | flags = READ_ONCE(object->flags); |
178 | if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) { | |
179 | fscache_stat(&fscache_n_op_rejected); | |
d3b97ca4 | 180 | op->cancel(op); |
30ceec62 DH |
181 | op->state = FSCACHE_OP_ST_CANCELLED; |
182 | ret = -ENOBUFS; | |
183 | } else if (unlikely(fscache_cache_is_broken(object))) { | |
d3b97ca4 | 184 | op->cancel(op); |
30ceec62 DH |
185 | op->state = FSCACHE_OP_ST_CANCELLED; |
186 | ret = -EIO; | |
187 | } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) { | |
952efe7b DH |
188 | op->object = object; |
189 | object->n_ops++; | |
190 | object->n_exclusive++; /* reads and writes must wait */ | |
191 | ||
9f10523f | 192 | if (object->n_in_progress > 0) { |
952efe7b DH |
193 | atomic_inc(&op->usage); |
194 | list_add_tail(&op->pend_link, &object->pending_ops); | |
195 | fscache_stat(&fscache_n_op_pend); | |
196 | } else if (!list_empty(&object->pending_ops)) { | |
197 | atomic_inc(&op->usage); | |
198 | list_add_tail(&op->pend_link, &object->pending_ops); | |
199 | fscache_stat(&fscache_n_op_pend); | |
200 | fscache_start_operations(object); | |
201 | } else { | |
202 | ASSERTCMP(object->n_in_progress, ==, 0); | |
203 | fscache_run_op(object, op); | |
204 | } | |
205 | ||
206 | /* need to issue a new write op after this */ | |
207 | clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); | |
8d76349d | 208 | ret = 0; |
30ceec62 | 209 | } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) { |
952efe7b DH |
210 | op->object = object; |
211 | object->n_ops++; | |
212 | object->n_exclusive++; /* reads and writes must wait */ | |
213 | atomic_inc(&op->usage); | |
214 | list_add_tail(&op->pend_link, &object->pending_ops); | |
215 | fscache_stat(&fscache_n_op_pend); | |
8d76349d | 216 | ret = 0; |
6515d1db | 217 | } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) { |
d3b97ca4 | 218 | op->cancel(op); |
6515d1db DH |
219 | op->state = FSCACHE_OP_ST_CANCELLED; |
220 | ret = -ENOBUFS; | |
952efe7b | 221 | } else { |
30ceec62 | 222 | fscache_report_unexpected_submission(object, op, ostate); |
d3b97ca4 | 223 | op->cancel(op); |
30ceec62 DH |
224 | op->state = FSCACHE_OP_ST_CANCELLED; |
225 | ret = -ENOBUFS; | |
952efe7b DH |
226 | } |
227 | ||
228 | spin_unlock(&object->lock); | |
8d76349d | 229 | return ret; |
952efe7b DH |
230 | } |
231 | ||
952efe7b DH |
232 | /* |
233 | * submit an operation for an object | |
234 | * - objects may be submitted only in the following states: | |
235 | * - during object creation (write ops may be submitted) | |
236 | * - whilst the object is active | |
237 | * - after an I/O error incurred in one of the two above states (op rejected) | |
238 | * - this gets any extra refs it needs on an op | |
239 | */ | |
240 | int fscache_submit_op(struct fscache_object *object, | |
241 | struct fscache_operation *op) | |
242 | { | |
caaef690 | 243 | const struct fscache_state *ostate; |
30ceec62 | 244 | unsigned long flags; |
952efe7b DH |
245 | int ret; |
246 | ||
247 | _enter("{OBJ%x OP%x},{%u}", | |
248 | object->debug_id, op->debug_id, atomic_read(&op->usage)); | |
249 | ||
08c2e3d0 DH |
250 | trace_fscache_op(object->cookie, op, fscache_op_submit); |
251 | ||
9f10523f | 252 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_INITIALISED); |
952efe7b DH |
253 | ASSERTCMP(atomic_read(&op->usage), >, 0); |
254 | ||
255 | spin_lock(&object->lock); | |
256 | ASSERTCMP(object->n_ops, >=, object->n_in_progress); | |
257 | ASSERTCMP(object->n_ops, >=, object->n_exclusive); | |
5753c441 | 258 | ASSERT(list_empty(&op->pend_link)); |
952efe7b DH |
259 | |
260 | ostate = object->state; | |
261 | smp_rmb(); | |
262 | ||
9f10523f | 263 | op->state = FSCACHE_OP_ST_PENDING; |
30ceec62 DH |
264 | flags = READ_ONCE(object->flags); |
265 | if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) { | |
266 | fscache_stat(&fscache_n_op_rejected); | |
d3b97ca4 | 267 | op->cancel(op); |
30ceec62 DH |
268 | op->state = FSCACHE_OP_ST_CANCELLED; |
269 | ret = -ENOBUFS; | |
270 | } else if (unlikely(fscache_cache_is_broken(object))) { | |
d3b97ca4 | 271 | op->cancel(op); |
30ceec62 DH |
272 | op->state = FSCACHE_OP_ST_CANCELLED; |
273 | ret = -EIO; | |
274 | } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) { | |
952efe7b DH |
275 | op->object = object; |
276 | object->n_ops++; | |
277 | ||
278 | if (object->n_exclusive > 0) { | |
279 | atomic_inc(&op->usage); | |
280 | list_add_tail(&op->pend_link, &object->pending_ops); | |
281 | fscache_stat(&fscache_n_op_pend); | |
282 | } else if (!list_empty(&object->pending_ops)) { | |
283 | atomic_inc(&op->usage); | |
284 | list_add_tail(&op->pend_link, &object->pending_ops); | |
285 | fscache_stat(&fscache_n_op_pend); | |
286 | fscache_start_operations(object); | |
287 | } else { | |
288 | ASSERTCMP(object->n_exclusive, ==, 0); | |
289 | fscache_run_op(object, op); | |
290 | } | |
291 | ret = 0; | |
30ceec62 | 292 | } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) { |
952efe7b DH |
293 | op->object = object; |
294 | object->n_ops++; | |
295 | atomic_inc(&op->usage); | |
296 | list_add_tail(&op->pend_link, &object->pending_ops); | |
297 | fscache_stat(&fscache_n_op_pend); | |
298 | ret = 0; | |
6515d1db | 299 | } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) { |
d3b97ca4 | 300 | op->cancel(op); |
6515d1db DH |
301 | op->state = FSCACHE_OP_ST_CANCELLED; |
302 | ret = -ENOBUFS; | |
30ceec62 | 303 | } else { |
952efe7b DH |
304 | fscache_report_unexpected_submission(object, op, ostate); |
305 | ASSERT(!fscache_object_is_active(object)); | |
d3b97ca4 | 306 | op->cancel(op); |
9f10523f | 307 | op->state = FSCACHE_OP_ST_CANCELLED; |
952efe7b | 308 | ret = -ENOBUFS; |
952efe7b DH |
309 | } |
310 | ||
311 | spin_unlock(&object->lock); | |
312 | return ret; | |
313 | } | |
314 | ||
315 | /* | |
316 | * queue an object for withdrawal on error, aborting all following asynchronous | |
317 | * operations | |
318 | */ | |
319 | void fscache_abort_object(struct fscache_object *object) | |
320 | { | |
321 | _enter("{OBJ%x}", object->debug_id); | |
322 | ||
323 | fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); | |
324 | } | |
325 | ||
326 | /* | |
dcfae32f DH |
327 | * Jump start the operation processing on an object. The caller must hold |
328 | * object->lock. | |
952efe7b DH |
329 | */ |
330 | void fscache_start_operations(struct fscache_object *object) | |
331 | { | |
332 | struct fscache_operation *op; | |
333 | bool stop = false; | |
334 | ||
335 | while (!list_empty(&object->pending_ops) && !stop) { | |
336 | op = list_entry(object->pending_ops.next, | |
337 | struct fscache_operation, pend_link); | |
338 | ||
339 | if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) { | |
340 | if (object->n_in_progress > 0) | |
341 | break; | |
342 | stop = true; | |
343 | } | |
344 | list_del_init(&op->pend_link); | |
5753c441 | 345 | fscache_run_op(object, op); |
952efe7b DH |
346 | |
347 | /* the pending queue was holding a ref on the object */ | |
348 | fscache_put_operation(op); | |
349 | } | |
350 | ||
351 | ASSERTCMP(object->n_in_progress, <=, object->n_ops); | |
352 | ||
353 | _debug("woke %d ops on OBJ%x", | |
354 | object->n_in_progress, object->debug_id); | |
355 | } | |
356 | ||
5753c441 DH |
357 | /* |
358 | * cancel an operation that's pending on an object | |
359 | */ | |
91c7fbbf | 360 | int fscache_cancel_op(struct fscache_operation *op, |
418b7eb9 | 361 | bool cancel_in_progress_op) |
5753c441 DH |
362 | { |
363 | struct fscache_object *object = op->object; | |
418b7eb9 | 364 | bool put = false; |
5753c441 DH |
365 | int ret; |
366 | ||
367 | _enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id); | |
368 | ||
08c2e3d0 DH |
369 | trace_fscache_op(object->cookie, op, fscache_op_cancel); |
370 | ||
9f10523f DH |
371 | ASSERTCMP(op->state, >=, FSCACHE_OP_ST_PENDING); |
372 | ASSERTCMP(op->state, !=, FSCACHE_OP_ST_CANCELLED); | |
373 | ASSERTCMP(atomic_read(&op->usage), >, 0); | |
374 | ||
5753c441 DH |
375 | spin_lock(&object->lock); |
376 | ||
377 | ret = -EBUSY; | |
9f10523f DH |
378 | if (op->state == FSCACHE_OP_ST_PENDING) { |
379 | ASSERT(!list_empty(&op->pend_link)); | |
5753c441 | 380 | list_del_init(&op->pend_link); |
418b7eb9 | 381 | put = true; |
d3b97ca4 | 382 | |
418b7eb9 | 383 | fscache_stat(&fscache_n_op_cancelled); |
d3b97ca4 | 384 | op->cancel(op); |
418b7eb9 DH |
385 | op->state = FSCACHE_OP_ST_CANCELLED; |
386 | if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) | |
387 | object->n_exclusive--; | |
388 | if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) | |
389 | wake_up_bit(&op->flags, FSCACHE_OP_WAITING); | |
390 | ret = 0; | |
391 | } else if (op->state == FSCACHE_OP_ST_IN_PROGRESS && cancel_in_progress_op) { | |
73c04a47 DH |
392 | ASSERTCMP(object->n_in_progress, >, 0); |
393 | if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) | |
394 | object->n_exclusive--; | |
395 | object->n_in_progress--; | |
396 | if (object->n_in_progress == 0) | |
397 | fscache_start_operations(object); | |
398 | ||
418b7eb9 | 399 | fscache_stat(&fscache_n_op_cancelled); |
d3b97ca4 | 400 | op->cancel(op); |
9f10523f | 401 | op->state = FSCACHE_OP_ST_CANCELLED; |
5753c441 DH |
402 | if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) |
403 | object->n_exclusive--; | |
404 | if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) | |
405 | wake_up_bit(&op->flags, FSCACHE_OP_WAITING); | |
5753c441 DH |
406 | ret = 0; |
407 | } | |
408 | ||
418b7eb9 DH |
409 | if (put) |
410 | fscache_put_operation(op); | |
5753c441 DH |
411 | spin_unlock(&object->lock); |
412 | _leave(" = %d", ret); | |
413 | return ret; | |
414 | } | |
415 | ||
ef778e7a DH |
416 | /* |
417 | * Cancel all pending operations on an object | |
418 | */ | |
419 | void fscache_cancel_all_ops(struct fscache_object *object) | |
420 | { | |
421 | struct fscache_operation *op; | |
422 | ||
423 | _enter("OBJ%x", object->debug_id); | |
424 | ||
425 | spin_lock(&object->lock); | |
426 | ||
427 | while (!list_empty(&object->pending_ops)) { | |
428 | op = list_entry(object->pending_ops.next, | |
429 | struct fscache_operation, pend_link); | |
430 | fscache_stat(&fscache_n_op_cancelled); | |
431 | list_del_init(&op->pend_link); | |
432 | ||
08c2e3d0 DH |
433 | trace_fscache_op(object->cookie, op, fscache_op_cancel_all); |
434 | ||
ef778e7a | 435 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING); |
d3b97ca4 | 436 | op->cancel(op); |
ef778e7a DH |
437 | op->state = FSCACHE_OP_ST_CANCELLED; |
438 | ||
439 | if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) | |
440 | object->n_exclusive--; | |
441 | if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) | |
442 | wake_up_bit(&op->flags, FSCACHE_OP_WAITING); | |
443 | fscache_put_operation(op); | |
444 | cond_resched_lock(&object->lock); | |
445 | } | |
446 | ||
447 | spin_unlock(&object->lock); | |
448 | _leave(""); | |
449 | } | |
450 | ||
9f10523f | 451 | /* |
1f372dff | 452 | * Record the completion or cancellation of an in-progress operation. |
9f10523f | 453 | */ |
1f372dff | 454 | void fscache_op_complete(struct fscache_operation *op, bool cancelled) |
9f10523f DH |
455 | { |
456 | struct fscache_object *object = op->object; | |
457 | ||
458 | _enter("OBJ%x", object->debug_id); | |
459 | ||
460 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_IN_PROGRESS); | |
461 | ASSERTCMP(object->n_in_progress, >, 0); | |
462 | ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags), | |
463 | object->n_exclusive, >, 0); | |
464 | ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags), | |
465 | object->n_in_progress, ==, 1); | |
466 | ||
467 | spin_lock(&object->lock); | |
468 | ||
d3b97ca4 | 469 | if (!cancelled) { |
08c2e3d0 | 470 | trace_fscache_op(object->cookie, op, fscache_op_completed); |
d3b97ca4 DH |
471 | op->state = FSCACHE_OP_ST_COMPLETE; |
472 | } else { | |
473 | op->cancel(op); | |
08c2e3d0 | 474 | trace_fscache_op(object->cookie, op, fscache_op_cancelled); |
d3b97ca4 DH |
475 | op->state = FSCACHE_OP_ST_CANCELLED; |
476 | } | |
9f10523f DH |
477 | |
478 | if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) | |
479 | object->n_exclusive--; | |
480 | object->n_in_progress--; | |
481 | if (object->n_in_progress == 0) | |
482 | fscache_start_operations(object); | |
483 | ||
484 | spin_unlock(&object->lock); | |
485 | _leave(""); | |
486 | } | |
487 | EXPORT_SYMBOL(fscache_op_complete); | |
488 | ||
952efe7b DH |
489 | /* |
490 | * release an operation | |
491 | * - queues pending ops if this is the last in-progress op | |
492 | */ | |
493 | void fscache_put_operation(struct fscache_operation *op) | |
494 | { | |
495 | struct fscache_object *object; | |
496 | struct fscache_cache *cache; | |
497 | ||
498 | _enter("{OBJ%x OP%x,%d}", | |
d0eb06af KKM |
499 | op->object ? op->object->debug_id : 0, |
500 | op->debug_id, atomic_read(&op->usage)); | |
952efe7b DH |
501 | |
502 | ASSERTCMP(atomic_read(&op->usage), >, 0); | |
503 | ||
504 | if (!atomic_dec_and_test(&op->usage)) | |
505 | return; | |
506 | ||
402cb8dd | 507 | trace_fscache_op(op->object ? op->object->cookie : NULL, op, fscache_op_put); |
08c2e3d0 | 508 | |
952efe7b | 509 | _debug("PUT OP"); |
a39caadf DH |
510 | ASSERTIFCMP(op->state != FSCACHE_OP_ST_INITIALISED && |
511 | op->state != FSCACHE_OP_ST_COMPLETE, | |
9f10523f | 512 | op->state, ==, FSCACHE_OP_ST_CANCELLED); |
952efe7b DH |
513 | |
514 | fscache_stat(&fscache_n_op_release); | |
515 | ||
516 | if (op->release) { | |
517 | op->release(op); | |
518 | op->release = NULL; | |
519 | } | |
4a47132f | 520 | op->state = FSCACHE_OP_ST_DEAD; |
952efe7b DH |
521 | |
522 | object = op->object; | |
a39caadf DH |
523 | if (likely(object)) { |
524 | if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags)) | |
525 | atomic_dec(&object->n_reads); | |
526 | if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags)) | |
527 | fscache_unuse_cookie(object); | |
528 | ||
529 | /* now... we may get called with the object spinlock held, so we | |
530 | * complete the cleanup here only if we can immediately acquire the | |
531 | * lock, and defer it otherwise */ | |
532 | if (!spin_trylock(&object->lock)) { | |
533 | _debug("defer put"); | |
534 | fscache_stat(&fscache_n_op_deferred_release); | |
535 | ||
536 | cache = object->cache; | |
537 | spin_lock(&cache->op_gc_list_lock); | |
538 | list_add_tail(&op->pend_link, &cache->op_gc_list); | |
539 | spin_unlock(&cache->op_gc_list_lock); | |
540 | schedule_work(&cache->op_gc); | |
541 | _leave(" [defer]"); | |
542 | return; | |
543 | } | |
952efe7b | 544 | |
a39caadf DH |
545 | ASSERTCMP(object->n_ops, >, 0); |
546 | object->n_ops--; | |
547 | if (object->n_ops == 0) | |
548 | fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED); | |
952efe7b | 549 | |
a39caadf | 550 | spin_unlock(&object->lock); |
952efe7b DH |
551 | } |
552 | ||
952efe7b DH |
553 | kfree(op); |
554 | _leave(" [done]"); | |
555 | } | |
556 | EXPORT_SYMBOL(fscache_put_operation); | |
557 | ||
558 | /* | |
559 | * garbage collect operations that have had their release deferred | |
560 | */ | |
561 | void fscache_operation_gc(struct work_struct *work) | |
562 | { | |
563 | struct fscache_operation *op; | |
564 | struct fscache_object *object; | |
565 | struct fscache_cache *cache = | |
566 | container_of(work, struct fscache_cache, op_gc); | |
567 | int count = 0; | |
568 | ||
569 | _enter(""); | |
570 | ||
571 | do { | |
572 | spin_lock(&cache->op_gc_list_lock); | |
573 | if (list_empty(&cache->op_gc_list)) { | |
574 | spin_unlock(&cache->op_gc_list_lock); | |
575 | break; | |
576 | } | |
577 | ||
578 | op = list_entry(cache->op_gc_list.next, | |
579 | struct fscache_operation, pend_link); | |
580 | list_del(&op->pend_link); | |
581 | spin_unlock(&cache->op_gc_list_lock); | |
582 | ||
583 | object = op->object; | |
08c2e3d0 DH |
584 | trace_fscache_op(object->cookie, op, fscache_op_gc); |
585 | ||
9f10523f | 586 | spin_lock(&object->lock); |
952efe7b DH |
587 | |
588 | _debug("GC DEFERRED REL OBJ%x OP%x", | |
589 | object->debug_id, op->debug_id); | |
590 | fscache_stat(&fscache_n_op_gc); | |
591 | ||
592 | ASSERTCMP(atomic_read(&op->usage), ==, 0); | |
9f10523f | 593 | ASSERTCMP(op->state, ==, FSCACHE_OP_ST_DEAD); |
952efe7b DH |
594 | |
595 | ASSERTCMP(object->n_ops, >, 0); | |
596 | object->n_ops--; | |
597 | if (object->n_ops == 0) | |
598 | fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED); | |
599 | ||
600 | spin_unlock(&object->lock); | |
9f10523f | 601 | kfree(op); |
952efe7b DH |
602 | |
603 | } while (count++ < 20); | |
604 | ||
605 | if (!list_empty(&cache->op_gc_list)) | |
606 | schedule_work(&cache->op_gc); | |
607 | ||
608 | _leave(""); | |
609 | } | |
610 | ||
611 | /* | |
8af7c124 TH |
612 | * execute an operation using fs_op_wq to provide processing context - |
613 | * the caller holds a ref to this object, so we don't need to hold one | |
952efe7b | 614 | */ |
8af7c124 | 615 | void fscache_op_work_func(struct work_struct *work) |
952efe7b DH |
616 | { |
617 | struct fscache_operation *op = | |
8af7c124 | 618 | container_of(work, struct fscache_operation, work); |
952efe7b DH |
619 | unsigned long start; |
620 | ||
621 | _enter("{OBJ%x OP%x,%d}", | |
622 | op->object->debug_id, op->debug_id, atomic_read(&op->usage)); | |
623 | ||
08c2e3d0 DH |
624 | trace_fscache_op(op->object->cookie, op, fscache_op_work); |
625 | ||
952efe7b DH |
626 | ASSERT(op->processor != NULL); |
627 | start = jiffies; | |
628 | op->processor(op); | |
629 | fscache_hist(fscache_ops_histogram, start); | |
8af7c124 | 630 | fscache_put_operation(op); |
952efe7b DH |
631 | |
632 | _leave(""); | |
633 | } |