Updated: 2022/Sep/29

Please read Privacy Policy. It's for your privacy.


PSERIALIZE(9)              Kernel Developer's Manual             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
              are typically additionally serialized with a separate mechanism,
              e.g.  mutex(9), to remove objects used by readers from a
              published list.  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;
                     }
             }
             mutex_exit(&frobbotzim.lock);

             /*
              * 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.  */
             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, US Patent 4809168, February 28, 1989.

HISTORY
     Passive serialization mechanism first appeared in NetBSD 6.0.

NetBSD 10.99                   January 26, 2016                   NetBSD 10.99