214 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 		       ================================
 | |
| 		       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.
 |