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