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