Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
  Previous   Contents   Next 
   
 
Chapter 2

Architecture Features for Module Writers

This chapter discusses architectural details you should keep in mind when creating a public module for a data service.

The following topics are included:

Function Categories

The Service Provider Layer API functions can be divided into three categories:

  • Data store functions, which facilitate activities related to the public module and underlying data service themselves. These functions include configure(), mklocation(), status(), and version().

  • dhcptab container functions, which facilitate the creation of the dhcptab container, the writing of records to the dhcptab container, and the query of records in the dhcptab container. The open_dt() function creates a handle for the container, and the other functions take a pointer to that handle. The close_dt() function destroys the handle when it closes the container.

  • Network container functions, which facilitate the creation of DHCP network containers, the writing of records to the network containers, and the query of records in the network containers. The open_dn() function creates a handle for the container, and the other functions take a pointer to that handle. The close_dn() function destroys the handle when it closes the container.

The functions are described in more detail in Chapter 3, Service Provider Layer API.

Considerations for Multithreading

The DHCP server implements multithreading, which enables it to service many clients simultaneously. Public modules are required to be MT-SAFE to support multithreading by the DHCP server.

To make your module MT-SAFE, you must synchronize calls to add_d?(), delete_d?(), and modify_d?() so that they are called serially. For example, if one thread is inside add_dn() for a given DHCP network container, no other thread may be inside add_dn(), delete_dn(), modify_dn(), or lookup_dn() for that same container. If your public module supports a local file-system-based data service, you can use the synchronization service to take care of this for you. See "Synchronizing Access to File-System-Based Containers" for more information.

Synchronizing Access to File-System-Based Containers

When you write a public module that provides access to containers in a local file-system-based data service (the data service runs on the same machine as the DHCP server), it can be difficult to synchronize access to the underlying data service between multiple processes and threads.

The Solaris DHCP synchronization service simplifies the design of public modules using local file-system-based data services by pushing synchronization up into the Framework Configuration Layer. When you design your module to use this framework, your code becomes simpler and your design cleaner.

The synchronization service provides public modules with per-container exclusive synchronization of all callers of the add_d?(), delete_d?(), and modify_d?() Service Provider Layer API calls. This means that if one thread is inside add_dn() for a given DHCP network container, no other thread may be inside add_dn(), delete_dn(), modify_dn() or lookup_dn() for that same container. However, other threads may be within routines that provide no synchronization guarantees, such as close_dn().

Per-container shared synchronization of all callers of lookup_d?() is also provided. Thus, there may be many threads performing a lookup on the same container, but only one thread may perform an add, delete, or modify operation.

The synchronization service is implemented as a daemon (/usr/lib/inet/dsvclockd). Lock manager requests are made on the public module's behalf through Framework Configuration Layer API calls. The interface between the Framework Configuration layer and the lock manager daemon uses the Solaris doors interprocess communication mechanism. See, for example, door_create(3DOOR) and door_call(3DOOR).

The Framework Configuration layer starts the dsvclockd daemon if a public module requests synchronization and the daemon is not already running. The daemon automatically exits if it manages no locks for 15 minutes. To change this interval, you can create a /etc/default/dsvclockd file and set the IDLE default to the number of idle minutes before the daemon terminates.

A public module notifies the Framework Configuration Layer that it requires synchronization services by providing the following global variable in one of the module's source files:

dsvc_synchtype_t dsvc_synchtype = DSVC_SYNCH_DSVCD;

A public module notifies the Framework Configuration Layer that it does not require synchronization services by including the following global variable in one of the module's source files:

dsvc_synchtype_t dsvc_synchtype = DSVC_SYNCH_NONE;

DSVC_SYNCH_DSVCD and DSVC_SYNCH_NONE are the only two synchronization types that exist currently.

Avoiding Update Collisions

The architecture provides a facility that helps a files-based module avoid record update collisions. The Service Provider API facilitates the maintenance of data consistency through the use of a per-record update signature, an unsigned 64-bit integer. The update signature is the d?_sig element of the d?_rec_t container record data structure, defined in /usr/include/dhcp_svc_public.h. All layers of the architecture use d?_rec_t records, from the Application/Service Layer through the Framework Configuration Layer API and on through to the Service Provider Layer API. Above the Service Provider Layer, the update signature is an opaque object which is not manipulated by users of the Framework Configuration Layer API.

When a module receives a d?_rec_t record through a Service Provider Layer API function call, it should perform a lookup in the data service to find a record that matches the key fields of the d?_rec_t, and compare the signature of the internal record against the d?_rec_t passed by the call. If the signature of the internal record does not match that of the passed record, then the record has been changed since the consumer acquired it from the public module. In this case, the module should return DSVC_COLLISION, which informs the caller that the record has been changed since it was acquired. If the signatures match, the module should increment the update signature of the argument record before it stores the record.

When a module receives a new d?_rec_t record through the Service Provider Layer API, the module must assign a value to the update signature before it adds the record to the container. The simplest way is to set the value to 1.

However, in certain rare situations a collision might not be detected if the signature always has the same initial value. Consider the following scenario. Thread A adds a record with a signature of 1, and Thread B looks up that record. Thread A deletes the record and creates a new record with the same key fields and a signature of 1 since it has just been created. Thread B then modifies the record it looked up, but that has already been deleted. The module compares the key fields and signatures of the record looked up by Thread B and the record in the data store, finds them to be the same, and makes the modification. Such a modification attempt should have been a collision because the records are, in fact, not the same.

The ds_SUNWfiles.so and ds_SUNWbinfiles.so modules provided with Solaris DHCP address such a possibility. They divide the update signature into two fields to ensure the uniqueness of each record's signature. The first 16 bit field of the update signature is set to a randomly generated number. This field never changes in the record after it is set. The lower 48 bit field of the signature is set to 1 and then incremented each time the record is updated.


Note - The modules provided with Solaris DHCP illustrate one approach you can use to avoid record update collisions. You can devise your own method or use a similar one.


Naming the Public Module and Data Store Containers

The public module and containers must both contain version numbers to enable the architecture's upgrading mechanism to work.

Public Module Name

You must use the following name format for your public module:

ds_name.so.ver

where name is the name of the module and ver is the container format version number. The name must use a prefix that is an internationally known identifier associated with your organization. For example, the public modules that Sun Microsystems provides have names prefixed with SUNW, the stock ticker symbol for Sun. For example, the NIS+ public module is named ds_SUNWnisplus.so.1. By including such an identifier in the module name, you avoid public module name collisions in the /usr/lib/inet/dhcp/svc public module directory.

If your company name is Inet DataBase, for example, you might call your module ds_IDBtrees.so.1

Container Name

The container names presented to the administrator through the administrative interface must always be dhcptab and the dotted IP network address for the DHCP network tables, such as 10.0.0.0.

Internally, the data store container names must contain the version number to enable you to produce revisions of your container formats whenever necessary. This naming scheme allows the coexistence of multiple versions of a container, which is a requirement for the architecture's container version upgrade mechanism to work.

The names used for the containers should include a globally recognizable token to ensure that the names are unique.

For example, the NIS+ public module provided with Solaris DHCP would create the dhcptab container internally as SUNWnisplus1_dhcptab. The container for the 172.21.174.0 network table would be SUNWnisplus1_172.21.174.0.

If your company name is Inet DataBase, and your public module is ds_IDBtrees.so.1, you would name your containers IDBtrees1_dhcptab and IDBtrees1_172.21.174.0.

 
 
 
  Previous   Contents   Next