PSERIALIZE(9)

NAME
pserialize - passive serialization mechanism

SYNOPSIS
#include <sys/pserialize.h>
pserialize_t
pserialize_create(void);
void
pserialize_destroy(pserialize_t psz);
int
pserialize_read_enter(void);
void
pserialize_read_exit(int s);
void
pserialize_perform(pserialize_t psz);

DESCRIPTION
Passive serialization is a reader / writer synchronisation mechanism designed for lock-less read operations. The read operations may happen from software interrupt at IPL_SOFTCLOCK.

FUNCTIONS

pserialize_create()
    Allocate a new synchronisation object.

pserialize_destroy()
    Destroy the synchronisation object. No synchronisation activity should happen at this point.

pserialize_read_enter()
    Enter the critical path of the reader side. Returns an IPL value, which must be passed to pserialize_read_exit(9). Protected code path is not allowed to block.

pserialize_read_exit()
    Exit the critical path of the reader side. Takes the IPL value returned by pserialize_read_enter(9).

pserialize_perform()
    Perform the passive serialization on the writer side. Passing of this function ensures that no readers are in action. Writers must be additionally serialized with a separate mechanism, e.g. mutex(9). Operation blocks and it may only be performed from thread context.

EXAMPLES
Given a global database of frotz records:

    	struct frotz {
    		...
    		struct frotz	*f_next;
    	};
    	static struct {
    		kmutex_t	lock;
    		pserialize_t	psz;
    		struct frotz	*first;
    	} frobbotzim __cacheline_aligned;

Create a frotz and publish it, as a writer:

    	struct frotz *f = pool_get(&frotz_pool, PR_WAITOK);
    	/* Initialize f.  */
    	...
    	mutex_enter(&frobbotzim.lock);
    	f->f_next = frobbotzim.first;
    	/*
    	 * Publish the contents of f->f_next before we publish the
    	 * pointer to f in frobbotzim.first.
    	 */
    	membar_producer();
    	frobbotzim.first = f;
    	mutex_exit(&frobbotzim.lock);

Find a frotz, as a reader:

    	struct frotz *f;
    	int error = ENOENT;
    	int s;
    	s = pserialize_read_enter();
    	for (f = frobbotzim.first; f != NULL; f = f->f_next) {
    		/* Fetch f before we fetch anything f points to.  */
    		membar_datadep_consumer();
    		if (f->f_... == key) {
    			/*
    			 * Grab whatever part of the frotz we need.
    			 * Note that we can't use the frotz after
    			 * pserialize_read_exit, without a stronger
    			 * kind of reference, say a reference count
    			 * managed by atomic_ops(3).
    			 */
    			*resultp = f->f_...;
    			error = 0;
    			break;
    		}
    	}
    	pserialize_read_exit(s);
    	return error;

Remove a frotz, as a writer, and free it once there are no more readers:

    	struct frotz **fp, *f;
    	mutex_enter(&frobbotzim.lock);
    	for (fp = &frobbotzim.first; (f = *fp) != NULL; fp = &f->f_next) {
    		if (f->f_... == key) {
    			/*
    			 * Unhook it from the list.  Readers may still
    			 * be traversing the list at this point, so
    			 * the next pointer must remain valid and
    			 * memory must remain allocated.
    			 */
    			*fp = f->f_next;
    			break;
    		}
    	}
    	/*
    	 * Wait for all existing readers to complete.  New readers will
    	 * not see f because the list no longer points to it.
    	 */
    	pserialize_perform(frobbotzim.psz);
    	/* Now nobody else can be touching f, so it is safe to free.  */
    	mutex_exit(&frobbotzim.lock);
    	if (f != NULL)
    		pool_put(&frotz_pool, f);

CODE REFERENCES
The pserialize is implemented within the file sys/kern/subr_pserialize.c.

SEE ALSO
membar_ops(3), condvar(9), mutex(9), rwlock(9) Hennessy, et al., Passive serialization in a multitasking environment, US Patent and Trademark Office, February 28, 1989, US Patent 4809168.

HISTORY
Passive serialization mechanism first appeared in NetBSD 6.0.