Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 1 | .. SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | ================================ |
| 4 | Asynchronous Operations Handling |
| 5 | ================================ |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 6 | |
| 7 | By: David Howells <dhowells@redhat.com> |
| 8 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 9 | .. Contents: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 10 | |
| 11 | (*) Overview. |
| 12 | |
| 13 | (*) Operation record initialisation. |
| 14 | |
| 15 | (*) Parameters. |
| 16 | |
| 17 | (*) Procedure. |
| 18 | |
| 19 | (*) Asynchronous callback. |
| 20 | |
| 21 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 22 | Overview |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 23 | ======== |
| 24 | |
| 25 | FS-Cache has an asynchronous operations handling facility that it uses for its |
| 26 | data storage and retrieval routines. Its operations are represented by |
| 27 | fscache_operation structs, though these are usually embedded into some other |
| 28 | structure. |
| 29 | |
Randy Dunlap | 1116645 | 2020-07-03 14:43:17 -0700 | [diff] [blame] | 30 | This facility is available to and expected to be used by the cache backends, |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 31 | and FS-Cache will create operations and pass them off to the appropriate cache |
| 32 | backend for completion. |
| 33 | |
| 34 | To make use of this facility, <linux/fscache-cache.h> should be #included. |
| 35 | |
| 36 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 37 | Operation Record Initialisation |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 38 | =============================== |
| 39 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 40 | An operation is recorded in an fscache_operation struct:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 41 | |
| 42 | struct fscache_operation { |
| 43 | union { |
| 44 | struct work_struct fast_work; |
| 45 | struct slow_work slow_work; |
| 46 | }; |
| 47 | unsigned long flags; |
| 48 | fscache_operation_processor_t processor; |
| 49 | ... |
| 50 | }; |
| 51 | |
| 52 | Someone wanting to issue an operation should allocate something with this |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 53 | struct embedded in it. They should initialise it by calling:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 54 | |
| 55 | void fscache_operation_init(struct fscache_operation *op, |
| 56 | fscache_operation_release_t release); |
| 57 | |
| 58 | with the operation to be initialised and the release function to use. |
| 59 | |
| 60 | The op->flags parameter should be set to indicate the CPU time provision and |
| 61 | the exclusivity (see the Parameters section). |
| 62 | |
| 63 | The op->fast_work, op->slow_work and op->processor flags should be set as |
| 64 | appropriate for the CPU time provision (see the Parameters section). |
| 65 | |
| 66 | FSCACHE_OP_WAITING may be set in op->flags prior to each submission of the |
| 67 | operation and waited for afterwards. |
| 68 | |
| 69 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 70 | Parameters |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 71 | ========== |
| 72 | |
| 73 | There are a number of parameters that can be set in the operation record's flag |
| 74 | parameter. There are three options for the provision of CPU time in these |
| 75 | operations: |
| 76 | |
| 77 | (1) The operation may be done synchronously (FSCACHE_OP_MYTHREAD). A thread |
| 78 | may decide it wants to handle an operation itself without deferring it to |
| 79 | another thread. |
| 80 | |
| 81 | This is, for example, used in read operations for calling readpages() on |
| 82 | the backing filesystem in CacheFiles. Although readpages() does an |
| 83 | asynchronous data fetch, the determination of whether pages exist is done |
| 84 | synchronously - and the netfs does not proceed until this has been |
| 85 | determined. |
| 86 | |
| 87 | If this option is to be used, FSCACHE_OP_WAITING must be set in op->flags |
| 88 | before submitting the operation, and the operating thread must wait for it |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 89 | to be cleared before proceeding:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 90 | |
| 91 | wait_on_bit(&op->flags, FSCACHE_OP_WAITING, |
NeilBrown | 7431620 | 2014-07-07 15:16:04 +1000 | [diff] [blame] | 92 | TASK_UNINTERRUPTIBLE); |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 93 | |
| 94 | |
| 95 | (2) The operation may be fast asynchronous (FSCACHE_OP_FAST), in which case it |
| 96 | will be given to keventd to process. Such an operation is not permitted |
| 97 | to sleep on I/O. |
| 98 | |
| 99 | This is, for example, used by CacheFiles to copy data from a backing fs |
| 100 | page to a netfs page after the backing fs has read the page in. |
| 101 | |
| 102 | If this option is used, op->fast_work and op->processor must be |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 103 | initialised before submitting the operation:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 104 | |
| 105 | INIT_WORK(&op->fast_work, do_some_work); |
| 106 | |
| 107 | |
| 108 | (3) The operation may be slow asynchronous (FSCACHE_OP_SLOW), in which case it |
| 109 | will be given to the slow work facility to process. Such an operation is |
| 110 | permitted to sleep on I/O. |
| 111 | |
| 112 | This is, for example, used by FS-Cache to handle background writes of |
| 113 | pages that have just been fetched from a remote server. |
| 114 | |
| 115 | If this option is used, op->slow_work and op->processor must be |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 116 | initialised before submitting the operation:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 117 | |
| 118 | fscache_operation_init_slow(op, processor) |
| 119 | |
| 120 | |
| 121 | Furthermore, operations may be one of two types: |
| 122 | |
| 123 | (1) Exclusive (FSCACHE_OP_EXCLUSIVE). Operations of this type may not run in |
| 124 | conjunction with any other operation on the object being operated upon. |
| 125 | |
| 126 | An example of this is the attribute change operation, in which the file |
| 127 | being written to may need truncation. |
| 128 | |
| 129 | (2) Shareable. Operations of this type may be running simultaneously. It's |
| 130 | up to the operation implementation to prevent interference between other |
| 131 | operations running at the same time. |
| 132 | |
| 133 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 134 | Procedure |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 135 | ========= |
| 136 | |
| 137 | Operations are used through the following procedure: |
| 138 | |
| 139 | (1) The submitting thread must allocate the operation and initialise it |
| 140 | itself. Normally this would be part of a more specific structure with the |
| 141 | generic op embedded within. |
| 142 | |
| 143 | (2) The submitting thread must then submit the operation for processing using |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 144 | one of the following two functions:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 145 | |
| 146 | int fscache_submit_op(struct fscache_object *object, |
| 147 | struct fscache_operation *op); |
| 148 | |
| 149 | int fscache_submit_exclusive_op(struct fscache_object *object, |
| 150 | struct fscache_operation *op); |
| 151 | |
| 152 | The first function should be used to submit non-exclusive ops and the |
| 153 | second to submit exclusive ones. The caller must still set the |
| 154 | FSCACHE_OP_EXCLUSIVE flag. |
| 155 | |
| 156 | If successful, both functions will assign the operation to the specified |
| 157 | object and return 0. -ENOBUFS will be returned if the object specified is |
| 158 | permanently unavailable. |
| 159 | |
| 160 | The operation manager will defer operations on an object that is still |
| 161 | undergoing lookup or creation. The operation will also be deferred if an |
| 162 | operation of conflicting exclusivity is in progress on the object. |
| 163 | |
| 164 | If the operation is asynchronous, the manager will retain a reference to |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 165 | it, so the caller should put their reference to it by passing it to:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 166 | |
| 167 | void fscache_put_operation(struct fscache_operation *op); |
| 168 | |
| 169 | (3) If the submitting thread wants to do the work itself, and has marked the |
| 170 | operation with FSCACHE_OP_MYTHREAD, then it should monitor |
| 171 | FSCACHE_OP_WAITING as described above and check the state of the object if |
Will Deacon | 806654a | 2018-11-19 11:02:45 +0000 | [diff] [blame] | 172 | necessary (the object might have died while the thread was waiting). |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 173 | |
| 174 | When it has finished doing its processing, it should call |
David Howells | 9f10523 | 2012-12-20 21:52:35 +0000 | [diff] [blame] | 175 | fscache_op_complete() and fscache_put_operation() on it. |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 176 | |
| 177 | (4) The operation holds an effective lock upon the object, preventing other |
| 178 | exclusive ops conflicting until it is released. The operation can be |
| 179 | enqueued for further immediate asynchronous processing by adjusting the |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 180 | CPU time provisioning option if necessary, eg:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 181 | |
| 182 | op->flags &= ~FSCACHE_OP_TYPE; |
| 183 | op->flags |= ~FSCACHE_OP_FAST; |
| 184 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 185 | and calling:: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 186 | |
| 187 | void fscache_enqueue_operation(struct fscache_operation *op) |
| 188 | |
| 189 | This can be used to allow other things to have use of the worker thread |
| 190 | pools. |
| 191 | |
| 192 | |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 193 | Asynchronous Callback |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 194 | ===================== |
| 195 | |
| 196 | When used in asynchronous mode, the worker thread pool will invoke the |
| 197 | processor method with a pointer to the operation. This should then get at the |
Mauro Carvalho Chehab | 09eac7c | 2020-04-27 23:16:56 +0200 | [diff] [blame] | 198 | container struct by using container_of():: |
David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 199 | |
| 200 | static void fscache_write_op(struct fscache_operation *_op) |
| 201 | { |
| 202 | struct fscache_storage *op = |
| 203 | container_of(_op, struct fscache_storage, op); |
| 204 | ... |
| 205 | } |
| 206 | |
| 207 | The caller holds a reference on the operation, and will invoke |
| 208 | fscache_put_operation() when the processor function returns. The processor |
| 209 | function is at liberty to call fscache_enqueue_operation() or to take extra |
| 210 | references. |