XAM Query Language

As well as providing vendor-independent means of creating, retrieving, modifying and deleting XSets, the SNIA XAM v1.0 specification also defines a query language (XAM QL), based on a subset of the SQL language, for selecting and retrieving the XUIDs of XSets based on content-defined criteria.

The set of reserved words for this query language is quite small: select, where, and, or, not, like, exists, binding, readonly, typeof, length, date, TRUE, FALSE, before, after, contains, and within. By design, XAM queries look like an SQL select statement. The query language is case insensitive and uses the ASCII character set.

Here is an example of a simple XAM query:

select ".xset.xuid" where "com.example.name" = ’Tuckers Plantation'


Version 1.0 of the XAM specification defines two levels of query language support, i.e. Level 1 and Level 2. Level 1 defines queries on properties and field attributes in XSets and is mandatory. Any XSet property value that is accessible to an application program via the XAM library can be queried. Level 2 extends Level 1 to support queries on XStreams and is optional. Both levels of query are accessed through a single, defined job type that all XAM providers must support. Since no vendor that I am aware of has actually implemented level 2 queries, the remainder of this post focuses on level 1 queries.

A XAM query statement consists of a mandatory select clause followed by an optional where clause. For XAM v1.0 the only valid select clause is select '.xset.xuid'. This specifies that the application is requesting a list of XUID values. For example

select ".xset.xuid "


will return a list of every XSet that is readable at the time of the query.

The where clause is used to specify a subset of XSets to be matched. For level 1 queries it is restricted to comparisons between XSet properties and literal values and/or field attributes and literal values.

select ".xset.xuid" where ".xam.time.xuid" > date(’2009-02-01T00:00:00.0’)


will return the list of all Xsets which were created on or after February 1st 2009.

The following table shows which field and literal types can be validly compared.
[table id=14/]
The XAM library validates that strings and strings liberals are conforming UTF-8 strings. Non-conforming UTF-8 string literals generate a XAM non-fatal query syntax error. Issues such as single versus multiple glyph characters and non printable characters are unspecified and are implementation and application dependent. String comparisons are case sensitive and comparison operators operate on a byte-by-byte basis. For relational operators the relationships are defined by the byte values. All data normalization is the responsibility of the application.

The supported escape sequences are the following. Any other escape sequence will generate an non-fatal error.
[table id=15/]
String literals must be quoted with single quotes. String literals which contain one or more single quote characters must escape each single quote character using a backslash. For example, the string literal of ’This is a string literal’ would be represented in a query as

select ".xset.xuid" where "com.example.string-property" = ’This is a string literal’


To use a single quote in a string, guard it with a backslash

select ".xset.xuid" where "com.example.claimed.ownership" = ’Tucker\’s’


All instances of field names in a XAM query string must be quoted with double quotes. If a field name contains double quote characters, each double quote character must be escaped using a backslash. For example, the field name for the xam_boolean property com.example.”qstring” should be represented in a query as

select ".xam.xuid" where "com.example.\"qstring\"" = TRUE


If a field name contains a backslash character, the backslash itself must be escaped with another backslash. For example, the field name for the xam_double property com.example.file\ratio should be represented in a query as

select ".xam.xuid" where "com.example.file\\ratio" = 100.1


The query language accepts date-time and XUID-valued literals using the selector functions date() and xuid() respectively. The date() function takes a properly formed date-time value, specified as a literal string which is consistent with the XAM date-time specification.

select ".xset.xuid" where ".xam.time.xuid" = date(’2009-06-01T00:00:00.0’)


The xuid() function expects an XUID which is in the form of a base64 encoded string literal. An improperly formed string literal generates a non-fatal error during query parsing.

Field attributes are accessed using the exists, typeof, readonly, binding, and length field attribute accessor functions. The exists() function tests for the existence of an named field attribute (property) in an XSet. It evaluates to TRUE if an XSet contains the named field otherwise it evaluates FALSE.

select ".xset.xuid" where exists("com.example.name")


The typeof() function returns the MIME type of a named field in an XSet which is a string-valued property.

select ".xset.xuid" where typeof("com.example.data") = ’text/plain’
select ".xset.xuid" where typeof("com.example.data") like ’text%’


This function can be used whenever an application could use a field reference to a string-valued property. Note that comparisons with any non-string literal value generate a non-fatal error during the parsing of the query.

The readonly() function evaluates to TRUE when a field in an XSet is marked as readonly.

select ".xset.xuid" where readonly("com.example.flag")
select ".xset.xuid" where not readonly("com.example.name")


The binding() function evaluates to TRUE when a field in an XSet is marked as binding.

select ".xset.xuid" where binding("com.example.case_id")
select ".xset.xuid" where not binding("com.example.subject")


The length() function returns the length, in bytes, of a named field.

select ".xset.xuid" where length("com.example.data") > 1024


When used on property fields this function returns the length as defined for stypes. Note that this function should not be used to compare the number of characters in a string as this comparison depends on the character encoding being used.

Turning now to logical operators. Subclauses within the where clause may be combined and modified by using the logical operators and, or and not. These operators are similar to their SQL counterparts.

The and operator requires both subclauses to evaluate to TRUE before including an XSet in the results. For example

select ".xset.xuid" where typeof("com.example.stream") = ’image/gif’ and length("com.example.stream") > 4096


selects only those XSets containing GIF images whose size is greater than 4096 bytes. The or operator evaluates to TRUE if either subclause evaluates to TRUE. For example

select ".xset.xuid" where typeof("com.example.stream") = ’image/jpeg’ or typeof("com.example.stream") = ’image/gif’


selects only those XSets containing a named stream of image type JPEG or GIF. The not operator negates a boolean expression. For example

select ".xset.xuid" where not binding("com.example.property")


selects all XSets with the nonbinding property com.example.property.

As in SQL certain operators take precedence over other operators in queries. XAM QL operator precedence is as follows:
[table id=16/]
Operators of the same precedence are evaluated left to right within a query. However operator precedence may be overridden using parentheses as shown by the following example.

select ".xset.xuid" where not "com.example.bool-prop" and  "com.example.int-prop" = 64
select ".xset.xuid" where not ("com.example.bool-prop" and "com.example.int-prop" = 64)


In the first example, the not operator only applies to the com.example.bool-prop property. In the second example, the not operator applies to both com.example.bool-prop and com.example.int-prop = 42.

Now that we are somewhat familiar with XAM QL syntax, the next question to be answered is how do we tell a XAM Storage System to execute a query and return the results to us. Well it turns out that a query is executed as a particular type of XAM job called a query job. The input to a query job is an XSet which you must create. This XSet must contain two items - the field org.snia.xam.job.command which is always set to 'xam.job.query' and a XStream xam.job.query.command which is a UTF-8 text stream (MIME type 'text/plain; charset=utf-8’) containing the actual query expression string.

A query job may optionally be committed before or during the execution of the job, but this commit is not required. The ability to commit a running job is optional and implementation dependant. An application can determine if job commit is supported by checking the XSystem boolean property .xsystem.job.commit.supported. Failure to commit a query job before closing the XSet means the results of the query job are not persistently stored.

A query job is executed by invoking submitJob. If either of the two required items in the query job XSet is not correctly filled in, the standard job error field .xam.job.error is added to the query job XSet and it's value is set to either org.snia.xam::not_a_job or org.snia.xam::unspecified_command and the query job is aborted. As shown in the sample application below, the state of a query job may be determined by examining the value of .xam.job.status.

When a query job is successful, the results are stored in the query job XSet in the form of a new XStream named xam.job.query.results. This XStream has a MIME type of application/vnd.snia.xam.query.xuid_list and contains the XUIDs for the set of XSets resulting from the evaluation of the query. Each XUID is stored in a binary format as part of an 80-byte record. If an XUID is shorter than 80 bytes, the record is zero padded to 80 bytes.

A number of other properties relating to the results XStream are also set up. The xam_int property xam.job.query.results.count contains the current count of the XUID records in the results XStream. This property is updated as results are entered into the results XStream during query processing. An application can use this to provide status information to users as the query job executes. The xam_string property xam.job.query.level indicates the query level which matches the results. Its value is either org.snia.xam.job.query.level.1 or org.snia.xam.job.query.level.2.

It is important to remember that a query job operates within the roles and permissions granted to the connection. That means the results XStream only includes those XSets that are visible and accessible to an application, at least from a read perspective, according to the role under which the query job is executed.

The query job may set the following error codes in .xam.job.error:
[table id=17/]
When a query job is halted for whatever reason, the standard job health and status fields, i.e .xam.job.errorhealth and .xam.job.status, are set to appropriate values and the XAM Storage System may place zero or more additional XUIDs in the results XStream. Thus an application should always see complete XUID values in the results XStream. Resuming a halted query job is not supported.

Note that, unlike a traditional RDBMS, there is no locking of an XAM Storage System during the execution of a query job. Thus a query result is not an instantaneous snapshot of a XAM Storage System. The contents of a XAM Storage System may or may not change while a query job is executed. The general rule is that any XSet that has been stored before the start of the query job is included in the results XStream if it meets the query criteria, and any XSet that has been stored after the query job has completed is not be included in the results XStream. XSets that are stored during the query job execution may or may not be included. The same criteria applies to the results XStream. It may include XUIDs of XSets that are no longer in the XAM Storage System and XUIDs of XSets that no longer meet the search criteria due to a change in the XSet after the particular XSet was queried.

Here is a simple Java application that queries an XAM Storage System to determine it's level of support for XAM queries.

import org.snia.xam.XAMException;
import org.snia.xam.XAMLibrary;
import org.snia.xam.XSystem;
import org.snia.xam.toolkit.XAMXUID;
import org.snia.xam.util.XAMLibraryFactory;

public class CheckQuerySupport
{
private static XAMLibrary xamLib;
private static XSystem xSystem;

public static void main(String[] args)
{
String xri = "snia-xam://centera_vim!XXX.XXXX.XXX.XXX?/home/fpm/xam/xamconnect.pea";

long exitCode = 0;
boolean isSupported = false;

try
{
xamLib = XAMLibraryFactory.newXAMLibrary();
System.out.println("Connecting to XSystem: " + xri + "\n");

xSystem = xamLib.connect(xri);

// support job commit?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_COMMIT_SUPPORTED);
System.out.println("Support job commit? " + (isSupported ? "Yes" : "No"));

// support continuance of job?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_QUERY_CONTINUANCE_SUPPORTED);
System.out.println("Support job continuance? " + (isSupported ? "Yes" : "No"));

// support level 1 query?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_QUERY_LEVEL1_SUPPORTED);
System.out.println("Support level 1 query? " + (isSupported ? "Yes" : "No"));

// support level 2 query?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_QUERY_LEVEL2_SUPPORTED);
System.out.println("Support level 2 query? " + (isSupported ? "Yes" : "No"));

xSystem.close();
System.out.println("\nClosed connection to XSystem");

} catch (XAMException xe) {
exitCode = xe.getStatusCode();
System.err.println("XAM Error occured: " + xe.getMessage());
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
exitCode = 1;
}

System.exit((int) exitCode);
}
}

Here is the output from this application when connected to an EMC Centera XAM Storage System

Connecting to XSystem: snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea

Support job commit? No
Support job continuance? No
Support level 1 query? Yes
Support level 2 query? No

Closed connection to XSystem

Here is a longer Java application that implements the three sample XSets and the sample queries which are discussed in section 10.8 of the XAM Architecture V1.0 document.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.snia.xam.XAMException;
import org.snia.xam.XAMLibrary;
import org.snia.xam.XSet;
import org.snia.xam.XStream;
import org.snia.xam.XSystem;
import org.snia.xam.XUID;
import org.snia.xam.toolkit.XAMXUID;
import org.snia.xam.util.XAMLibraryFactory;

public class QueryExample
{
private static XAMLibrary xamLib;
private static XSystem xSystem;

private static void CreateXset(int parm1, String parm2, double parm3)
throws XAMException
{
System.out.print("Create XSet 1 .... ");

XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("com.example.rhc", false, true);
xSet.createProperty("com,example.foo", false, parm1);
xSet.createProperty("com.example.bar", false, parm2);
xSet.createProperty("com.example.num", false, parm3);

XUID xuID = xSet.commit();
System.out.println("XUID: " + xuID.toString());

xSet.close();
}

private static void CreateXset(int parm1, int parm2, int parm3)
throws XAMException
{
System.out.print("Create XSet 2 .... ");

XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("com.example.rhc", false, true);
xSet.createProperty("com,example.foo", false, parm1);
xSet.createProperty("com.example.bar", false, parm2);
xSet.createProperty("com.example.num", false, parm3);

XUID xuID = xSet.commit();
System.out.println("XUID: " + xuID.toString());

xSet.close();
}

private static void CreateXset(int parm1, int parm2)
throws XAMException
{
System.out.print("Create XSet 3 .... ");

XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("com.example.rhc", false, true);
xSet.createProperty("com,example.foo", false, parm1);
xSet.createProperty("com.example.num", false, parm2);

XUID xuID = xSet.commit();
System.out.println("XUID: " + xuID.toString());

xSet.close();
}

private static void QueryXsystem(String queryString)
throws XAMException
{
boolean finished = false;
final int XAM_MAX_XUID = 80;
int resultCount = 0;
String status;

System.out.println("Create query XSet. Query string: " + queryString);

XSet query = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
query.createProperty(XSet.XAM_JOB_COMMAND, true, XSet.XAM_JOB_QUERY);

byte[] buffer = queryString.getBytes();
XStream queryStream = query.createXStream(XSet.XAM_JOB_QUERY_COMMAND, true, XAMLibrary.TEXT_PLAIN_MIME_TYPE);
queryStream.write(buffer);
queryStream.close();

System.out.println("Submit query job ....");
query.submitJob();

System.out.println("Wait for query job to finish ....");

while (!finished)
{
// check status of query job
status = query.getString(XSet.XAM_JOB_ERRORHEALTH);
if (status.equals(XSet.XAM_JOB_ERRORHEALTH_ERROR))
{
System.out.println("ERROR: Errorhealth - " + query.getString(XSet.XAM_JOB_ERROR));
query.haltJob();
System.out.println("ERROR: Job halted");
break;
}

status = query.getString(XSet.XAM_JOB_STATUS);
resultCount = (int) query.getLong(XSet.XAM_JOB_QUERY_RESULTS_COUNT);

// uncomment if you want continuous job status
// System.out.println("Job Status: " + status + " Result count: " + resultCount);

// exit loop if job complete
if (status.equals(XSet.XAM_JOB_STATUS_COMPLETE))
{
finished = true;
}
}

System.out.println("Query job completed ....");
System.out.println("Result set final count is: " + resultCount);

if (resultCount > 0)
{
// open query job result stream
XStream results = query.openXStream(XSet.XAM_JOB_QUERY_RESULTS, XStream.MODE_READ_ONLY);

byte rawXUID[] = new byte[XAM_MAX_XUID];
long bytesRead = 0;

// output XUIDs in result set
while( (bytesRead = results.read(rawXUID, XAM_MAX_XUID)) >= 0 )
{
XAMXUID resultXUID = new XAMXUID(rawXUID);
System.out.println("XUID: " + resultXUID.toString());
}

results.close();
}

query.close();
}

public static void main(String[] args)
{
String xri = "snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea";
long exitCode = 0;

try
{
xamLib = XAMLibraryFactory.newXAMLibrary();

// uncomment next 2 lines if you want extensive XAM Library logging
// xamLib.setProperty(XAMLibrary.XAM_LOG_VERBOSITY, 1);
// xamLib.setProperty(XAMLibrary.XAM_LOG_LEVEL, 5);

System.out.println("Connecting to XSystem " + xri + "\n");

xSystem = xamLib.connect(xri);
xSystem.setProperty(".xsystem.log.verbosity", 1);
xSystem.setProperty(".xsystem.log.level", 5);

CreateXset(1, "string", 123.55);
CreateXset(77, 42, 100);
CreateXset(6, 200);

xSystem.close();

System.out.println("\nClose/reopen XSystem\n");

xSystem = xamLib.connect(xri);

// uncomment one or more of the following queries
// QueryXsystem("select \".xset.xuid\"");
QueryXsystem("select \".xset.xuid\" where (\"com.example.foo\" > 0) and (\"com.example.foo\" < 50)");
// QueryXsystem("select \".xset.xuid\" where (\"com.example.bar\" > 0) and (\"com.example.bar\" < 100)");
// QueryXsystem("select \".xset.xuid\" where exists (\"com.example.bar\")");
// QueryXsystem("select \".xset.xuid\" where \"com.example.bar\" like '%ing%'");
// QueryXsystem("select \".xset.xuid\" where \"com.example.num\" >= 124");
// QueryXsystem("select \".xset.xuid\" where \"com.example.num\" >= 124.6 ");
// QueryXsystem("select \".xset.xuid\" where (\"com.example.num\" >= 123) and typeof(\"com.example.num\") = 'application/vnd.snia.xam.int'");

xSystem.close();
System.out.println("\nClosed connection to XSystem");

} catch (XAMException xe) {
exitCode = xe.getStatusCode();
System.err.println("XAM Error occured: " + xe.getMessage() + "("
+ exitCode + ")");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
exitCode = -1;
}

System.exit((int) exitCode);
}
}

Note that a results XStream produced by a query job may be consumed by an application before the XAM Storage System has finished storing all of the XUIDs in the results XStream if XStream.asyncRead is used instead of XStream.read.

Well I think I have provided enough information and examples to enable you to go play with the XAM query language by yourself. It you need help the I am sure the XAM Developer Group on Google Groups will be more than happy to help you.

In parting I would point out that since XAM Storage System providers are currently only required to implement support for queries over the content metadata, I suspect that support for actual full content-based searches using XAM may not occur for some time.
 

Linux HPET Support

IA-PC HPET (High Precision Event Timer) is a specification which was jointly developed by Intel and Microsoft in the early part of this decade.. The latest version is dated October 2004. It's stated purpose is to
initially supplement and eventually replace the legacy 8254 Programmable Interval Timer and the Real Time Clock Periodic Interrupt generation functions that are currently used as the ‘de-facto’ timer hardware for IA-PCs.


The HPET architecture defines a set of timers that can be used by the operating system. A timer block is a combination of a single counter and up to 32 comparators and match registers. The comparator compares the contents of the match register against the value of a free running monotonic up-counter. When the output of the up-counter equals the value in the match register an interrupt is generated. Each of the comparators can output an interrupt. A maximum of 8 timer blocks are supported for a total of 256 timers. Each timer block can have different clocking attributes. Specific implementations may include only a subset of these timers. A minimum of three timers is required.

The specification contains the following block diagram of the HPET architecture.

Hardware Block Diagram

Some of the timers may be enabled to generate a periodic interrupt. If a timer is set to be periodic, its period is added to the match register each time a match occurs, thus computing the next time for this timer to generate an interrupt.. An up-counter is usually 64 bits wide but 32-bit implementations are permitted by the specification and 64-bit up-counters can also be driven in 32-bit mode. Up-counters run at a minimum of 10 MHz. which is much faster than the older RTC (Real Time Clock) and can thus produce periodic interrupts at a much higher resolution. The registers associated with these timers are mapped to memory space.

The BIOS uses ACPI ( Advanced Configuration and Power Interface) functionality to inform the operating system of the location of the HPET memory-mapped register space. Here is an example of a disassembled ACPI HPET table from an Intel DX48BT2 (AKA BoneTrail) motherboard.

$ cat /sys/firmware/acpi/tables/HPET > /var/tmp/hpet.out
$ iasl -d /var/tmp/hpet.out
$ cat /var/tmp/hpet.dsl
/*
* Intel ACPI Component Architecture
* AML Disassembler version 20090123
*
* Disassembly of /var/tmp/hpet.out, Sun Jul 5 19:34:47 2009
*
* ACPI Data Table [HPET]
*
* Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue
*/

[000h 000 4] Signature : "HPET" /* High Precision Event Timer table */
[004h 004 4] Table Length : 00000038
[008h 008 1] Revision : 01
[009h 009 1] Checksum : CE
[00Ah 010 6] Oem ID : "INTEL "
[010h 016 8] Oem Table ID : "DX48BT2 "
[018h 024 4] Oem Revision : 0000076E
[01Ch 028 4] Asl Compiler ID : "MSFT"
[020h 032 4] Asl Compiler Revision : 01000013

[024h 036 4] Hardware Block ID : 8086A301

[028h 040 12] Timer Block Register :
[028h 040 1] Space ID : 00 (SystemMemory)
[029h 041 1] Bit Width : 00
[02Ah 042 1] Bit Offset : 00
[02Bh 043 1] Access Width : 00
[02Ch 044 8] Address : 00000000FED00000

[034h 052 1] Sequence Number : 00
[035h 053 2] Minimum Clock Ticks : 0001
[037h 055 1] Flags (decoded below) : 00
Page Protect : 0
4K Page Protect : 0
64K Page Protect : 0
Raw Table Data

0000: 48 50 45 54 38 00 00 00 01 CE 49 4E 54 45 4C 20 HPET8.....INTEL
0010: 44 58 34 38 42 54 32 20 6E 07 00 00 4D 53 46 54 DX48BT2 n...MSFT
0020: 13 00 00 01 01 A3 86 80 00 00 00 00 00 00 D0 FE ................
0030: 00 00 00 00 00 01 00 00 ........
$

See page 30 of the HPET v1.0a specification for a detailed breakdown of the individual bits in the Event Time Block (called Hardware Block by the AML disassember). Note that only one Event Timer Block need be described in the HPET table in order to bootstrap an operating system. This is the case here. For non-legacy platforms, the Event Timer Block described in the HPET is the one that provides functionality to replace the 8254/RTC Periodic Interrupt Logic.

Other Event Time Blocks are described in the ACPI namespace. Here is the relevant section from the disassembled ACPI DSDT table.

Device (HPET)
{
Name (_HID, EisaId ("PNP0103"))
Name (_CRS, ResourceTemplate ()
{
Memory32Fixed (ReadOnly,
0xFED00000, // Address Base
0x00004000, // Address Length
)
})
Method (_STA, 0, NotSerialized)
{
If (HPEE)
{
Return (0x0F)
}
Else
{
Return (Zero)
}
}
}

Note the assigned PNPID (PNP0103) for the HPET. Because no _UID is specified it means that there are no other HPET timer blocks.

Here is a list of the HPET-related messages outputted when this particular motherboard is booted up under Fedora 11.

$ dmesg | grep -i HPET
ACPI: HPET CFBF2000, 0038 (r1 INTEL DX48BT2 76E MSFT 1000013)
ACPI: HPET id: 0x8086a301 base: 0xfed00000
hpet clockevent registered
HPET: 4 timers in total, 0 timers will be used for per-cpu timer
hpet0: at MMIO 0xfed00000, IRQs 2, 8, 0, 0
hpet0: 4 comparators, 64-bit 14.318180 MHz counter
rtc0: alarms up to one month, 114 bytes nvram, hpet irqs
$

The first line is outputted when the ACPI HPET table is read. The second line is outputted when the ACPI HPET table is mapped into memory by .../arch/x86/kernel/acpi/boot.c. The next line is outputted when the HPET legacy interrupts are started and HPET is registered as the global clock. The following line is outputted when the kernel checks to ensure that at least one timer is reserved for userspace (/dev/hpet.) The next two lines of output comes from the HPET device driver (.../drivers/char/hpet.c.) It shows that 2 timers have allocated interrupts and two do not..

Here is the relevant part of the output from /proc/time_list as it relates to HPET:

Tick Device: mode: 1
Broadcast device
Clock Event Device: hpet
max_delta_ns: 149983005959
min_delta_ns: 5000
mult: 61496114
shift: 32
mode: 3
next_event: 9223372036854775807 nsecs
set_next_event: hpet_legacy_next_event
set_mode: hpet_legacy_set_mode
event_handler: tick_handle_oneshot_broadcast
tick_broadcast_mask: 00000000
tick_broadcast_oneshot_mask: 00000000

Here is the output from /proc/sys/dev/hpet and /proc/driver/rtc:

$ cat /proc/sys/dev/hpet/max-user-freq
64
$ cat /proc/driver/rtc
rtc_time : 06:34:31
rtc_date : 2009-07-06
alrm_time : **:24:40
alrm_date : ****-**-**
alarm_IRQ : no
alrm_pending : no
24hr : yes
periodic_IRQ : no
update_IRQ : no
HPET_emulated : yes
DST_enable : no
periodic_freq : 1024
batt_status : okay


The HPET driver (/dev/hpet) has a similar API to the Real Time Clock driver. It is a character device which can support any number of HPET devices. The kernel API has three interfaces exported from the driver:

hpet_register( struct hpet_task *tp, int periodic )
hpet_unregister( struct hpet_task *tp )
hpet_control( struct hpet_task *tp, unsigned int cmd, unsigned long arg )

The userspace interface to HPET is defined in the header /usr/include/linux/hpet.h. The current set of supported operations is:

#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */
#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */
#define HPET_INFO _IOR('h', 0x03, struct hpet_info) /* get information */
#define HPET_EPI _IO('h', 0x04) /* enable periodic */
#define HPET_DPI _IO('h', 0x05) /* disable periodic */
#define HPET_IRQFREQ _IOW('h', 0x6, unsigned long) /* set frequency */

The following example shows how to use the published interface to access a HPET and call a simple periodic signal handler hpet_alarm between 2 and 99 times a second.
#include <stdio.h>
#include <stdlib.h;>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/time.h>
#include <linux/hpet.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <signal.h>

static uint16_t hpet_sigio_count;
static uint64_t secs;

static void
hpet_alarm(int val)
{
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);

if (!secs) secs = t.tv_sec;

fprintf(stderr, "hpet_alarm called. iteration: %2d secs: %ld nsecs: %ld \n",
hpet_sigio_count, (t.tv_sec - secs) , t.tv_sec * 100000 + t.tv_nsec );

hpet_sigio_count++;
}

int
main(int argc, const char **argv)
{
struct sigaction old, new;
struct hpet_info info;
int frequency;
int iterations;
int retval = 0;
int fd;
int r, i, value;

if (argc != 3) {
fprintf(stderr, "Usage: %s frequency(1-64) iterations(10-99)\n", argv[0]);
return -1;
}

frequency = atoi(argv[1]);
iterations = atoi(argv[2]);

if (frequency > 64 || frequency < 1 ) {
fprintf(stderr, "ERROR: Invalid value for frequency\n");
return -1;
}

if (iterations < 10 || iterations > 99 ) {
fprintf(stderr, "ERROR: Invalid value for iterations\n");
return -1;
}

hpet_sigio_count = 0;

sigemptyset(&new.sa_mask);
new.sa_flags = 0;
new.sa_handler = hpet_alarm;

sigaction(SIGIO, NULL, &old);
sigaction(SIGIO, &new, NULL);

fd = open("/dev/hpet", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "ERROR: Failed to open /dev/hpet\n");
return -1;
}

if ((fcntl(fd, F_SETOWN, getpid()) == 1) ||
((value = fcntl(fd, F_GETFL)) == 1) ||
(fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) {
fprintf(stderr, "ERROR: fcntl failed\n");
retval = 1;
goto fail;
}

if (ioctl(fd, HPET_IRQFREQ, frequency) < 0) {
fprintf(stderr, "ERROR: Could not set /dev/hpet to have a %2dHz timer\n", frequency);
retval = 2;
goto fail;
}

if (ioctl(fd, HPET_INFO, &info) < 0) {
fprintf(stderr, "ERROR: failed to get info\n");
retval = 3;
goto fail;
}

fprintf(stdout, "\nhi_ireqfreq: 0x%lx hi_flags: %0x%lx hi_hpet: 0x%x hi_timer: 0x%x\n\n",
info.hi_ireqfreq, info.hi_flags, info.hi_hpet, info.hi_timer);

r = ioctl(fd, HPET_EPI, 0);
if (info.hi_flags && (r < 0)) {
fprintf(stderr, "ERROR: HPET_EPI failed\n");
retval = 4;
goto fail;
}

if (ioctl(fd, HPET_IE_ON, 0) < 0) {
fprintf(stderr, "ERROR: HPET_IE_ON failed\n");
retval = 5;
goto fail;
}

/* wait for specified number of signal interrupts */
for (i = 0; i < iterations; i++) {
(void) pause();
}

if (ioctl(fd, HPET_IE_OFF, 0) < 0) {
fprintf(stderr, "ERROR: HPET_IE_OFF failed\n");
retval = 6;
}

fail:
sigaction(SIGIO, &old, NULL);

if (fd > 0)
close(fd);

return retval;
}

Here is the output from this example when it is invoked with a frequency of 32 and an iteration count of 64.

$ sudo ./hpet_example 32 64

hi_ireqfreq: 0x20 hi_flags: 00 hi_hpet: 0x2 hi_timer: 0x4a1cb9c8

hpet_alarm called. iteration: 0 secs: 0 nsecs: 124683205055050
hpet_alarm called. iteration: 1 secs: 0 nsecs: 124683236313149
hpet_alarm called. iteration: 2 secs: 0 nsecs: 124683267566342
hpet_alarm called. iteration: 3 secs: 0 nsecs: 124683298821905
hpet_alarm called. iteration: 4 secs: 0 nsecs: 124683330077493
hpet_alarm called. iteration: 5 secs: 0 nsecs: 124683361341893
hpet_alarm called. iteration: 6 secs: 0 nsecs: 124683392590764
hpet_alarm called. iteration: 7 secs: 0 nsecs: 124683423849157
hpet_alarm called. iteration: 8 secs: 0 nsecs: 124683455101917
hpet_alarm called. iteration: 9 secs: 0 nsecs: 124683486357683
hpet_alarm called. iteration: 10 secs: 0 nsecs: 124683517617931
hpet_alarm called. iteration: 11 secs: 0 nsecs: 124683548872198
hpet_alarm called. iteration: 12 secs: 1 nsecs: 124682580229541
hpet_alarm called. iteration: 13 secs: 1 nsecs: 124682611481235
hpet_alarm called. iteration: 14 secs: 1 nsecs: 124682642740016
hpet_alarm called. iteration: 15 secs: 1 nsecs: 124682673992697
hpet_alarm called. iteration: 16 secs: 1 nsecs: 124682705247479
hpet_alarm called. iteration: 17 secs: 1 nsecs: 124682736504664
hpet_alarm called. iteration: 18 secs: 1 nsecs: 124682767758840
hpet_alarm called. iteration: 19 secs: 1 nsecs: 124682799014280
hpet_alarm called. iteration: 20 secs: 1 nsecs: 124682830270129
hpet_alarm called. iteration: 21 secs: 1 nsecs: 124682861530334
hpet_alarm called. iteration: 22 secs: 1 nsecs: 124682892784577
hpet_alarm called. iteration: 23 secs: 1 nsecs: 124682924038220
hpet_alarm called. iteration: 24 secs: 1 nsecs: 124682955294110
hpet_alarm called. iteration: 25 secs: 1 nsecs: 124682986550572
hpet_alarm called. iteration: 26 secs: 1 nsecs: 124683017805756
hpet_alarm called. iteration: 27 secs: 1 nsecs: 124683049061117
hpet_alarm called. iteration: 28 secs: 1 nsecs: 124683080318331
hpet_alarm called. iteration: 29 secs: 1 nsecs: 124683111576954
hpet_alarm called. iteration: 30 secs: 1 nsecs: 124683142828988
hpet_alarm called. iteration: 31 secs: 1 nsecs: 124683174083954
hpet_alarm called. iteration: 32 secs: 1 nsecs: 124683205337967
hpet_alarm called. iteration: 33 secs: 1 nsecs: 124683236593144
hpet_alarm called. iteration: 34 secs: 1 nsecs: 124683267851530
hpet_alarm called. iteration: 35 secs: 1 nsecs: 124683299104054
hpet_alarm called. iteration: 36 secs: 1 nsecs: 124683330358748
hpet_alarm called. iteration: 37 secs: 1 nsecs: 124683361617445
hpet_alarm called. iteration: 38 secs: 1 nsecs: 124683392870249
hpet_alarm called. iteration: 39 secs: 1 nsecs: 124683424124489
hpet_alarm called. iteration: 40 secs: 1 nsecs: 124683455379717
hpet_alarm called. iteration: 41 secs: 1 nsecs: 124683486634424
hpet_alarm called. iteration: 42 secs: 1 nsecs: 124683517889149
hpet_alarm called. iteration: 43 secs: 1 nsecs: 124683549144315
hpet_alarm called. iteration: 44 secs: 2 nsecs: 124682580500695
hpet_alarm called. iteration: 45 secs: 2 nsecs: 124682611761325
hpet_alarm called. iteration: 46 secs: 2 nsecs: 124682643011863
hpet_alarm called. iteration: 47 secs: 2 nsecs: 124682674265864
hpet_alarm called. iteration: 48 secs: 2 nsecs: 124682705521034
hpet_alarm called. iteration: 49 secs: 2 nsecs: 124682736776049
hpet_alarm called. iteration: 50 secs: 2 nsecs: 124682768030654
hpet_alarm called. iteration: 51 secs: 2 nsecs: 124682799285398
hpet_alarm called. iteration: 52 secs: 2 nsecs: 124682830544701
hpet_alarm called. iteration: 53 secs: 2 nsecs: 124682861797319
hpet_alarm called. iteration: 54 secs: 2 nsecs: 124682893051578
hpet_alarm called. iteration: 55 secs: 2 nsecs: 124682924306748
hpet_alarm called. iteration: 56 secs: 2 nsecs: 124682955562132
hpet_alarm called. iteration: 57 secs: 2 nsecs: 124682986823545
hpet_alarm called. iteration: 58 secs: 2 nsecs: 124683018073636
hpet_alarm called. iteration: 59 secs: 2 nsecs: 124683049327560
hpet_alarm called. iteration: 60 secs: 2 nsecs: 124683080586707
hpet_alarm called. iteration: 61 secs: 2 nsecs: 124683111841132
hpet_alarm called. iteration: 62 secs: 2 nsecs: 124683143095147
hpet_alarm called. iteration: 63 secs: 2 nsecs: 124683174349985
hpet_alarm called. iteration: 64 secs: 2 nsecs: 124683205607103
$

Well, I think that I have provided you with enough information so that you should now be able to go away and experiment with the HPET interface yourself.

By the way, not all VMware products support HPET. Currently ESX does not provide a virtual HPET to guest operating systems and in some cases it may be necessary to disable HPET altogether because of timer drift in virtual machines. See VMware TimeKeeping for more information.

P.S. I tested the the above example on an Intel DX48BT2 motherboard running a 2.6.29.5-191 kernel.