/* -*- linux-c -*-
*
* (C) Copyright IBM Corp. 2004-2006
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This
* file and program are licensed under a BSD style license. See
* the Copying file included with the OpenHPI distribution for
* full licensing terms.
*
* Author(s):
* Renier Morales <renier@openhpi.org>
*
*/
#include <stdio.h>
#include <string.h>
#include <oHpi.h>
#include <oh_domain.h>
#include <oh_error.h>
#include <oh_plugin.h>
#include <oh_utils.h>
#include "alarm.h"
#include "conf.h"
#include "event.h"
#include "sahpi_wrappers.h"
#define domains_lock() wrap_g_static_rec_mutex_lock(&oh_domains.lock)
#define domains_unlock() wrap_g_static_rec_mutex_unlock(&oh_domains.lock)
struct oh_domain_table oh_domains = {
.table = NULL,
#if !GLIB_CHECK_VERSION (2, 32, 0)
.lock = G_STATIC_REC_MUTEX_INIT,
#endif
};
static void __inc_domain_refcount(struct oh_domain *d)
{
wrap_g_static_rec_mutex_lock(&d->refcount_lock);
d->refcount++;
wrap_g_static_rec_mutex_unlock(&d->refcount_lock);
return;
}
static void __dec_domain_refcount(struct oh_domain *d)
{
wrap_g_static_rec_mutex_lock(&d->refcount_lock);
d->refcount--;
wrap_g_static_rec_mutex_unlock(&d->refcount_lock);
return;
}
static void __free_drt_list(GSList *drt_list)
{
GSList *node = NULL;
for (node = drt_list; node; node = node->next) {
g_free(node->data);
}
g_slist_free(drt_list);
return;
}
static void __delete_domain(struct oh_domain *d)
{
oh_flush_rpt(&d->rpt);
oh_el_close(d->del);
oh_close_alarmtable(d);
__free_drt_list(d->drt.list);
wrap_g_static_rec_mutex_free_clear(&d->lock);
wrap_g_static_rec_mutex_free_clear(&d->refcount_lock);
g_free(d);
}
#if 0
static void __query_domains(gpointer key, gpointer value, gpointer user_data)
{
oh_domain_result dr;
GList *node = (GList *)value;
struct oh_domain *domain = (struct oh_domain *)node->data;
GArray *data = (GArray *)user_data;
dr.id = domain->id;
dr.entity_pattern = domain->entity_pattern;
dr.tag = domain->tag;
g_array_append_val(data, dr);
}
#endif
static GList *__get_domain(SaHpiDomainIdT did)
{
GList *node = NULL;
struct oh_domain *domain = NULL;
if (did == SAHPI_UNSPECIFIED_DOMAIN_ID) {
did = OH_DEFAULT_DOMAIN_ID;
}
domains_lock();
node = (GList *)g_hash_table_lookup(oh_domains.table, &did);
if (!node) {
domains_unlock();
return NULL;
}
domain = (struct oh_domain *)node->data;
/* Punch in */
__inc_domain_refcount(domain);
/* Unlock domain table */
domains_unlock();
/* Wait to get domain lock */
wrap_g_static_rec_mutex_lock(&domain->lock);
return node;
}
static void gen_domain_event(SaHpiDomainIdT target_id,
SaHpiDomainIdT subject_id,
SaHpiBoolT addition)
{
struct oh_event *e = NULL;
SaHpiDomainEventTypeT type =
(addition) ? SAHPI_DOMAIN_REF_ADDED : SAHPI_DOMAIN_REF_REMOVED;
e = g_new0(struct oh_event, 1);
e->resource.ResourceId = SAHPI_UNSPECIFIED_RESOURCE_ID;
e->event.Source = target_id;
e->event.EventType = SAHPI_ET_DOMAIN;
e->event.Severity = SAHPI_INFORMATIONAL;
e->event.EventDataUnion.DomainEvent.Type = type;
e->event.EventDataUnion.DomainEvent.DomainId = subject_id;
oh_gettimeofday(&e->event.Timestamp);
DBG("domain %d %s domain %d", subject_id,
type == SAHPI_DOMAIN_REF_ADDED ? "added to" : "removed from",
target_id);
oh_evt_queue_push(oh_process_q, e);
}
static void update_drt(SaHpiDomainIdT target_id,
SaHpiDomainIdT subject_id,
SaHpiBoolT subject_is_peer,
SaHpiBoolT addition)
{
struct oh_domain *domain = NULL;
SaHpiDrtEntryT *drtentry = NULL;
int found = 0;
domain = oh_get_domain(target_id);
if (!domain) {
CRIT("Warning. Could not update DRT. Domain %u not found.",
target_id);
return;
}
if (addition) {
drtentry = g_new0(SaHpiDrtEntryT, 1);
drtentry->DomainId = subject_id;
drtentry->EntryId = ++(domain->drt.next_id);
drtentry->IsPeer = subject_is_peer;
domain->drt.list = g_slist_append(domain->drt.list, drtentry);
gen_domain_event(target_id, subject_id, SAHPI_TRUE);
} else {
GSList *node = NULL, *savenode = NULL;
int child_count = 0, peer_count = 0;
for (node = domain->drt.list;
node || savenode;
node = (node) ? node->next : savenode) {
drtentry = (SaHpiDrtEntryT *)node->data;
savenode = NULL;
if (drtentry->IsPeer) peer_count++;
else child_count++;
if (drtentry->DomainId == subject_id && !found) {
savenode = node->next;
domain->drt.list =
g_slist_delete_link(domain->drt.list,
node);
g_free(node->data);
gen_domain_event(target_id, subject_id, SAHPI_FALSE);
found = 1;
node = NULL;
}
}
}
if (addition || found) {
oh_gettimeofday(&domain->drt.update_timestamp);
domain->drt.update_count++;
}
oh_release_domain(domain);
}
#define add_drtentry(target_id, subject_id, subject_is_peer) \
update_drt(target_id, subject_id, subject_is_peer, SAHPI_TRUE);
#define del_drtentry(target_id, subject_id) \
update_drt(target_id, subject_id, SAHPI_FALSE, SAHPI_FALSE);
static int connect2parent(struct oh_domain *domain, SaHpiDomainIdT parent_id)
{
struct oh_domain *parent = NULL;
if (!domain) return -1;
if (parent_id == SAHPI_UNSPECIFIED_DOMAIN_ID) return -2;
parent = oh_get_domain(parent_id);
if (!parent) {
CRIT("Couldn't get domain %d", parent_id);
return -3;
}
/* Add child drt to peers of parent domain */
#if 0
if (parent->state & OH_DOMAIN_PEER) {
GSList *node = NULL;
for (node = parent->drt.list; node; node = node->next) {
SaHpiDrtEntryT *drte = (SaHpiDrtEntryT *)node->data;
if (drte->IsPeer) {
add_drtentry(drte->DomainId, domain->id,
SAHPI_FALSE);
}
}
}
#endif
oh_release_domain(parent);
/* Add child drt to parent domain */
add_drtentry(parent_id, domain->id, SAHPI_FALSE);
// domain->state |= OH_DOMAIN_CHILD; /* set child state */
domain->drt.parent_id = parent_id;
return 0;
}
static int connect2peer(struct oh_domain *domain, SaHpiDomainIdT peer_id)
{
struct oh_domain *peer = NULL;
GSList *node = NULL;
if (!domain) return -1;
if (peer_id == SAHPI_UNSPECIFIED_DOMAIN_ID) return -2;
peer = oh_get_domain(peer_id);
if (!peer) {
CRIT("Couldn't get domain %d", peer_id);
return -3;
}
/* Copy entitypath pattern. Peers contain the same resources */
// domain->entity_pattern = peer->entity_pattern;
/* Copy drt list from target peer.
* Also, add self drt to peers of target peer. */
for (node = peer->drt.list; node; node = node->next) {
SaHpiDrtEntryT *drtentry = (SaHpiDrtEntryT *)node->data;
add_drtentry(domain->id,
drtentry->DomainId,
drtentry->IsPeer);
if (drtentry->IsPeer) {
add_drtentry(drtentry->DomainId,
domain->id,
SAHPI_TRUE);
}
}
oh_release_domain(peer);
/* Add each others drts to domain and domain's peer */
add_drtentry(domain->id, peer_id, SAHPI_TRUE);
add_drtentry(peer_id, domain->id, SAHPI_TRUE);
// domain->state |= OH_DOMAIN_PEER;
return 0;
}
#if 0
static int disconnect_parent(struct oh_domain *child)
{
GSList *node = NULL;
struct oh_domain *parent = NULL;
if (!child) return -1;
// if (!(child->state & OH_DOMAIN_CHILD)) return -2;
parent = oh_get_domain(child->drt.parent_id);
if (!parent) return -3;
/* Remove child drt from peers of the parent */
for (node = parent->drt.list; node; node = node->next) {
SaHpiDrtEntryT *drte = (SaHpiDrtEntryT *)node->data;
if (drte->IsPeer) {
del_drtentry(drte->DomainId, child->id);
}
}
oh_release_domain(parent);
/* Finally, remove child drt from the parent */
del_drtentry(child->drt.parent_id, child->id);
// child->state = child->state & 0x06; /* Unset child state */
return 0;
}
static int disconnect_peers(struct oh_domain *domain)
{
GSList *node = NULL;
if (!domain) return -1;
// if (!(domain->state & OH_DOMAIN_PEER)) return -2;
/* Remove drt from peers */
for (node = domain->drt.list; node; node = node->next) {
SaHpiDrtEntryT *drte = (SaHpiDrtEntryT *)node->data;
if (drte->IsPeer) {
del_drtentry(drte->DomainId, domain->id);
}
}
// domain->state = domain->state & 0x03; /* Unset peer state */
return 0;
}
#endif
/**
* oh_create_domain
* @id: Required. 0 or SAHPI_UNSPECIFIED_DOMAIN_ID means default.
* @entity_pattern: Required.
* @nametag: Optional.
* @tier_of: Optional. SAHPI_UNSPECIFIED_DOMAIN_ID means none.
* @peer_of: Optional. SAHPI_UNSPECIFIED_DOMAIN_ID means none.
* @capabilities:
* @ai_timeout:
*
*
*
* Returns: SA_OK if domain was created successfully.
**/
SaErrorT oh_create_domain(SaHpiDomainIdT id,
char *tag,
SaHpiDomainIdT child_of,
SaHpiDomainIdT peer_of,
SaHpiDomainCapabilitiesT capabilities,
SaHpiTimeoutT ai_timeout)
{
struct oh_domain *domain = g_new0(struct oh_domain,1);
struct oh_global_param param = { .type = OPENHPI_DEL_SIZE_LIMIT };
char filepath[SAHPI_MAX_TEXT_BUFFER_LENGTH*2];
/* Fix id to int capable value */
if (id == SAHPI_UNSPECIFIED_DOMAIN_ID)
id = OH_DEFAULT_DOMAIN_ID;
/* Input validation */
if (peer_of == id || child_of == id ||
(child_of == peer_of && child_of != SAHPI_UNSPECIFIED_DOMAIN_ID))
return SA_ERR_HPI_INVALID_PARAMS;
/* Check to see if domain id is already taken */
domains_lock();
if (g_hash_table_lookup(oh_domains.table, &id)) {
domains_unlock();
g_free(domain);
CRIT("Domain %u already exists; not creating twice.", id);
return SA_ERR_HPI_INVALID_DOMAIN;
}
domain->id = id; /* Set domain id */
if (tag) { /* Set domain tag */
oh_init_textbuffer(&domain->tag);
oh_append_textbuffer(&domain->tag, tag);
}
domain->capabilities = capabilities;
domain->ai_timeout = ai_timeout;
/* Initialize Resource Precense Table */
oh_init_rpt(&(domain->rpt));
/* Initialize domain reference table timestamp to a valid value */
domain->drt.update_timestamp = SAHPI_TIME_UNSPECIFIED;
oh_get_global_param(¶m); /* Get domain event log size limit */
/* Initialize domain event log */
domain->del = oh_el_create(param.u.del_size_limit);
if (!domain->del) {
domains_unlock();
g_free(domain->del);
g_free(domain);
return SA_ERR_HPI_ERROR;
}
wrap_g_static_rec_mutex_init(&domain->lock);
wrap_g_static_rec_mutex_init(&domain->refcount_lock);
/* Get option for saving domain event log or not */
param.type = OPENHPI_DEL_SAVE;
oh_get_global_param(¶m);
if (param.u.del_save) {
param.type = OPENHPI_VARPATH;
oh_get_global_param(¶m);
snprintf(filepath,
SAHPI_MAX_TEXT_BUFFER_LENGTH*2,
"%s/del.%u", param.u.varpath, domain->id);
oh_el_map_from_file(domain->del, filepath);
}
param.type = OPENHPI_DAT_SAVE;
oh_get_global_param(¶m);
if (param.u.dat_save) {
param.type = OPENHPI_VARPATH;
oh_get_global_param(¶m);
memset(filepath, 0, SAHPI_MAX_TEXT_BUFFER_LENGTH*2);
snprintf(filepath,
SAHPI_MAX_TEXT_BUFFER_LENGTH*2,
"%s/dat.%u", param.u.varpath, domain->id);
oh_alarms_from_file(domain, filepath);
}
/* Need to put new domain in table before relating to other domains. */
oh_domains.list = g_list_append(oh_domains.list, domain);
g_hash_table_insert(oh_domains.table,
&domain->id,
g_list_last(oh_domains.list));
/* Establish child-parent relationship */
if (child_of != SAHPI_UNSPECIFIED_DOMAIN_ID &&
connect2parent(domain, child_of)) {
CRIT("Error connecting domain %u to parent %u",
domain->id, child_of);
}
/* Establish peer relationships */
if (peer_of != SAHPI_UNSPECIFIED_DOMAIN_ID &&
connect2peer(domain, peer_of)) {
CRIT("Error connection domain %u to peer %u",
domain->id, peer_of);
}
domains_unlock();
return SA_OK;
}
#if 0
SaErrorT oh_create_domain_from_table(GHashTable *table)
{
SaHpiDomainIdT *id = NULL, *child_of = NULL, *peer_of = NULL;
char *entity_pattern = NULL, *tag = NULL;
oh_entitypath_pattern epp;
SaHpiTimeT ai_timeout = SAHPI_TIMEOUT_IMMEDIATE, *ait = NULL;
unsigned int *ai_readonly = NULL;
SaHpiDomainCapabilitiesT capabilities =
SAHPI_DOMAIN_CAP_AUTOINSERT_READ_ONLY;
if (!table) return SA_ERR_HPI_INVALID_PARAMS;
id = (SaHpiDomainIdT *)g_hash_table_lookup(table, "id");
child_of = (SaHpiDomainIdT *)g_hash_table_lookup(table, "child_of");
peer_of = (SaHpiDomainIdT *)g_hash_table_lookup(table, "peer_of");
entity_pattern = (char *)g_hash_table_lookup(table, "entity_pattern");
tag = (char *)g_hash_table_lookup(table, "tag");
ait = (SaHpiTimeT *)g_hash_table_lookup(table, "ai_timeout");
ai_readonly = (unsigned int *)g_hash_table_lookup(table, "ai_readonly");
if (!id) {
CRIT("Error creating a domain from configuration."
" No domain id was given.");
return SA_ERR_HPI_INVALID_PARAMS;
}
if (id == 0) { /* ID == 0 is the default domain */
/* Default domain cannot be a peer or child of anyone */
child_of = NULL;
peer_of = NULL;
}
if (peer_of && entity_pattern) {
WARN("Warning creating domain %u. Entity pattern will be"
" disregarded since a peer was specified.",
*id);
} else if (!peer_of &&
oh_compile_entitypath_pattern(entity_pattern, &epp)) {
CRIT("Error creating domain %u. "
"Invalid entity pattern given.", *id);
return SA_ERR_HPI_INVALID_PARAMS;
}
if (ait) ai_timeout = *ait * 1000000000;
if (ai_readonly && *ai_readonly == 0)
capabilities = 0;
return oh_create_domain(*id, (peer_of) ? NULL : &epp, tag,
(child_of) ? *child_of : SAHPI_UNSPECIFIED_DOMAIN_ID,
(peer_of) ? *peer_of : SAHPI_UNSPECIFIED_DOMAIN_ID,
capabilities, ai_timeout);
}
#endif
/**
* oh_destroy_domain
* @did:
*
*
*
* Returns:
**/
SaErrorT oh_destroy_domain(SaHpiDomainIdT did)
{
struct oh_domain *domain = NULL;
GList *node = NULL;
if (did == OH_DEFAULT_DOMAIN_ID ||
did == SAHPI_UNSPECIFIED_DOMAIN_ID)
return SA_ERR_HPI_INVALID_PARAMS;
node = __get_domain(did);
if (!node) {
return SA_ERR_HPI_NOT_PRESENT;
}
domain = (struct oh_domain *)node->data;
#if 0
if (domain->state & OH_DOMAIN_CHILD)
disconnect_parent(domain);
if (domain->state & OH_DOMAIN_PEER)
disconnect_peers(domain);
#endif
domains_lock();
g_hash_table_remove(oh_domains.table, &domain->id);
oh_domains.list = g_list_delete_link(oh_domains.list, node);
domains_unlock();
__dec_domain_refcount(domain);
if (domain->refcount < 1)
__delete_domain(domain);
else
oh_release_domain(domain);
return SA_OK;
}
/**
* oh_get_domain
* @did:
*
*
*
* Returns:
**/
struct oh_domain *oh_get_domain(SaHpiDomainIdT did)
{
GList *node = NULL;
struct oh_domain *domain = NULL;
node = __get_domain(did);
if (!node) {
return NULL;
}
domain = (struct oh_domain *)node->data;
return domain;
}
/**
* oh_release_domain
* @domain:
*
*
*
* Returns:
**/
SaErrorT oh_release_domain(struct oh_domain *domain)
{
if (!domain) return SA_ERR_HPI_INVALID_PARAMS;
__dec_domain_refcount(domain); /* Punch out */
/*
* If domain was scheduled for destruction before, and
* no other threads are referring to it, then delete domain.
*/
if (domain->refcount < 0)
__delete_domain(domain);
else
wrap_g_static_rec_mutex_unlock(&domain->lock);
return SA_OK;
}
#if 0
/**
* oh_query_domains
*
* Fetches information on all present domains
*
* Returns: a GArray of oh_domain_result types.
**/
GArray *oh_query_domains()
{
GArray *domain_results =
g_array_new(FALSE, TRUE, sizeof(oh_domain_result));
domains_lock();
g_hash_table_foreach(oh_domains.table, __query_domains, domain_results);
domains_unlock();
return domain_results;
}
#endif
/**
* oh_drt_entry_get
* @did: a domain id
* @entryid: id of drt entry
* @nextentryid: id next to @entryid in the drt will be put here.
* @drtentry: drt entry corresponding to @entryid will be placed here.
*
* Fetches a drt entry from the domain identified by @did
*
* Returns: SA_OK on success, otherwise an error.
**/
SaErrorT oh_drt_entry_get(SaHpiDomainIdT did,
SaHpiEntryIdT entryid,
SaHpiEntryIdT *nextentryid,
SaHpiDrtEntryT *drtentry)
{
struct oh_domain *domain = NULL;
GSList *node = NULL;
if (!nextentryid || !drtentry) {
CRIT("Error - Invalid parameters passed.");
return SA_ERR_HPI_INVALID_PARAMS;
}
domain = oh_get_domain(did);
if (domain == NULL) {
CRIT("no domain for id %d", did);
return SA_ERR_HPI_INTERNAL_ERROR;
}
for (node = domain->drt.list; node; node = node->next) {
SaHpiDrtEntryT *curdrt = (SaHpiDrtEntryT *)node->data;
if (curdrt->EntryId == entryid || entryid == SAHPI_FIRST_ENTRY) {
if (node->next == NULL) { /* last entry */
*nextentryid = SAHPI_LAST_ENTRY;
} else {
SaHpiDrtEntryT *nextdrt =
(SaHpiDrtEntryT *)node->next->data;
*nextentryid = nextdrt->EntryId;
}
memcpy(drtentry, curdrt, sizeof(SaHpiDrtEntryT));
oh_release_domain(domain);
return SA_OK;
}
}
oh_release_domain(domain);
return SA_ERR_HPI_NOT_PRESENT;
}