]>
Commit | Line | Data |
---|---|---|
952efe7b DH |
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, | |
74316201 | 93 | TASK_UNINTERRUPTIBLE); |
952efe7b DH |
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 | |
9f10523f | 177 | fscache_op_complete() and fscache_put_operation() on it. |
952efe7b DH |
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. |