David Howells | 952efe7 | 2009-04-03 16:42:39 +0100 | [diff] [blame] | 1 | ================================ |
| 2 | ASYNCHRONOUS OPERATIONS HANDLING |
| 3 | ================================ |
| 4 | |
| 5 | By: David Howells <dhowells@redhat.com> |
| 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. |