| 		       ================================ | 
 | 		       ASYNCHRONOUS OPERATIONS HANDLING | 
 | 		       ================================ | 
 |  | 
 | By: David Howells <dhowells@redhat.com> | 
 |  | 
 | Contents: | 
 |  | 
 |  (*) Overview. | 
 |  | 
 |  (*) Operation record initialisation. | 
 |  | 
 |  (*) Parameters. | 
 |  | 
 |  (*) Procedure. | 
 |  | 
 |  (*) Asynchronous callback. | 
 |  | 
 |  | 
 | ======== | 
 | OVERVIEW | 
 | ======== | 
 |  | 
 | FS-Cache has an asynchronous operations handling facility that it uses for its | 
 | data storage and retrieval routines.  Its operations are represented by | 
 | fscache_operation structs, though these are usually embedded into some other | 
 | structure. | 
 |  | 
 | This facility is available to and expected to be be used by the cache backends, | 
 | and FS-Cache will create operations and pass them off to the appropriate cache | 
 | backend for completion. | 
 |  | 
 | To make use of this facility, <linux/fscache-cache.h> should be #included. | 
 |  | 
 |  | 
 | =============================== | 
 | OPERATION RECORD INITIALISATION | 
 | =============================== | 
 |  | 
 | An operation is recorded in an fscache_operation struct: | 
 |  | 
 | 	struct fscache_operation { | 
 | 		union { | 
 | 			struct work_struct fast_work; | 
 | 			struct slow_work slow_work; | 
 | 		}; | 
 | 		unsigned long		flags; | 
 | 		fscache_operation_processor_t processor; | 
 | 		... | 
 | 	}; | 
 |  | 
 | Someone wanting to issue an operation should allocate something with this | 
 | struct embedded in it.  They should initialise it by calling: | 
 |  | 
 | 	void fscache_operation_init(struct fscache_operation *op, | 
 | 				    fscache_operation_release_t release); | 
 |  | 
 | with the operation to be initialised and the release function to use. | 
 |  | 
 | The op->flags parameter should be set to indicate the CPU time provision and | 
 | the exclusivity (see the Parameters section). | 
 |  | 
 | The op->fast_work, op->slow_work and op->processor flags should be set as | 
 | appropriate for the CPU time provision (see the Parameters section). | 
 |  | 
 | FSCACHE_OP_WAITING may be set in op->flags prior to each submission of the | 
 | operation and waited for afterwards. | 
 |  | 
 |  | 
 | ========== | 
 | PARAMETERS | 
 | ========== | 
 |  | 
 | There are a number of parameters that can be set in the operation record's flag | 
 | parameter.  There are three options for the provision of CPU time in these | 
 | operations: | 
 |  | 
 |  (1) The operation may be done synchronously (FSCACHE_OP_MYTHREAD).  A thread | 
 |      may decide it wants to handle an operation itself without deferring it to | 
 |      another thread. | 
 |  | 
 |      This is, for example, used in read operations for calling readpages() on | 
 |      the backing filesystem in CacheFiles.  Although readpages() does an | 
 |      asynchronous data fetch, the determination of whether pages exist is done | 
 |      synchronously - and the netfs does not proceed until this has been | 
 |      determined. | 
 |  | 
 |      If this option is to be used, FSCACHE_OP_WAITING must be set in op->flags | 
 |      before submitting the operation, and the operating thread must wait for it | 
 |      to be cleared before proceeding: | 
 |  | 
 | 		wait_on_bit(&op->flags, FSCACHE_OP_WAITING, | 
 | 			    fscache_wait_bit, TASK_UNINTERRUPTIBLE); | 
 |  | 
 |  | 
 |  (2) The operation may be fast asynchronous (FSCACHE_OP_FAST), in which case it | 
 |      will be given to keventd to process.  Such an operation is not permitted | 
 |      to sleep on I/O. | 
 |  | 
 |      This is, for example, used by CacheFiles to copy data from a backing fs | 
 |      page to a netfs page after the backing fs has read the page in. | 
 |  | 
 |      If this option is used, op->fast_work and op->processor must be | 
 |      initialised before submitting the operation: | 
 |  | 
 | 		INIT_WORK(&op->fast_work, do_some_work); | 
 |  | 
 |  | 
 |  (3) The operation may be slow asynchronous (FSCACHE_OP_SLOW), in which case it | 
 |      will be given to the slow work facility to process.  Such an operation is | 
 |      permitted to sleep on I/O. | 
 |  | 
 |      This is, for example, used by FS-Cache to handle background writes of | 
 |      pages that have just been fetched from a remote server. | 
 |  | 
 |      If this option is used, op->slow_work and op->processor must be | 
 |      initialised before submitting the operation: | 
 |  | 
 | 		fscache_operation_init_slow(op, processor) | 
 |  | 
 |  | 
 | Furthermore, operations may be one of two types: | 
 |  | 
 |  (1) Exclusive (FSCACHE_OP_EXCLUSIVE).  Operations of this type may not run in | 
 |      conjunction with any other operation on the object being operated upon. | 
 |  | 
 |      An example of this is the attribute change operation, in which the file | 
 |      being written to may need truncation. | 
 |  | 
 |  (2) Shareable.  Operations of this type may be running simultaneously.  It's | 
 |      up to the operation implementation to prevent interference between other | 
 |      operations running at the same time. | 
 |  | 
 |  | 
 | ========= | 
 | PROCEDURE | 
 | ========= | 
 |  | 
 | Operations are used through the following procedure: | 
 |  | 
 |  (1) The submitting thread must allocate the operation and initialise it | 
 |      itself.  Normally this would be part of a more specific structure with the | 
 |      generic op embedded within. | 
 |  | 
 |  (2) The submitting thread must then submit the operation for processing using | 
 |      one of the following two functions: | 
 |  | 
 | 	int fscache_submit_op(struct fscache_object *object, | 
 | 			      struct fscache_operation *op); | 
 |  | 
 | 	int fscache_submit_exclusive_op(struct fscache_object *object, | 
 | 					struct fscache_operation *op); | 
 |  | 
 |      The first function should be used to submit non-exclusive ops and the | 
 |      second to submit exclusive ones.  The caller must still set the | 
 |      FSCACHE_OP_EXCLUSIVE flag. | 
 |  | 
 |      If successful, both functions will assign the operation to the specified | 
 |      object and return 0.  -ENOBUFS will be returned if the object specified is | 
 |      permanently unavailable. | 
 |  | 
 |      The operation manager will defer operations on an object that is still | 
 |      undergoing lookup or creation.  The operation will also be deferred if an | 
 |      operation of conflicting exclusivity is in progress on the object. | 
 |  | 
 |      If the operation is asynchronous, the manager will retain a reference to | 
 |      it, so the caller should put their reference to it by passing it to: | 
 |  | 
 | 	void fscache_put_operation(struct fscache_operation *op); | 
 |  | 
 |  (3) If the submitting thread wants to do the work itself, and has marked the | 
 |      operation with FSCACHE_OP_MYTHREAD, then it should monitor | 
 |      FSCACHE_OP_WAITING as described above and check the state of the object if | 
 |      necessary (the object might have died whilst the thread was waiting). | 
 |  | 
 |      When it has finished doing its processing, it should call | 
 |      fscache_put_operation() on it. | 
 |  | 
 |  (4) The operation holds an effective lock upon the object, preventing other | 
 |      exclusive ops conflicting until it is released.  The operation can be | 
 |      enqueued for further immediate asynchronous processing by adjusting the | 
 |      CPU time provisioning option if necessary, eg: | 
 |  | 
 | 	op->flags &= ~FSCACHE_OP_TYPE; | 
 | 	op->flags |= ~FSCACHE_OP_FAST; | 
 |  | 
 |      and calling: | 
 |  | 
 | 	void fscache_enqueue_operation(struct fscache_operation *op) | 
 |  | 
 |      This can be used to allow other things to have use of the worker thread | 
 |      pools. | 
 |  | 
 |  | 
 | ===================== | 
 | ASYNCHRONOUS CALLBACK | 
 | ===================== | 
 |  | 
 | When used in asynchronous mode, the worker thread pool will invoke the | 
 | processor method with a pointer to the operation.  This should then get at the | 
 | container struct by using container_of(): | 
 |  | 
 | 	static void fscache_write_op(struct fscache_operation *_op) | 
 | 	{ | 
 | 		struct fscache_storage *op = | 
 | 			container_of(_op, struct fscache_storage, op); | 
 | 	... | 
 | 	} | 
 |  | 
 | The caller holds a reference on the operation, and will invoke | 
 | fscache_put_operation() when the processor function returns.  The processor | 
 | function is at liberty to call fscache_enqueue_operation() or to take extra | 
 | references. |