Commit 4e8fe357 authored by Evan Hunt's avatar Evan Hunt
Browse files

create and use multiple fetch dispatches

Added API to create a set of UDP dispatches which can be shared
round-robin style when making upstream queries for authoritative
data; this should reduce lock contention in the query source
dispatch.
parent 708850fc
3315. [tuning] Use multiple dispatch objects for sending upstream
queries; this can improve performance on busy
multiprocessor systems by reducing lock contention.
[RT #28605]
3314. [bug] The masters list could be updated while refesh_callback
and stub_callback were using it. [RT #26732]
......
......@@ -1610,7 +1610,7 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
ns_cache_t *nsc;
isc_boolean_t zero_no_soattl;
dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
unsigned int query_timeout;
unsigned int query_timeout, ndisp;
struct cfg_context *nzctx;
REQUIRE(DNS_VIEW_VALID(view));
......@@ -2137,7 +2137,9 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
result = ISC_R_UNEXPECTED;
goto cleanup;
}
CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
ndisp = 4 * ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH);
CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31, ndisp,
ns_g_socketmgr, ns_g_timermgr,
resopts, ns_g_dispatchmgr,
dispatch4, dispatch6));
......
......@@ -216,7 +216,7 @@ create_view(void) {
== ISC_R_SUCCESS);
INSIST(disp6 != NULL);
RUNTIME_CHECK(dns_view_createresolver(view, taskmgr, 10,
RUNTIME_CHECK(dns_view_createresolver(view, taskmgr, 10, 1,
socketmgr,
timermgr, 0,
dispatchmgr,
......
......@@ -184,7 +184,7 @@ main(int argc, char *argv[]) {
INSIST(disp6 != NULL);
}
RUNTIME_CHECK(dns_view_createresolver(view, taskmgr, 10,
RUNTIME_CHECK(dns_view_createresolver(view, taskmgr, 10, 1,
socketmgr,
timermgr, 0,
dispatchmgr,
......
......@@ -303,7 +303,7 @@ main(int argc, char *argv[]) {
INSIST(disp6 != NULL);
}
RUNTIME_CHECK(dns_view_createresolver(view, taskmgr, 10,
RUNTIME_CHECK(dns_view_createresolver(view, taskmgr, 10, 1,
socketmgr,
timermgr, 0,
dispatchmgr,
......
......@@ -85,7 +85,7 @@ make_resolver(dns_resolver_t **resolverp) {
isc_result_t result;
result = dns_resolver_create(view,
task_manager, 1,
task_manager, 1, 1,
socket_manager,
timer_manager,
0, /* unsigned int options, */
......
......@@ -318,7 +318,7 @@ dns_client_createview(isc_mem_t *mctx, dns_rdataclass_t rdclass,
return (result);
}
result = dns_view_createresolver(view, taskmgr, ntasks, socketmgr,
result = dns_view_createresolver(view, taskmgr, ntasks, 1, socketmgr,
timermgr, 0, dispatchmgr,
dispatchv4, dispatchv6);
if (result != ISC_R_SUCCESS) {
......
......@@ -3568,6 +3568,128 @@ dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) {
isc_task_send(disp->task[0], ISC_EVENT_PTR(&newsevent));
}
dns_dispatch_t *
dns_dispatchset_get(dns_dispatchset_t *dset) {
dns_dispatch_t *disp;
/* check that dispatch set is configured */
if (dset == NULL || dset->ndisp == 0)
return (NULL);
LOCK(&dset->lock);
disp = dset->dispatches[dset->cur];
dset->cur++;
if (dset->cur == dset->ndisp)
dset->cur = 0;
UNLOCK(&dset->lock);
return (disp);
}
isc_result_t
dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr,
isc_taskmgr_t *taskmgr, dns_dispatch_t *source,
dns_dispatchset_t **dsetp, int n)
{
isc_result_t result;
dns_dispatchset_t *dset;
dns_dispatchmgr_t *mgr;
int i, j;
REQUIRE(VALID_DISPATCH(source));
REQUIRE((source->attributes & DNS_DISPATCHATTR_UDP) != 0);
REQUIRE(dsetp != NULL && *dsetp == NULL);
mgr = source->mgr;
dset = isc_mem_get(mctx, sizeof(dns_dispatchset_t));
if (dset == NULL)
return (ISC_R_NOMEMORY);
memset(dset, 0, sizeof(*dset));
result = isc_mutex_init(&dset->lock);
if (result != ISC_R_SUCCESS)
goto fail_alloc;
dset->dispatches = isc_mem_get(mctx, sizeof(dns_dispatch_t *) * n);
if (dset == NULL) {
result = ISC_R_NOMEMORY;
goto fail_lock;
}
isc_mem_attach(mctx, &dset->mctx);
dset->ndisp = n;
dset->cur = 0;
dset->dispatches[0] = NULL;
dns_dispatch_attach(source, &dset->dispatches[0]);
LOCK(&mgr->lock);
for (i = 1; i < n; i++) {
dset->dispatches[i] = NULL;
result = dispatch_createudp(mgr, sockmgr, taskmgr,
&source->local,
source->maxrequests,
source->attributes,
&dset->dispatches[i],
source->socket);
if (result != ISC_R_SUCCESS)
goto fail;
}
UNLOCK(&mgr->lock);
*dsetp = dset;
return (ISC_R_SUCCESS);
fail:
UNLOCK(&mgr->lock);
for (j = 0; j < i; j++)
dns_dispatch_detach(&(dset->dispatches[j]));
isc_mem_put(mctx, dset->dispatches, sizeof(dns_dispatch_t *) * n);
if (dset->mctx == mctx)
isc_mem_detach(&dset->mctx);
fail_lock:
DESTROYLOCK(&dset->lock);
fail_alloc:
isc_mem_put(mctx, dset, sizeof(dns_dispatchset_t));
return (result);
}
void
dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task) {
int i;
REQUIRE(dset != NULL);
for (i = 0; i < dset->ndisp; i++) {
isc_socket_t *sock;
sock = dns_dispatch_getsocket(dset->dispatches[i]);
isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
}
}
void
dns_dispatchset_destroy(dns_dispatchset_t **dsetp) {
dns_dispatchset_t *dset;
int i;
REQUIRE(dsetp != NULL && *dsetp != NULL);
dset = *dsetp;
for (i = 0; i < dset->ndisp; i++)
dns_dispatch_detach(&(dset->dispatches[i]));
isc_mem_put(dset->mctx, dset->dispatches,
sizeof(dns_dispatch_t *) * dset->ndisp);
DESTROYLOCK(&dset->lock);
isc_mem_putanddetach(&dset->mctx, dset, sizeof(dns_dispatchset_t));
*dsetp = NULL;
}
#if 0
void
dns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) {
......
......@@ -54,6 +54,7 @@
#include <isc/buffer.h>
#include <isc/lang.h>
#include <isc/mutex.h>
#include <isc/socket.h>
#include <isc/types.h>
......@@ -88,6 +89,18 @@ struct dns_dispatchevent {
isc_uint32_t attributes; /*%< mirrored from socket.h */
};
/*%
* This is a set of one or more dispatches which can be retrieved
* round-robin fashion.
*/
struct dns_dispatchset {
isc_mem_t *mctx;
dns_dispatch_t **dispatches;
int ndisp;
int cur;
isc_mutex_t lock;
};
/*@{*/
/*%
* Attributes for added dispatchers.
......@@ -505,6 +518,46 @@ dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event);
* event != NULL
*/
dns_dispatch_t *
dns_dispatchset_get(dns_dispatchset_t *dset);
/*%<
* Retrieve the next dispatch from dispatch set 'dset', and increment
* the round-robin counter.
*
* Requires:
*\li dset != NULL
*/
isc_result_t
dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr,
isc_taskmgr_t *taskmgr, dns_dispatch_t *source,
dns_dispatchset_t **dsetp, int n);
/*%<
* Given a valid dispatch 'source', create a dispatch set containing
* 'n' UDP dispatches, with the remainder filled out by clones of the
* source.
*
* Requires:
*\li source is a valid UDP dispatcher
*\li dsetp != NULL, *dsetp == NULL
*/
void
dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task);
/*%<
* Cancel socket operations for the dispatches in 'dset'.
*/
void
dns_dispatchset_destroy(dns_dispatchset_t **dsetp);
/*%<
* Dereference all the dispatches in '*dsetp', free the dispatchset
* memory, and set *dsetp to NULL.
*
* Requires:
*\li dset is valid
*/
ISC_LANG_ENDDECLS
#endif /* DNS_DISPATCH_H */
......@@ -126,7 +126,8 @@ typedef struct dns_fetchevent {
isc_result_t
dns_resolver_create(dns_view_t *view,
isc_taskmgr_t *taskmgr, unsigned int ntasks,
isc_taskmgr_t *taskmgr,
unsigned int ntasks, unsigned int ndisp,
isc_socketmgr_t *socketmgr,
isc_timermgr_t *timermgr,
unsigned int options,
......@@ -155,9 +156,11 @@ dns_resolver_create(dns_view_t *view,
*
*\li 'timermgr' is a valid timer manager.
*
*\li 'dispatchv4' is a valid dispatcher with an IPv4 UDP socket, or is NULL.
*\li 'dispatchv4' is a dispatch with an IPv4 UDP socket, or is NULL.
* If not NULL, 'ndisp' clones of it will be created by the resolver.
*
*\li 'dispatchv6' is a valid dispatcher with an IPv6 UDP socket, or is NULL.
*\li 'dispatchv6' is a dispatch with an IPv6 UDP socket, or is NULL.
* If not NULL, 'ndisp' clones of it will be created by the resolver.
*
*\li resp != NULL && *resp == NULL.
*
......
......@@ -65,6 +65,7 @@ typedef struct dns_decompress dns_decompress_t;
typedef struct dns_dispatch dns_dispatch_t;
typedef struct dns_dispatchevent dns_dispatchevent_t;
typedef struct dns_dispatchlist dns_dispatchlist_t;
typedef struct dns_dispatchset dns_dispatchset_t;
typedef struct dns_dispatchmgr dns_dispatchmgr_t;
typedef struct dns_dispentry dns_dispentry_t;
typedef struct dns_dns64 dns_dns64_t;
......
......@@ -310,7 +310,8 @@ dns_view_weakdetach(dns_view_t **targetp);
isc_result_t
dns_view_createresolver(dns_view_t *view,
isc_taskmgr_t *taskmgr, unsigned int ntasks,
isc_taskmgr_t *taskmgr,
unsigned int ntasks, unsigned int ndisp,
isc_socketmgr_t *socketmgr,
isc_timermgr_t *timermgr,
unsigned int options,
......
......@@ -374,11 +374,10 @@ struct dns_resolver {
isc_boolean_t frozen;
unsigned int options;
dns_dispatchmgr_t * dispatchmgr;
dns_dispatch_t * dispatchv4;
dns_dispatchset_t * dispatches4;
isc_boolean_t exclusivev4;
dns_dispatch_t * dispatchv6;
dns_dispatchset_t * dispatches6;
isc_boolean_t exclusivev6;
unsigned int ndisps;
unsigned int nbuckets;
fctxbucket_t * buckets;
isc_uint32_t lame_ttl;
......@@ -405,7 +404,6 @@ struct dns_resolver {
unsigned int activebuckets;
isc_boolean_t priming;
unsigned int spillat; /* clients-per-query */
unsigned int nextdisp;
/* Bad cache. */
dns_badcache_t ** badcache;
......@@ -1433,14 +1431,14 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
if (!have_addr) {
switch (pf) {
case PF_INET:
result =
dns_dispatch_getlocaladdress(res->dispatchv4,
&addr);
result = dns_dispatch_getlocaladdress(
res->dispatches4->dispatches[0],
&addr);
break;
case PF_INET6:
result =
dns_dispatch_getlocaladdress(res->dispatchv6,
&addr);
result = dns_dispatch_getlocaladdress(
res->dispatches6->dispatches[0],
&addr);
break;
default:
result = ISC_R_NOTIMPLEMENTED;
......@@ -1496,13 +1494,15 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
} else {
switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
case PF_INET:
dns_dispatch_attach(res->dispatchv4,
&query->dispatch);
dns_dispatch_attach(
dns_resolver_dispatchv4(res),
&query->dispatch);
query->exclusivesocket = res->exclusivev4;
break;
case PF_INET6:
dns_dispatch_attach(res->dispatchv6,
&query->dispatch);
dns_dispatch_attach(
dns_resolver_dispatchv6(res),
&query->dispatch);
query->exclusivesocket = res->exclusivev6;
break;
default:
......@@ -2520,9 +2520,9 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
*/
if (need_alternate != NULL &&
!*need_alternate && unshared &&
((res->dispatchv4 == NULL &&
((res->dispatches4 == NULL &&
find->result_v6 != DNS_R_NXDOMAIN) ||
(res->dispatchv6 == NULL &&
(res->dispatches6 == NULL &&
find->result_v4 != DNS_R_NXDOMAIN)))
*need_alternate = ISC_TRUE;
} else {
......@@ -2537,9 +2537,9 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
* an alternative server.
*/
if (need_alternate != NULL && !*need_alternate &&
((res->dispatchv4 == NULL &&
((res->dispatches4 == NULL &&
find->result_v6 == DNS_R_NXRRSET) ||
(res->dispatchv6 == NULL &&
(res->dispatches6 == NULL &&
find->result_v4 == DNS_R_NXRRSET)))
*need_alternate = ISC_TRUE;
dns_adb_destroyfind(&find);
......@@ -2640,9 +2640,9 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
while (sa != NULL) {
if ((isc_sockaddr_pf(sa) == AF_INET &&
fctx->res->dispatchv4 == NULL) ||
fctx->res->dispatches4 == NULL) ||
(isc_sockaddr_pf(sa) == AF_INET6 &&
fctx->res->dispatchv6 == NULL)) {
fctx->res->dispatches6 == NULL)) {
sa = ISC_LIST_NEXT(sa, link);
continue;
}
......@@ -2690,9 +2690,9 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
*/
stdoptions |= DNS_ADBFIND_AVOIDFETCHES;
}
if (res->dispatchv4 != NULL)
if (res->dispatches4 != NULL)
stdoptions |= DNS_ADBFIND_INET;
if (res->dispatchv6 != NULL)
if (res->dispatches6 != NULL)
stdoptions |= DNS_ADBFIND_INET6;
isc_stdtime_get(&now);
......@@ -2725,7 +2725,7 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
if (need_alternate) {
int family;
alternate_t *a;
family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET;
family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET;
for (a = ISC_LIST_HEAD(fctx->res->alternates);
a != NULL;
a = ISC_LIST_NEXT(a, link)) {
......@@ -7300,10 +7300,10 @@ destroy(dns_resolver_t *res) {
}
isc_mem_put(res->mctx, res->buckets,
res->nbuckets * sizeof(fctxbucket_t));
if (res->dispatchv4 != NULL)
dns_dispatch_detach(&res->dispatchv4);
if (res->dispatchv6 != NULL)
dns_dispatch_detach(&res->dispatchv6);
if (res->dispatches4 != NULL)
dns_dispatchset_destroy(&res->dispatches4);
if (res->dispatches6 != NULL)
dns_dispatchset_destroy(&res->dispatches6);
while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) {
ISC_LIST_UNLINK(res->alternates, a, link);
if (!a->isaddress)
......@@ -7393,7 +7393,8 @@ spillattimer_countdown(isc_task_t *task, isc_event_t *event) {
isc_result_t
dns_resolver_create(dns_view_t *view,
isc_taskmgr_t *taskmgr, unsigned int ntasks,
isc_taskmgr_t *taskmgr,
unsigned int ntasks, unsigned int ndisp,
isc_socketmgr_t *socketmgr,
isc_timermgr_t *timermgr,
unsigned int options,
......@@ -7415,6 +7416,7 @@ dns_resolver_create(dns_view_t *view,
REQUIRE(DNS_VIEW_VALID(view));
REQUIRE(ntasks > 0);
REQUIRE(ndisp > 0);
REQUIRE(resp != NULL && *resp == NULL);
REQUIRE(dispatchmgr != NULL);
REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL);
......@@ -7445,8 +7447,6 @@ dns_resolver_create(dns_view_t *view,
res->spillattimer = NULL;
res->zero_no_soa_ttl = ISC_FALSE;
res->query_timeout = DEFAULT_QUERY_TIMEOUT;
res->ndisps = 0;
res->nextdisp = 0; /* meaningless at this point, but init it */
res->nbuckets = ntasks;
res->activebuckets = ntasks;
res->buckets = isc_mem_get(view->mctx,
......@@ -7489,17 +7489,19 @@ dns_resolver_create(dns_view_t *view,
buckets_created++;
}
res->dispatchv4 = NULL;
res->dispatches4 = NULL;
if (dispatchv4 != NULL) {
dns_dispatch_attach(dispatchv4, &res->dispatchv4);
dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
dispatchv4, &res->dispatches4, ndisp);
dispattr = dns_dispatch_getattributes(dispatchv4);
res->exclusivev4 =
ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0);
}
res->dispatchv6 = NULL;
res->dispatches6 = NULL;
if (dispatchv6 != NULL) {
dns_dispatch_attach(dispatchv6, &res->dispatchv6);
dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
dispatchv6, &res->dispatches6, ndisp);
dispattr = dns_dispatch_getattributes(dispatchv6);
res->exclusivev6 =
ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0);
......@@ -7575,10 +7577,10 @@ dns_resolver_create(dns_view_t *view,
DESTROYLOCK(&res->lock);
cleanup_dispatches:
if (res->dispatchv6 != NULL)
dns_dispatch_detach(&res->dispatchv6);
if (res->dispatchv4 != NULL)
dns_dispatch_detach(&res->dispatchv4);
if (res->dispatches6 != NULL)
dns_dispatchset_destroy(&res->dispatches6);
if (res->dispatches4 != NULL)
dns_dispatchset_destroy(&res->dispatches4);
cleanup_buckets:
for (i = 0; i < buckets_created; i++) {
......@@ -7767,7 +7769,6 @@ void
dns_resolver_shutdown(dns_resolver_t *res) {
unsigned int i;
fetchctx_t *fctx;
isc_socket_t *sock;
isc_result_t result;
REQUIRE(VALID_RESOLVER(res));
......@@ -7786,15 +7787,13 @@ dns_resolver_shutdown(dns_resolver_t *res) {
fctx != NULL;
fctx = ISC_LIST_NEXT(fctx, link))
fctx_shutdown(fctx);
if (res->dispatchv4 != NULL && !res->exclusivev4) {
sock = dns_dispatch_getsocket(res->dispatchv4);
isc_socket_cancel(sock, res->buckets[i].task,
ISC_SOCKCANCEL_ALL);
if (res->dispatches4 != NULL && !res->exclusivev4) {
dns_dispatchset_cancelall(res->dispatches4,
res->buckets[i].task);
}
if (res->dispatchv6 != NULL && !res->exclusivev6) {
sock = dns_dispatch_getsocket(res->dispatchv6);
isc_socket_cancel(sock, res->buckets[i].task,
ISC_SOCKCANCEL_ALL);
if (res->dispatches6 != NULL && !res->exclusivev6) {
dns_dispatchset_cancelall(res->dispatches6,
res->buckets[i].task);
}
res->buckets[i].exiting = ISC_TRUE;
if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) {
......@@ -8172,13 +8171,13 @@ dns_resolver_dispatchmgr(dns_resolver_t *resolver) {
dns_dispatch_t *
dns_resolver_dispatchv4(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->dispatchv4);
return (dns_dispatchset_get(resolver->dispatches4));
}
dns_dispatch_t *
dns_resolver_dispatchv6(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->dispatchv6);
return (dns_dispatchset_get(resolver->dispatches6));
}
isc_socketmgr_t *
......
......@@ -39,13 +39,13 @@ LIBS = @LIBS@ @ATFLIBS@
OBJS = dnstest.@O@
SRCS = dnstest.c master_test.c dbiterator_test.c time_test.c \
private_test.c update_test.c zonemgr_test.c zt_test.c \
dbdiff_test.c nsec3_test.c
dbdiff_test.c nsec3_test.c dispatch_test.c
SUBDIRS =
TARGETS = master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \
private_test@EXEEXT@ update_test@EXEEXT@ zonemgr_test@EXEEXT@ \
zt_test@EXEEXT@ dbversion_test@EXEEXT@ dbdiff_test@EXEEXT@ \
nsec3_test@EXEEXT@
nsec3_test@EXEEXT@ dispatch_test@EXEEXT@
@BIND9_MAKE_RULES@
......@@ -105,6 +105,11 @@ nsec3_test@EXEEXT@: nsec3_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
nsec3_test.@O@ dnstest.@O@ ${DNSLIBS} \
${ISCLIBS} ${LIBS}
dispatch_test@EXEEXT@: dispatch_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
dispatch_test.@O@ dnstest.@O@ ${DNSLIBS} \
${ISCLIBS} ${LIBS}
unit::
sh ${top_srcdir}/unit/unittest.sh
......
/*
* Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id$ */
/*! \file */
#include <config.h>