/* * domain.c * * MontaVista IPMI code for handling IPMI domains * * Author: MontaVista Software, Inc. * Corey Minyard * source@mvista.com * * Copyright 2002,2003 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG_EVENTS static void dump_hex(const unsigned char *data, int len) { int i; for (i=0; icon_up[i]) return i; return -1; } static int first_active_con(ipmi_domain_t *domain) { int i; for (i=0; icon_up[i] && domain->con_active[i]) return i; return -1; } static int get_con_num(ipmi_domain_t *domain, ipmi_con_t *ipmi) { int u; for (u=0; uconn[u]) break; } if (u == MAX_CONS) { ipmi_log(IPMI_LOG_SEVERE, "%sdomain.c(get_con_num): " "Got a connection change from an invalid domain", DOMAIN_NAME(domain)); return -1; } return u; } static void deliver_rsp(ipmi_domain_t *domain, ipmi_addr_response_handler_t rsp_handler, ipmi_msgi_t *rspi) { int used = IPMI_MSG_ITEM_NOT_USED; if (rsp_handler) used = rsp_handler(domain, rspi); if (!used) ipmi_free_msg_item(rspi); } /*********************************************************************** * * Used for handling detecting when the domain is fully up. * **********************************************************************/ void i_ipmi_get_domain_fully_up(ipmi_domain_t *domain, const char *name) { ipmi_lock(domain->domain_lock); domain->fully_up_count++; ipmi_unlock(domain->domain_lock); } void i_ipmi_put_domain_fully_up(ipmi_domain_t *domain, const char *name) { ipmi_lock(domain->domain_lock); domain->fully_up_count--; if (domain->fully_up_count == 0) { ipmi_domain_ptr_cb domain_fully_up; void *domain_fully_up_cb_data; domain_fully_up = domain->domain_fully_up; domain_fully_up_cb_data = domain->domain_fully_up_cb_data; domain->domain_fully_up = NULL; ipmi_unlock(domain->domain_lock); i_ipmi_entities_report_mcs_scanned(domain->entities); if (domain_fully_up) domain_fully_up(domain, domain_fully_up_cb_data); return; } ipmi_unlock(domain->domain_lock); } int ipmi_domain_is_fully_up(ipmi_domain_t *domain) { return domain->fully_up_count == 0; } /*********************************************************************** * * Domain data structure creation and destruction * **********************************************************************/ static locked_list_t *domain_change_handlers; int ipmi_domain_add_domain_change_handler(ipmi_domain_change_cb handler, void *cb_data) { if (locked_list_add(domain_change_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_domain_change_handler(ipmi_domain_change_cb handler, void *cb_data) { if (locked_list_remove(domain_change_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct domain_change_info_s { enum ipmi_update_e op; ipmi_domain_t *domain; } domain_change_info_t; static int iterate_domain_changes(void *cb_data, void *item1, void *item2) { domain_change_info_t *info = cb_data; ipmi_domain_change_cb handler = item1; handler(info->domain, info->op, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_domain_change(ipmi_domain_t *domain, enum ipmi_update_e op) { domain_change_info_t info = { op, domain }; locked_list_iterate(domain_change_handlers, iterate_domain_changes, &info); } int i_ipmi_domain_in_shutdown(ipmi_domain_t *domain) { return domain->in_shutdown; } static void iterate_cleanup_mc(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { i_ipmi_cleanup_mc(mc); } void ipmi_domain_set_oem_shutdown_handler(ipmi_domain_t *domain, ipmi_domain_shutdown_cb handler) { domain->shutdown_handler = handler; } static int destroy_attr(void *cb_data, void *item1, void *item2); static int destroy_stat(void *cb_data, void *item1, void *item2); static void call_mc_upd_cl_handlers(ipmi_domain_t *domain, ipmi_domain_mc_upd_cb handler, void *handler_data); static void call_con_change_cl_handlers(ipmi_domain_t *domain, ipmi_domain_con_cb handler, void *handler_data); static void call_event_handler_cl_handlers(ipmi_domain_t *domain, ipmi_event_handler_cb handler, void *handler_data); static int mc_upds_cleanup(void *cb_data, void *item1, void *item2) { call_mc_upd_cl_handlers(cb_data, item1, item2); return LOCKED_LIST_ITER_CONTINUE; } static int con_change_cleanup(void *cb_data, void *item1, void *item2) { call_con_change_cl_handlers(cb_data, item1, item2); return LOCKED_LIST_ITER_CONTINUE; } static int event_handler_cleanup(void *cb_data, void *item1, void *item2) { call_event_handler_cl_handlers(cb_data, item1, item2); return LOCKED_LIST_ITER_CONTINUE; } static void cleanup_domain(ipmi_domain_t *domain) { unsigned int i; int rv; /* This must be first, so that nuking the oustanding messages will cause the right thing to happen. */ cancel_domain_oem_check(domain); if (domain->attr) { locked_list_iterate(domain->attr, destroy_attr, domain); locked_list_destroy(domain->attr); domain->attr = NULL; } if (domain->stats) { locked_list_iterate(domain->stats, destroy_stat, domain); locked_list_destroy(domain->stats); domain->stats = NULL; } /* Nuke all outstanding messages. */ if ((domain->cmds_lock) && (domain->cmds)) { ll_msg_t *nmsg; int ok; ilist_iter_t iter; ipmi_lock(domain->cmds_lock); ilist_init_iter(&iter, domain->cmds); ok = ilist_first(&iter); while (ok) { ipmi_msgi_t *rspi; nmsg = ilist_get(&iter); rspi = nmsg->rsp_item; rspi->msg.netfn = nmsg->msg.netfn | 1; rspi->msg.cmd = nmsg->msg.cmd; rspi->msg.data = rspi->data; rspi->msg.data_len = 1; rspi->msg.data[0] = IPMI_UNKNOWN_ERR_CC; deliver_rsp(domain, nmsg->rsp_handler, rspi); ilist_delete(&iter); ipmi_mem_free(nmsg); ok = ilist_first(&iter); } ipmi_unlock(domain->cmds_lock); } if (domain->cmds_lock) ipmi_destroy_lock(domain->cmds_lock); if (domain->cmds) free_ilist(domain->cmds); /* Shutdown code called here. */ if (domain->shutdown_handler) domain->shutdown_handler(domain); /* Delete the sensors from the main SDR repository. */ if (domain->sensors_in_main_sdr) { for (i=0; isensors_in_main_sdr_count; i++) { i_ipmi_domain_entity_lock(domain); if (domain->sensors_in_main_sdr[i]) { ipmi_sensor_t *sensor = domain->sensors_in_main_sdr[i]; ipmi_entity_t *entity = ipmi_sensor_get_entity(sensor); ipmi_mc_t *mc = ipmi_sensor_get_mc(sensor); i_ipmi_entity_get(entity); i_ipmi_sensor_get(sensor); i_ipmi_domain_entity_unlock(domain); i_ipmi_domain_mc_lock(domain); i_ipmi_mc_get(mc); i_ipmi_domain_mc_unlock(domain); ipmi_sensor_destroy(domain->sensors_in_main_sdr[i]); i_ipmi_sensor_put(sensor); i_ipmi_mc_put(mc); i_ipmi_entity_put(entity); } else i_ipmi_domain_entity_unlock(domain); } ipmi_mem_free(domain->sensors_in_main_sdr); } if (domain->entities_in_main_sdr) { ipmi_sdr_entity_destroy(domain->entities_in_main_sdr); domain->entities_in_main_sdr = NULL; } if (domain->activate_timer_info) { if (domain->activate_timer_info->lock) { ipmi_lock(domain->activate_timer_info->lock); if (domain->activate_timer) { int arv = 0; if (domain->activate_timer_info->running) arv = domain->os_hnd->stop_timer(domain->os_hnd, domain->activate_timer); if (!arv) { /* If we can stop the timer, free it and it's info. If we can't stop the timer, that means that the code is currently in the timer handler, so we let the "cancelled" value do this for us. */ domain->os_hnd->free_timer(domain->os_hnd, domain->activate_timer); ipmi_unlock(domain->activate_timer_info->lock); ipmi_destroy_lock(domain->activate_timer_info->lock); ipmi_mem_free(domain->activate_timer_info); } else { domain->activate_timer_info->cancelled = 1; ipmi_unlock(domain->activate_timer_info->lock); } } else { ipmi_unlock(domain->activate_timer_info->lock); ipmi_destroy_lock(domain->activate_timer_info->lock); } } else { ipmi_mem_free(domain->activate_timer_info); } } /* We cleanup the MCs twice. Some MCs may not be destroyed (but only left inactive) in the first pass due to references form other MCs SDR repositories. The second pass will get them all. */ ipmi_domain_iterate_mcs(domain, iterate_cleanup_mc, NULL); ipmi_domain_iterate_mcs(domain, iterate_cleanup_mc, NULL); if (domain->si_mc) { i_ipmi_mc_get(domain->si_mc); i_ipmi_mc_release(domain->si_mc); i_ipmi_cleanup_mc(domain->si_mc); i_ipmi_mc_put(domain->si_mc); } /* Destroy the main SDR repository, if it exists. */ if (domain->main_sdrs) ipmi_sdr_info_destroy(domain->main_sdrs, NULL, NULL); if (domain->audit_domain_timer_info) { domain->audit_domain_timer_info->cancelled = 1; ipmi_lock(domain->audit_domain_timer_info->lock); rv = domain->os_hnd->stop_timer(domain->os_hnd, domain->audit_domain_timer); ipmi_unlock(domain->audit_domain_timer_info->lock); if (!rv) { /* If we can stop the timer or it wasn't running, free it and it's info. If we can't stop the timer, that means that the code is currently in the timer handler, so we let the "cancelled" value do this for us. */ if (domain->audit_domain_timer) domain->os_hnd->free_timer(domain->os_hnd, domain->audit_domain_timer); if (domain->audit_domain_timer_info->lock) ipmi_destroy_lock(domain->audit_domain_timer_info->lock); ipmi_mem_free(domain->audit_domain_timer_info); } } if (domain->event_handlers) { locked_list_iterate(domain->event_handlers, event_handler_cleanup, domain); locked_list_destroy(domain->event_handlers); } if (domain->event_handlers_cl) locked_list_destroy(domain->event_handlers_cl); if (domain->con_change_handlers) { locked_list_iterate(domain->con_change_handlers, con_change_cleanup, domain); locked_list_destroy(domain->con_change_handlers); } if (domain->con_change_cl_handlers) locked_list_destroy(domain->con_change_cl_handlers); if (domain->new_sensor_handlers) locked_list_destroy(domain->new_sensor_handlers); if (domain->ipmb_ignores) { ilist_iter_t iter; ilist_init_iter(&iter, domain->ipmb_ignores); while (ilist_first(&iter)) { ilist_delete(&iter); } free_ilist(domain->ipmb_ignores); } if (domain->bus_scans_running) { mc_ipmb_scan_info_t *item; while (domain->bus_scans_running) { item = domain->bus_scans_running; domain->bus_scans_running = item->next; ipmi_lock(item->lock); if (item->timer_running) { if (item->os_hnd->stop_timer(item->os_hnd, item->timer)) { item->cancelled = 1; ipmi_unlock(item->lock); item = NULL; } } if (item) { ipmi_unlock(item->lock); item->os_hnd->free_timer(item->os_hnd, item->timer); ipmi_destroy_lock(item->lock); ipmi_mem_free(item); } } } /* Destroy the entities last, since sensors and controls may refer to them. */ if (domain->entities) ipmi_entity_info_destroy(domain->entities); if (domain->entities_lock) ipmi_destroy_lock(domain->entities_lock); call_domain_change(domain, IPMI_DELETED); /* The MC list should no longer have anything in it. */ if (domain->mc_upd_handlers) { locked_list_iterate(domain->mc_upd_handlers, mc_upds_cleanup, domain); locked_list_destroy(domain->mc_upd_handlers); } if (domain->mc_upd_cl_handlers) locked_list_destroy(domain->mc_upd_cl_handlers); for (i=0; iipmb_mcs[i].mcs) ipmi_mem_free(domain->ipmb_mcs[i].mcs); } /* We wait until here to call the OEM data destroyer, the process of destroying information that has previously gone on can call OEM callbacks, we want the OEM data to hang around until we don't need it for sure. */ if (domain->oem_data && domain->oem_data_destroyer) domain->oem_data_destroyer(domain, domain->oem_data); if (domain->con_stat_info) ipmi_ll_con_free_stat_info(domain->con_stat_info); /* Locks must be last, because they can be used by many things. */ if (domain->ipmb_ignores_lock) ipmi_destroy_lock(domain->ipmb_ignores_lock); if (domain->mc_lock) ipmi_destroy_lock(domain->mc_lock); if (domain->con_lock) ipmi_destroy_lock(domain->con_lock); if (domain->domain_lock) ipmi_destroy_lock(domain->domain_lock); /* Cruft */ free_domain_cruft(domain); ipmi_mem_free(domain); } static int con_register_stat(ipmi_ll_stat_info_t *info, const char *name, const char *instance, void **stat) { ipmi_domain_stat_t *rstat = NULL; int rv; ipmi_domain_t *domain = ipmi_ll_con_stat_get_user_data(info); rv = ipmi_domain_stat_register(domain, name, instance, &rstat); if (!rv) *stat = rstat; return rv; } static void con_add_stat(ipmi_ll_stat_info_t *info, void *stat, int value) { ipmi_domain_stat_add(stat, value); } static void con_unregister_stat(ipmi_ll_stat_info_t *info, void *stat) { ipmi_domain_stat_put(stat); } static int process_options(ipmi_domain_t *domain, ipmi_open_option_t *options, unsigned int num_options) { unsigned int i; /* Option processing. */ for (i=0; ioption_all = options[i].ival != 0; break; case IPMI_OPEN_OPTION_SDRS: domain->option_SDRs = options[i].ival != 0; break; case IPMI_OPEN_OPTION_FRUS: domain->option_FRUs = options[i].ival != 0; break; case IPMI_OPEN_OPTION_SEL: domain->option_SEL = options[i].ival != 0; break; case IPMI_OPEN_OPTION_IPMB_SCAN: domain->option_IPMB_scan = options[i].ival != 0; break; case IPMI_OPEN_OPTION_OEM_INIT: domain->option_OEM_init = options[i].ival != 0; break; case IPMI_OPEN_OPTION_SET_EVENT_RCVR: domain->option_set_event_rcvr = options[i].ival != 0; break; case IPMI_OPEN_OPTION_SET_SEL_TIME: domain->option_set_sel_time = options[i].ival != 0; break; case IPMI_OPEN_OPTION_USE_CACHE: domain->option_use_cache = options[i].ival != 0; break; case IPMI_OPEN_OPTION_ACTIVATE_IF_POSSIBLE: domain->option_activate_if_possible = options[i].ival != 0; break; case IPMI_OPEN_OPTION_LOCAL_ONLY: domain->option_local_only = options[i].ival != 0; domain->option_local_only_set = 1; break; default: return EINVAL; } } return 0; } static int setup_domain(const char *name, ipmi_con_t *ipmi[], int num_con, ipmi_open_option_t *options, unsigned int num_options, ipmi_domain_t **new_domain) { struct timeval timeout; ipmi_domain_t *domain; int rv; ipmi_system_interface_addr_t si; int i, j; unsigned int priv; /* Don't allow '(' in the domain name, as that messes up the naming. That is the only restriction. */ if (strchr(name, '(')) return EINVAL; domain = ipmi_mem_alloc(sizeof(*domain)); if (!domain) return ENOMEM; memset(domain, 0, sizeof(*domain)); domain->in_startup = 1; domain->option_all = 1; domain->option_set_event_rcvr = 1; domain->option_set_sel_time = 1; domain->option_activate_if_possible = 1; domain->option_local_only = 0; domain->option_local_only_set = 0; domain->option_use_cache = 1; priv = IPMI_PRIVILEGE_ADMIN; for (i=0; ipriv_level != 0) && (ipmi[i]->priv_level < priv)) priv = ipmi[i]->priv_level; } /* Enable setting the event receiver (by default) if the privilege is admin or greater. */ domain->option_set_event_rcvr = (priv >= IPMI_PRIVILEGE_ADMIN); domain->option_set_sel_time = (priv >= IPMI_PRIVILEGE_ADMIN); if (options) process_options(domain, options, num_options); strncpy(domain->name, name, sizeof(domain->name)-2); i = strlen(domain->name); if (i > 0) { domain->name[i] = ' '; domain->name[i+1] = '\0'; } domain->os_hnd = ipmi[0]->os_hnd; domain->valid = 1; domain->in_shutdown = 0; domain->usecount = 1; domain->stats = locked_list_alloc(domain->os_hnd); if (!domain->stats) { ipmi_mem_free(domain); return ENOMEM; } domain->con_stat_info = ipmi_ll_con_alloc_stat_info(); if (!domain->con_stat_info) { locked_list_destroy(domain->stats); ipmi_mem_free(domain); return ENOMEM; } ipmi_ll_con_stat_info_set_register(domain->con_stat_info, con_register_stat); ipmi_ll_con_stat_info_set_adder(domain->con_stat_info, con_add_stat); ipmi_ll_con_stat_info_set_unregister(domain->con_stat_info, con_unregister_stat); ipmi_ll_con_stat_set_user_data(domain->con_stat_info, domain); for (i=0; iname); domain->conn[i] = ipmi[i]; for (j=0; jcon_ipmb_addr[i][j] = 0x20; domain->con_active[i] = 1; domain->con_up[i] = 0; ipmi[i]->name = ipmi_mem_alloc(len1 + 11); if (ipmi[i]->name) snprintf(ipmi[i]->name, len1 + 11, "%s%d ", domain->name, i); ipmi[i]->user_data = domain; for (j=0; jport_up[j][i] = -1; if (ipmi[i]->register_stat_handler) ipmi[i]->register_stat_handler(ipmi[i], domain->con_stat_info); } domain->connection_up = 0; /* Create the locks before anything else. */ domain->default_sel_rescan_time = IPMI_SEL_QUERY_INTERVAL; /* Set the default timer intervals. */ domain->audit_domain_interval = IPMI_AUDIT_DOMAIN_INTERVAL; rv = ipmi_create_lock(domain, &domain->mc_lock); if (rv) goto out_err; rv = ipmi_create_lock(domain, &domain->con_lock); if (rv) goto out_err; rv = ipmi_create_lock(domain, &domain->domain_lock); if (rv) goto out_err; rv = ipmi_create_lock(domain, &domain->entities_lock); if (rv) goto out_err; domain->activate_timer_info = ipmi_mem_alloc(sizeof(activate_timer_info_t)); if (!domain->activate_timer_info) { rv = ENOMEM; goto out_err; } domain->activate_timer_info->lock = NULL; domain->activate_timer_info->domain = domain; domain->activate_timer_info->cancelled = 0; domain->activate_timer_info->os_hnd = domain->os_hnd; domain->activate_timer_info->running = 0; rv = ipmi_create_lock(domain, &domain->activate_timer_info->lock); if (rv) goto out_err; rv = domain->os_hnd->alloc_timer(domain->os_hnd, &(domain->activate_timer)); if (rv) goto out_err; domain->event_handlers_cl = locked_list_alloc(domain->os_hnd); if (!domain->event_handlers_cl) { rv = ENOMEM; goto out_err; } domain->event_handlers = locked_list_alloc(domain->os_hnd); if (!domain->event_handlers) { rv = ENOMEM; goto out_err; } domain->attr = locked_list_alloc(domain->os_hnd); if (!domain->attr) { rv = ENOMEM; goto out_err; } domain->do_bus_scan = 1; si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si.channel = IPMI_BMC_CHANNEL; si.lun = 0; rv = i_ipmi_create_mc(domain, (ipmi_addr_t *) &si, sizeof(si), &domain->si_mc); if (rv) goto out_err; i_ipmi_mc_use(domain->si_mc); /* Force this one to always be active, so anything that uses it is always ready to go. Since it represents the connection, it really can't ever go inactive. */ i_ipmi_mc_force_active(domain->si_mc, 1); rv = ipmi_sdr_info_alloc(domain, domain->si_mc, 0, 0, &domain->main_sdrs); if (rv) goto out_err; rv = ipmi_create_lock(domain, &domain->cmds_lock); if (rv) goto out_err; domain->cmds = alloc_ilist(); if (! domain->cmds) { rv = ENOMEM; goto out_err; } domain->con_change_cl_handlers = locked_list_alloc(domain->os_hnd); if (! domain->con_change_cl_handlers) { rv = ENOMEM; goto out_err; } domain->con_change_handlers = locked_list_alloc(domain->os_hnd); if (! domain->con_change_handlers) { rv = ENOMEM; goto out_err; } domain->mc_upd_cl_handlers = locked_list_alloc(domain->os_hnd); if (! domain->mc_upd_cl_handlers) { rv = ENOMEM; goto out_err; } domain->mc_upd_handlers = locked_list_alloc(domain->os_hnd); if (! domain->mc_upd_handlers) { rv = ENOMEM; goto out_err; } domain->new_sensor_handlers = locked_list_alloc(domain->os_hnd); if (! domain->new_sensor_handlers) { rv = ENOMEM; goto out_err; } rv = ipmi_create_lock(domain, &domain->ipmb_ignores_lock); if (rv) goto out_err; domain->ipmb_ignores = alloc_ilist(); if (! domain->ipmb_ignores) { rv = ENOMEM; goto out_err; } domain->bus_scans_running = NULL; domain->audit_domain_timer_info = ipmi_mem_alloc(sizeof(audit_domain_info_t)); if (!domain->audit_domain_timer_info) { rv = ENOMEM; goto out_err; } memset(domain->audit_domain_timer_info, 0, sizeof(audit_domain_info_t)); domain->audit_domain_timer_info->domain = domain; domain->audit_domain_timer_info->os_hnd = domain->os_hnd; domain->audit_domain_timer_info->cancelled = 0; rv = ipmi_create_lock(domain, &domain->audit_domain_timer_info->lock); if (rv) goto out_err; rv = domain->os_hnd->alloc_timer(domain->os_hnd, &(domain->audit_domain_timer)); if (rv) goto out_err; timeout.tv_sec = domain->audit_domain_interval; timeout.tv_usec = 0; domain->os_hnd->start_timer(domain->os_hnd, domain->audit_domain_timer, &timeout, domain_audit, domain->audit_domain_timer_info); rv = ipmi_entity_info_alloc(domain, &(domain->entities)); if (rv) goto out_err; memset(domain->chan, 0, sizeof(domain->chan)); out_err: if (domain->si_mc) i_ipmi_mc_put(domain->si_mc); if (rv) { for (i=0; iregister_stat_handler) ipmi[i]->unregister_stat_handler(ipmi[i], domain->con_stat_info); } cleanup_domain(domain); } else *new_domain = domain; return rv; } /*********************************************************************** * * Locking handling * **********************************************************************/ #ifdef IPMI_CHECK_LOCKS void i__ipmi_check_domain_lock(const ipmi_domain_t *domain) { if (!domain) return; if (!DEBUG_LOCKS) return; if (domain->usecount == 0) ipmi_report_lock_error(domain->os_hnd, "domain not locked when it should have been"); } #endif void i_ipmi_domain_entity_lock(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->entities_lock); } void i_ipmi_domain_entity_unlock(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); ipmi_unlock(domain->entities_lock); } void i_ipmi_domain_mc_lock(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->mc_lock); } void i_ipmi_domain_mc_unlock(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); ipmi_unlock(domain->mc_lock); } /*********************************************************************** * * Domain validation * **********************************************************************/ /* A open hash table of all the registered domains. */ #define DOMAIN_HASH_SIZE 128 static ipmi_domain_t *domains[DOMAIN_HASH_SIZE]; static ipmi_lock_t *domains_lock; static int domains_initialized = 0; static void add_known_domain(ipmi_domain_t *domain) { unsigned int hash = ipmi_hash_pointer(domain) % DOMAIN_HASH_SIZE; ipmi_lock(domains_lock); domain->prev = NULL; domain->next = domains[hash]; if (domains[hash]) domains[hash]->prev = domain; domains[hash] = domain; ipmi_unlock(domains_lock); } static void remove_known_domain(ipmi_domain_t *domain) { ipmi_lock(domains_lock); if (domain->next) domain->next->prev = domain->prev; if (domain->prev) domain->prev->next = domain->next; else { unsigned int hash = ipmi_hash_pointer(domain) % DOMAIN_HASH_SIZE; domains[hash] = domain->next; } ipmi_unlock(domains_lock); } /* Validate that the domain and it's underlying connection is valid and increment its use count. */ int i_ipmi_domain_get(ipmi_domain_t *domain) { unsigned int hash = ipmi_hash_pointer(domain) % DOMAIN_HASH_SIZE; ipmi_domain_t *c; int rv = 0; if (!domains_initialized) return ECANCELED; ipmi_lock(domains_lock); c = domains[hash]; while (c != NULL) { if (c == domain) break; c = c->next; } if (c == NULL) { rv = EINVAL; goto out; } /* We do this check after we find the domain in the list, because want to make sure the pointer is good before we do this. */ if (!domain->valid) { rv = EINVAL; goto out; } domain->usecount++; out: ipmi_unlock(domains_lock); return rv; } void i_ipmi_domain_put(ipmi_domain_t *domain) { ipmi_lock(domains_lock); if ((domain->usecount == 1) && (domain->in_shutdown)) { ipmi_unlock(domains_lock); /* The domain has been destroyed, finish the process. */ real_close_connection(domain); return; } domain->usecount--; ipmi_unlock(domains_lock); } /*********************************************************************** * * Handle global OEM callbacks new domains. * **********************************************************************/ typedef struct oem_handlers_s { ipmi_domain_oem_check check; void *cb_data; } oem_handlers_t; /* FIXME - do we need a lock? Probably, add it. */ static ilist_t *oem_handlers; int ipmi_register_domain_oem_check(ipmi_domain_oem_check check, void *cb_data) { oem_handlers_t *new_item; new_item = ipmi_mem_alloc(sizeof(*new_item)); if (!new_item) return ENOMEM; new_item->check = check; new_item->cb_data = cb_data; if (! ilist_add_tail(oem_handlers, new_item, NULL)) { ipmi_mem_free(new_item); return ENOMEM; } return 0; } static int oem_handler_cmp(void *item, void *cb_data) { oem_handlers_t *hndlr = item; oem_handlers_t *cmp = cb_data; return ((hndlr->check == cmp->check) && (hndlr->cb_data == cmp->cb_data)); } int ipmi_deregister_domain_oem_check(ipmi_domain_oem_check check, void *cb_data) { oem_handlers_t *hndlr; oem_handlers_t tmp; ilist_iter_t iter; tmp.check = check; tmp.cb_data = cb_data; ilist_init_iter(&iter, oem_handlers); ilist_unpositioned(&iter); hndlr = ilist_search_iter(&iter, oem_handler_cmp, &tmp); if (hndlr) { ilist_delete(&iter); ipmi_mem_free(hndlr); return 0; } return ENOENT; } struct domain_check_oem_s { int cancelled; ipmi_domain_oem_check_done done; void *cb_data; oem_handlers_t *curr_handler; }; static void domain_oem_check_done(ipmi_domain_t *domain, int err, void *cb_data); static void start_oem_domain_check(ipmi_domain_t *domain, domain_check_oem_t *check) { ilist_iter_t iter; ilist_init_iter(&iter, oem_handlers); if (!ilist_first(&iter)) { /* Empty list, just go on */ check->done(domain, 0, check->cb_data); ipmi_mem_free(check); goto out; } else { oem_handlers_t *h = ilist_get(&iter); int rv = ENOSYS; while (rv) { check->curr_handler = h; rv = h->check(domain, domain_oem_check_done, check); if (!rv) break; if (rv != ENOSYS) break; if (!ilist_next(&iter)) { /* End of list, just go on */ check->done(domain, 0, check->cb_data); ipmi_mem_free(check); goto out; } h = ilist_get(&iter); } if (rv) { if (rv == ENOSYS) /* This just means that we didn't match anything. */ rv = 0; /* We didn't get a check to start, just give up. */ check->done(domain, rv, check->cb_data); ipmi_mem_free(check); } } out: return; } static int oem_handler_cmp2(void *item, void *cb_data) { oem_handlers_t *hndlr = item; oem_handlers_t *cmp = cb_data; return (hndlr == cmp); } static void next_oem_domain_check(ipmi_domain_t *domain, domain_check_oem_t *check) { oem_handlers_t *h; ilist_iter_t iter; /* We can't keep an interater in the check, because the list may change during execution. */ ilist_init_iter(&iter, oem_handlers); ilist_unpositioned(&iter); h = ilist_search_iter(&iter, oem_handler_cmp2, check->curr_handler); if (!h) { /* The current handler we were working on went away, start over. */ start_oem_domain_check(domain, check); } else { int rv = 1; while (rv) { if (!ilist_next(&iter)) { /* End of list, just go on */ check->done(domain, 0, check->cb_data); ipmi_mem_free(check); goto out; } h = ilist_get(&iter); check->curr_handler = h; rv = h->check(domain, domain_oem_check_done, check); } if (rv) { /* We didn't get a check to start, just give up. */ check->done(domain, 0, check->cb_data); ipmi_mem_free(check); } } out: return; } static void domain_oem_check_done(ipmi_domain_t *domain, int err, void *cb_data) { domain_check_oem_t *check = cb_data; if (check->cancelled) { check->done(NULL, ECANCELED, check->cb_data); ipmi_mem_free(check); return; } if (err != ENOSYS) { /* Either we got a success or some error trying to install the OEM handlers. */ check->done(domain, err, check->cb_data); ipmi_mem_free(check); return; } next_oem_domain_check(domain, check); } static int check_oem_handlers(ipmi_domain_t *domain, ipmi_domain_oem_check_done done, void *cb_data) { domain_check_oem_t *check; check = ipmi_mem_alloc(sizeof(*check)); if (!check) return ENOMEM; check->done = done; check->cb_data = cb_data; check->cancelled = 0; start_oem_domain_check(domain, check); return 0; } static void cancel_domain_oem_check(ipmi_domain_t *domain) { if (domain->check) domain->check->cancelled = 1; } /*********************************************************************** * * FRU data handling * **********************************************************************/ int i_ipmi_domain_fru_set_special_setup(ipmi_domain_t *domain, i_ipmi_domain_fru_setup_cb setup, void *cb_data) { domain->fru_setup_cb = setup; domain->fru_setup_cb_data = cb_data; return 0; } int i_ipmi_domain_fru_call_special_setup(ipmi_domain_t *domain, unsigned char is_logical, unsigned char device_address, unsigned char device_id, unsigned char lun, unsigned char private_bus, unsigned char channel, ipmi_fru_t *fru) { if (!domain->fru_setup_cb) return 0; return domain->fru_setup_cb(domain, is_logical, device_address, device_id, lun, private_bus, channel, fru, domain->fru_setup_cb_data); } /*********************************************************************** * * MC handling * **********************************************************************/ #define HASH_SLAVE_ADDR(x) (((x) >> 1) & (IPMB_HASH-1)) ipmi_mc_t * i_ipmi_find_mc_by_addr(ipmi_domain_t *domain, const ipmi_addr_t *addr, unsigned int addr_len) { ipmi_mc_t *mc = NULL; if (addr_len > sizeof(ipmi_addr_t)) return NULL; ipmi_lock(domain->mc_lock); if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { if (addr->channel == IPMI_BMC_CHANNEL) mc = domain->si_mc; else if (addr->channel < MAX_CONS) mc = domain->sys_intf_mcs[addr->channel]; } else if (addr->addr_type == IPMI_IPMB_ADDR_TYPE) { const ipmi_ipmb_addr_t *ipmb = (ipmi_ipmb_addr_t *) addr; int idx; const mc_table_t *tab; ipmi_addr_t addr2; unsigned int addr2_len; int i; if (addr_len >= sizeof(*ipmb)) { idx = HASH_SLAVE_ADDR(ipmb->slave_addr); tab = &(domain->ipmb_mcs[idx]); for (i=0; isize; i++) { if (tab->mcs[i]) { ipmi_mc_get_ipmi_address(tab->mcs[i], &addr2, &addr2_len); if (ipmi_addr_equal_nolun(addr, addr_len, &addr2, addr2_len)) { mc = tab->mcs[i]; break; } } } } } /* If we cannot get the MC, it has been destroyed. */ if (mc) { if (i_ipmi_mc_get(mc)) mc = NULL; } ipmi_unlock(domain->mc_lock); return mc; } static int in_ipmb_ignores(ipmi_domain_t *domain, unsigned char channel, unsigned char ipmb_addr) { unsigned long addr; unsigned char first, last, ichan; ilist_iter_t iter; int rv = 0; ipmi_lock(domain->ipmb_ignores_lock); ilist_init_iter(&iter, domain->ipmb_ignores); ilist_unpositioned(&iter); while (ilist_next(&iter)) { addr = (unsigned long) ilist_get(&iter); first = addr & 0xff; last = (addr >> 8) & 0xff; ichan = (addr >> 16) & 0xff; if ((ichan == channel) && (ipmb_addr >= first) && (ipmb_addr <= last)) rv = 1; } ipmi_unlock(domain->ipmb_ignores_lock); return rv; } int ipmi_domain_add_ipmb_ignore(ipmi_domain_t *domain, unsigned char channel, unsigned char ipmb_addr) { unsigned long addr = ipmb_addr | (ipmb_addr << 8) | (channel << 16); int rv = 0; ipmi_lock(domain->ipmb_ignores_lock); if (! ilist_add_tail(domain->ipmb_ignores, (void *) addr, NULL)) rv = ENOMEM; ipmi_unlock(domain->ipmb_ignores_lock); return rv; } int ipmi_domain_add_ipmb_ignore_range(ipmi_domain_t *domain, unsigned char channel, unsigned char first_ipmb_addr, unsigned char last_ipmb_addr) { unsigned long addr = (first_ipmb_addr | (last_ipmb_addr << 8) | (channel << 16)); int rv = 0; ipmi_lock(domain->ipmb_ignores_lock); if (! ilist_add_tail(domain->ipmb_ignores, (void *) addr, NULL)) return ENOMEM; ipmi_unlock(domain->ipmb_ignores_lock); return rv; } typedef struct mc_upd_info_s { enum ipmi_update_e op; ipmi_domain_t *domain; ipmi_mc_t *mc; } mc_upd_info_t; static int iterate_mc_upds(void *cb_data, void *item1, void *item2) { mc_upd_info_t *info = cb_data; ipmi_domain_mc_upd_cb handler = item1; handler(info->op, info->domain, info->mc, item2); return LOCKED_LIST_ITER_CONTINUE; } static int add_mc_to_domain(ipmi_domain_t *domain, ipmi_mc_t *mc) { char addr_data[sizeof(ipmi_addr_t)]; ipmi_addr_t *addr = (ipmi_addr_t *) addr_data; unsigned int addr_len; int rv = 0; CHECK_DOMAIN_LOCK(domain); CHECK_MC_LOCK(mc); ipmi_mc_get_ipmi_address(mc, addr, &addr_len); ipmi_lock(domain->mc_lock); if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { if (addr->channel >= MAX_CONS) rv = EINVAL; else domain->sys_intf_mcs[addr->channel] = mc; } else if (addr->addr_type == IPMI_IPMB_ADDR_TYPE) { ipmi_ipmb_addr_t *ipmb = (ipmi_ipmb_addr_t *) addr; int idx; mc_table_t *tab; int i; idx = HASH_SLAVE_ADDR(ipmb->slave_addr); tab = &(domain->ipmb_mcs[idx]); if (tab->size == tab->curr) { ipmi_mc_t **nmcs; nmcs = ipmi_mem_alloc(sizeof(ipmi_mc_t *) * (tab->size+5)); if (!nmcs) { rv = ENOMEM; goto out_unlock; } if (tab->mcs) { memcpy(nmcs, tab->mcs, sizeof(ipmi_mc_t *) * tab->size); ipmi_mem_free(tab->mcs); } memset(nmcs+tab->size, 0, sizeof(ipmi_mc_t *) * 5); tab->size += 5; tab->mcs = nmcs; } for (i=0; isize; i++) { if (!tab->mcs[i]) { tab->mcs[i] = mc; tab->curr++; break; } } } out_unlock: ipmi_unlock(domain->mc_lock); return rv; } static void call_mc_upd_handlers(ipmi_domain_t *domain, ipmi_mc_t *mc, enum ipmi_update_e op) { mc_upd_info_t info; CHECK_DOMAIN_LOCK(domain); CHECK_MC_LOCK(mc); info.domain = domain; info.op = op; info.mc = mc; locked_list_iterate(domain->mc_upd_handlers, iterate_mc_upds, &info); } int ipmi_domain_add_mc_updated_handler(ipmi_domain_t *domain, ipmi_domain_mc_upd_cb handler, void *cb_data) { if (locked_list_add(domain->mc_upd_handlers, handler, cb_data)) return 0; else return ENOMEM; } typedef struct mc_upd_cl_info_s { ipmi_domain_mc_upd_cb handler; void *handler_data; } mc_upd_cl_info_t; static int iterate_mc_upds_cl(void *cb_data, void *item1, void *item2) { mc_upd_cl_info_t *info = cb_data; ipmi_domain_mc_upd_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_mc_upd_cl_handlers(ipmi_domain_t *domain, ipmi_domain_mc_upd_cb handler, void *handler_data) { mc_upd_cl_info_t info; info.handler = handler; info.handler_data = handler_data; locked_list_iterate(domain->mc_upd_cl_handlers, iterate_mc_upds_cl, &info); } int ipmi_domain_remove_mc_updated_handler(ipmi_domain_t *domain, ipmi_domain_mc_upd_cb handler, void *cb_data) { if (locked_list_remove(domain->mc_upd_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_domain_add_mc_updated_handler_cl(ipmi_domain_t *domain, ipmi_domain_mc_upd_cl_cb handler, void *cb_data) { if (locked_list_add(domain->mc_upd_cl_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_mc_updated_handler_cl(ipmi_domain_t *domain, ipmi_domain_mc_upd_cl_cb handler, void *cb_data) { if (locked_list_remove(domain->mc_upd_cl_handlers, handler, cb_data)) return 0; else return EINVAL; } /* Must be called with the domain MC lock held. It will be released. */ int i_ipmi_remove_mc_from_domain(ipmi_domain_t *domain, ipmi_mc_t *mc) { char addr_data[sizeof(ipmi_addr_t)]; ipmi_addr_t *addr = (ipmi_addr_t *) addr_data; unsigned int addr_len; int found = 0; ipmi_mc_get_ipmi_address(mc, addr, &addr_len); if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { if ((addr->channel < MAX_CONS) && (mc == domain->sys_intf_mcs[addr->channel])) { domain->sys_intf_mcs[addr->channel] = NULL; found = 1; } } else if (addr->addr_type == IPMI_IPMB_ADDR_TYPE) { ipmi_ipmb_addr_t *ipmb = (ipmi_ipmb_addr_t *) addr; int idx; mc_table_t *tab; int i; idx = HASH_SLAVE_ADDR(ipmb->slave_addr); tab = &(domain->ipmb_mcs[idx]); for (i=0; isize; i++) { if (tab->mcs[i] == mc) { tab->curr--; tab->mcs[i] = NULL; found = 1; } } } ipmi_unlock(domain->mc_lock); if (found) { call_mc_upd_handlers(domain, mc, IPMI_DELETED); return 0; } else return ENOENT; } int i_ipmi_find_or_create_mc_by_slave_addr(ipmi_domain_t *domain, unsigned int channel, unsigned int slave_addr, ipmi_mc_t **new_mc) { ipmi_mc_t *mc; char addr_data[sizeof(ipmi_addr_t)]; ipmi_addr_t *addr = (ipmi_addr_t *) addr_data; int addr_size; int rv; if (channel == IPMI_BMC_CHANNEL) { ipmi_system_interface_addr_t *saddr = (void *) addr; saddr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; saddr->channel = slave_addr; saddr->lun = 0; addr_size = sizeof(*saddr); } else { ipmi_ipmb_addr_t *iaddr = (void *) addr; iaddr->addr_type = IPMI_IPMB_ADDR_TYPE; iaddr->channel = channel; iaddr->lun = 0; iaddr->slave_addr = slave_addr; addr_size = sizeof(*iaddr); } mc = i_ipmi_find_mc_by_addr(domain, addr, addr_size); if (mc) { if (new_mc) *new_mc = mc; return 0; } rv = i_ipmi_create_mc(domain, addr, addr_size, &mc); if (rv) return rv; /* If we find an MC in the SDRs that we don't know about yet, attempt to scan it. */ if (ipmi_option_IPMB_scan(domain)) ipmi_start_ipmb_mc_scan(domain, channel, slave_addr, slave_addr, NULL, NULL); rv = add_mc_to_domain(domain, mc); if (rv) { i_ipmi_cleanup_mc(mc); i_ipmi_mc_put(mc); return rv; } call_mc_upd_handlers(domain, mc, IPMI_ADDED); if (new_mc) *new_mc = mc; return 0; } /*********************************************************************** * * Command/response handling * **********************************************************************/ static int cmp_nmsg(void *item, void *cb_data) { ll_msg_t *nmsg1 = item; ll_msg_t *nmsg2 = cb_data; return ((nmsg1 == nmsg2) && (nmsg1->domain == nmsg2->domain)); } /* Must be called with the cmds_lock held. */ static int find_and_remove_msg(ipmi_domain_t *domain, ll_msg_t *nmsg, long seq) { ilist_iter_t iter; int rv = 0; ilist_init_iter(&iter, domain->cmds); ilist_unpositioned(&iter); if ((ilist_search_iter(&iter, cmp_nmsg, nmsg) != NULL) && (nmsg->seq == seq)) { ilist_delete(&iter); rv = 1; } return rv; } static int ll_rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *orspi) { ipmi_msgi_t *rspi; ipmi_domain_t *domain = orspi->data1; ll_msg_t *nmsg = orspi->data2; long seq = (long) orspi->data3; long conn_seq = (long) orspi->data4; int rv; rv = i_ipmi_domain_get(domain); if (rv) /* No need to report these to the upper layer, they have already been delivered in the cleanup code. */ return IPMI_MSG_ITEM_NOT_USED; ipmi_lock(domain->cmds_lock); if (conn_seq != domain->conn_seq[nmsg->con]) { /* The message has been rerouted, just ignore this response. */ ipmi_unlock(domain->cmds_lock); goto out_unlock; } if (!find_and_remove_msg(domain, nmsg, seq)) { ipmi_unlock(domain->cmds_lock); goto out_unlock; } ipmi_unlock(domain->cmds_lock); rspi = nmsg->rsp_item; if (nmsg->rsp_handler) { ipmi_move_msg_item(rspi, orspi); memcpy(&rspi->addr, &orspi->addr, orspi->addr_len); rspi->addr_len = orspi->addr_len; deliver_rsp(domain, nmsg->rsp_handler, rspi); } else ipmi_free_msg_item(rspi); ipmi_mem_free(nmsg); out_unlock: i_ipmi_domain_put(domain); return IPMI_MSG_ITEM_NOT_USED; } static int ll_si_rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *orspi) { ipmi_msgi_t *rspi; ipmi_domain_t *domain = orspi->data1; ll_msg_t *nmsg = orspi->data2; int rv; rspi = nmsg->rsp_item; rv = i_ipmi_domain_get(domain); if (rv) { /* Note that since we don't track SI messages, we must report them to the upper layer through this interface when the domain goes away. */ deliver_rsp(NULL, nmsg->rsp_handler, rspi); return IPMI_MSG_ITEM_NOT_USED; } if (nmsg->rsp_handler) { ipmi_move_msg_item(rspi, orspi); /* Set the LUN from the response message. */ ipmi_addr_set_lun(&rspi->addr, ipmi_addr_get_lun(&rspi->addr)); deliver_rsp(domain, nmsg->rsp_handler, rspi); } else ipmi_free_msg_item(rspi); ipmi_mem_free(nmsg); i_ipmi_domain_put(domain); return IPMI_MSG_ITEM_NOT_USED; } static int matching_domain_sysaddr(ipmi_domain_t *domain, const ipmi_addr_t *addr, ipmi_system_interface_addr_t *si) { if (addr->addr_type == IPMI_IPMB_ADDR_TYPE) { ipmi_ipmb_addr_t *ipmb = (ipmi_ipmb_addr_t *) addr; int i; if (ipmb->channel >= MAX_IPMI_USED_CHANNELS) return 0; if (domain->chan[ipmb->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) return 0; for (i=0; icon_active[i] && domain->con_up[i] && (domain->con_ipmb_addr[i][ipmb->channel]==ipmb->slave_addr) && domain->sys_intf_mcs[i]) { si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = i; si->lun = ipmb->lun; return 1; } } } return 0; } static int send_command_option(ipmi_domain_t *domain, int conn, const ipmi_addr_t *addr, unsigned int addr_len, const ipmi_msg_t *msg, const ipmi_con_option_t *options, ipmi_ll_rsp_handler_t handler, void *handler_data) { if (domain->conn[conn]->send_command_option) return domain->conn[conn]->send_command_option(domain->conn[conn], addr, addr_len, msg, options, handler, handler_data); else return domain->conn[conn]->send_command(domain->conn[conn], addr, addr_len, msg, handler, handler_data); } static int send_command_addr(ipmi_domain_t *domain, const ipmi_addr_t *addr, unsigned int addr_len, const ipmi_msg_t *msg, ipmi_addr_response_handler_t rsp_handler, void *rsp_data1, void *rsp_data2, int side_effects) { int rv; int u; ll_msg_t *nmsg; ipmi_system_interface_addr_t si; ipmi_ll_rsp_handler_t handler; void *data4 = NULL; int is_ipmb = 0; ipmi_msgi_t *rspi; ipmi_con_option_t opt_data[2]; ipmi_con_option_t *options = NULL; if (addr_len > sizeof(ipmi_addr_t)) return EINVAL; if (msg->data_len > IPMI_MAX_MSG_LENGTH) return EINVAL; if (domain->in_shutdown) return EINVAL; if (side_effects) { options = opt_data; options[0].option = IPMI_CON_MSG_OPTION_SIDE_EFFECTS; options[0].ival = 1; options[1].option = IPMI_CON_OPTION_LIST_END; } CHECK_DOMAIN_LOCK(domain); nmsg = ipmi_mem_alloc(sizeof(*nmsg)); if (!nmsg) return ENOMEM; nmsg->rsp_item = ipmi_alloc_msg_item(); if (!nmsg->rsp_item) { ipmi_mem_free(nmsg); return ENOMEM; } /* Copy the address here because where we send it may change. But we want the response address to match what we sent. */ memcpy(&nmsg->rsp_item->addr, addr, addr_len); nmsg->rsp_item->addr_len = addr_len; if (matching_domain_sysaddr(domain, addr, &si)) { /* We have a direct connection to this BMC and it is up and operational, so talk directly to it. */ u = si.channel; si.channel = IPMI_BMC_CHANNEL; addr = (ipmi_addr_t *) &si; addr_len = sizeof(si); handler = ll_si_rsp_handler; } else if ((addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (addr->channel != IPMI_BMC_CHANNEL)) { u = addr->channel; /* Messages to system interface addresses use the channel to choose which system address to message. */ if ((u < 0) || (u >= MAX_CONS)) { rv = EINVAL; goto out; } if (!domain->conn[u]) { rv = EINVAL; goto out; } si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si.channel = IPMI_BMC_CHANNEL; si.lun = ((ipmi_system_interface_addr_t *) addr)->lun; addr = (ipmi_addr_t *) &si; addr_len = sizeof(si); handler = ll_si_rsp_handler; } else { u = domain->working_conn; /* If we don't have any working connection, just use connection zero. */ if (u == -1) u = 0; handler = ll_rsp_handler; is_ipmb = 1; } nmsg->domain = domain; nmsg->con = u; memcpy(&nmsg->msg, msg, sizeof(nmsg->msg)); nmsg->msg.data = nmsg->msg_data; nmsg->msg.data_len = msg->data_len; memcpy(nmsg->msg.data, msg->data, msg->data_len); nmsg->rsp_handler = rsp_handler; nmsg->rsp_item->data1 = rsp_data1; nmsg->rsp_item->data2 = rsp_data2; nmsg->side_effects = side_effects; ipmi_lock(domain->cmds_lock); nmsg->seq = domain->cmds_seq; domain->cmds_seq++; /* Have to delay this to here so we are holding the lock. */ if (is_ipmb) data4 = (void *) (long) domain->conn_seq[u]; rspi = ipmi_alloc_msg_item(); if (!rspi) { rv = ENOMEM; goto out_unlock; } rspi->data1 = domain; rspi->data2 = nmsg; rspi->data3 = (void *) nmsg->seq; rspi->data4 = data4; rv = send_command_option(domain, u, addr, addr_len, msg, options, handler, rspi); if (rv) { ipmi_free_msg_item(rspi); goto out_unlock; } else if (is_ipmb) { /* If it's a system interface we don't add it to the list of commands running, because it will never need to be rerouted. */ ilist_add_tail(domain->cmds, nmsg, &nmsg->link); } out_unlock: ipmi_unlock(domain->cmds_lock); out: if (rv) { ipmi_free_msg_item(nmsg->rsp_item); ipmi_mem_free(nmsg); } return rv; } int ipmi_send_command_addr(ipmi_domain_t *domain, const ipmi_addr_t *addr, unsigned int addr_len, const ipmi_msg_t *msg, ipmi_addr_response_handler_t rsp_handler, void *rsp_data1, void *rsp_data2) { return send_command_addr(domain, addr, addr_len, msg, rsp_handler, rsp_data1, rsp_data2, 0); } int ipmi_send_command_addr_sideeff(ipmi_domain_t *domain, const ipmi_addr_t *addr, unsigned int addr_len, const ipmi_msg_t *msg, ipmi_addr_response_handler_t rsp_handler, void *rsp_data1, void *rsp_data2) { return send_command_addr(domain, addr, addr_len, msg, rsp_handler, rsp_data1, rsp_data2, 1); } /* Take all the commands for any inactive or down connection and resend them on another connection. */ static void reroute_cmds(ipmi_domain_t *domain, int old_con, int new_con) { ilist_iter_t iter; int rv; ll_msg_t *nmsg; ipmi_lock(domain->cmds_lock); ilist_init_iter(&iter, domain->cmds); rv = ilist_first(&iter); (domain->conn_seq[old_con])++; while (rv) { nmsg = ilist_get(&iter); if (nmsg->con == old_con) { ipmi_msgi_t *rspi; ipmi_con_option_t opt_data[2]; ipmi_con_option_t *options = NULL; nmsg->seq = domain->cmds_seq; domain->cmds_seq++; /* Make the message unique so a response from the other connection will not match. */ nmsg->con = new_con; rspi = ipmi_alloc_msg_item(); if (!rspi) goto send_err; if (nmsg->side_effects) { options = opt_data; options[0].option = IPMI_CON_MSG_OPTION_SIDE_EFFECTS; options[0].ival = 1; options[1].option = IPMI_CON_OPTION_LIST_END; } rspi->data1 = domain; rspi->data2 = nmsg; rspi->data3 = (void *) nmsg->seq; rspi->data4 = (void *) domain->conn_seq[new_con]; rv = send_command_option(domain, new_con, &nmsg->rsp_item->addr, nmsg->rsp_item->addr_len, &nmsg->msg, options, ll_rsp_handler, rspi); if (rv) { ipmi_free_msg_item(rspi); send_err: /* Couldn't send the message, just fail it. */ if (nmsg->rsp_handler) { rspi = nmsg->rsp_item; rspi->msg.netfn = nmsg->msg.netfn | 1; rspi->msg.cmd = nmsg->msg.cmd; rspi->msg.data = rspi->data; rspi->msg.data_len = 1; rspi->data[0] = IPMI_UNKNOWN_ERR_CC; deliver_rsp(domain, nmsg->rsp_handler, rspi); } rv = ilist_delete(&iter); ipmi_mem_free(nmsg); continue; } } rv = ilist_next(&iter); } ipmi_unlock(domain->cmds_lock); } /*********************************************************************** * * Bus scanning * **********************************************************************/ /* This is the number of device ID queries that an MC must not respond to in a row to be considered dead. */ #define MAX_MC_MISSED_RESPONSES 10 void ipmi_domain_set_ipmb_rescan_time(ipmi_domain_t *domain, unsigned int seconds) { int rv; struct timeval timeout; CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->audit_domain_timer_info->lock); domain->audit_domain_interval = seconds; rv = domain->os_hnd->stop_timer(domain->os_hnd, domain->audit_domain_timer); if (rv) { /* If we can't stop the timer, that's ok, the timer is in the wakeup and will handle the restart for us. */ ipmi_unlock(domain->audit_domain_timer_info->lock); return; } timeout.tv_sec = domain->audit_domain_interval; timeout.tv_usec = 0; domain->os_hnd->start_timer(domain->os_hnd, domain->audit_domain_timer, &timeout, domain_audit, domain->audit_domain_timer_info); ipmi_unlock(domain->audit_domain_timer_info->lock); } unsigned int ipmi_domain_get_ipmb_rescan_time(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); return domain->audit_domain_interval; } int ipmi_domain_set_full_bus_scan(ipmi_domain_t *domain, int val) { CHECK_DOMAIN_LOCK(domain); domain->do_bus_scan = val; return 0; } static void add_bus_scans_running(ipmi_domain_t *domain, mc_ipmb_scan_info_t *info) { info->next = domain->bus_scans_running; domain->bus_scans_running = info; } static void remove_bus_scans_running(ipmi_domain_t *domain, mc_ipmb_scan_info_t *info) { mc_ipmb_scan_info_t *i2; i2 = domain->bus_scans_running; if (i2 == info) domain->bus_scans_running = info->next; else while (i2->next != NULL) { if (i2->next == info) { i2->next = info->next; break; } i2 = i2->next; } } static int devid_bc_rsp_handler(ipmi_domain_t *domain, ipmi_msgi_t *rspi); static void rescan_timeout_handler(void *cb_data, os_hnd_timer_id_t *id) { mc_ipmb_scan_info_t *info = cb_data; int rv; ipmi_ipmb_addr_t *ipmb; ipmi_domain_t *domain; ipmi_lock(info->lock); if (info->cancelled) { ipmi_unlock(info->lock); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return; } info->timer_running = 0; ipmi_unlock(info->lock); domain = info->domain; rv = i_ipmi_domain_get(domain); if (rv) { ipmi_log(IPMI_LOG_INFO, "%sdomain.c(rescan_timeout_handler): " "BMC went away while scanning for MCs", DOMAIN_NAME(domain)); return; } goto retry_addr; next_addr_nolock: ipmb = (ipmi_ipmb_addr_t *) &info->addr; ipmb->slave_addr += 2; if ((info->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) || (ipmb->slave_addr > info->end_addr)) { /* We've hit the end, we can quit now. */ if (info->done_handler) info->done_handler(domain, 0, info->cb_data); remove_bus_scans_running(domain, info); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); goto out; } info->missed_responses = 0; if (in_ipmb_ignores(domain, ipmb->channel, ipmb->slave_addr)) goto next_addr_nolock; retry_addr: rv = ipmi_send_command_addr(domain, &(info->addr), info->addr_len, &(info->msg), devid_bc_rsp_handler, info, NULL); if (rv) goto next_addr_nolock; out: i_ipmi_domain_put(domain); } static int devid_bc_rsp_handler(ipmi_domain_t *domain, ipmi_msgi_t *rspi) { ipmi_msg_t *msg = &rspi->msg; ipmi_addr_t *addr = &rspi->addr; unsigned int addr_len = rspi->addr_len; mc_ipmb_scan_info_t *info = rspi->data1; int rv; ipmi_mc_t *mc = NULL; ipmi_ipmb_addr_t *ipmb; int mc_added = 0; int mc_changed = 0; rv = i_ipmi_domain_get(domain); if (rv) { ipmi_log(IPMI_LOG_INFO, "%sdomain.c(devid_bc_rsp_handler): " "BMC went away while scanning for MCs", DOMAIN_NAME(domain)); return IPMI_MSG_ITEM_NOT_USED; } mc = i_ipmi_find_mc_by_addr(domain, addr, addr_len); if (msg->data[0] == 0) { if (mc && ipmi_mc_is_active(mc) && !i_ipmi_mc_device_data_compares(mc, msg)) { /* The MC was replaced with a new one, so clear the old one and add a new one. */ i_ipmi_cleanup_mc(mc); i_ipmi_mc_put(mc); mc = i_ipmi_find_mc_by_addr(domain, addr, addr_len); } if (!mc || !ipmi_mc_is_active(mc)) { /* It doesn't already exist, or it's inactive, so add it. */ if (!mc) { /* If it's not there, then add it. If it's just not active, reuse the same data. */ rv = i_ipmi_create_mc(domain, addr, addr_len, &mc); if (rv) { /* Out of memory, just give up for now. */ if (info->done_handler) info->done_handler(domain, 0, info->cb_data); remove_bus_scans_running(domain, info); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); goto out; } rv = add_mc_to_domain(domain, mc); if (rv) { i_ipmi_cleanup_mc(mc); goto next_addr; } rv = i_ipmi_mc_get_device_id_data_from_rsp(mc, msg); if (rv) { /* If we couldn't handle the device data, just clean it up */ i_ipmi_cleanup_mc(mc); goto out; } /* In this case, the use count is defined to be 1, so it will always be set up properly and the previous function will not return EAGAIN, no need to check. */ mc_added = 1; i_ipmi_mc_handle_new(mc); } else { /* It was inactive, activate it. */ rv = i_ipmi_mc_get_device_id_data_from_rsp(mc, msg); if (rv == EAGAIN) { /* The MC is in use, so we cannot handle it right now. We wait until the MC is released to do that and cue off the pending new MC field in the MC. */ } else if (rv) { /* If we couldn't handle the device data, just clean it up. */ i_ipmi_cleanup_mc(mc); } else { mc_changed = 1; i_ipmi_mc_handle_new(mc); } } } else { /* Periodically check the MCs. */ i_ipmi_mc_check_mc(mc); } } else if (mc && ipmi_mc_is_active(mc)) { /* Didn't get a response. Maybe the MC has gone away? */ info->missed_responses++; /* We fail system interface addresses immediately, since they shouldn't be a timeout problem. */ if ((info->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) || (info->missed_responses >= MAX_MC_MISSED_RESPONSES)) { i_ipmi_cleanup_mc(mc); goto next_addr; } else { /* Try again after a second. */ struct timeval timeout; if (msg->data[0] == IPMI_TIMEOUT_CC) /* If we timed out, then no need to time, since a second has gone by already. */ goto retry_addr; ipmi_lock(info->lock); timeout.tv_sec = 1; timeout.tv_usec = 0; info->timer_running = 1; info->os_hnd->start_timer(info->os_hnd, info->timer, &timeout, rescan_timeout_handler, info); ipmi_unlock(info->lock); goto out; } } next_addr: if (mc_added) call_mc_upd_handlers(domain, mc, IPMI_ADDED); else if (mc_changed) call_mc_upd_handlers(domain, mc, IPMI_CHANGED); next_addr_nolock: ipmb = (ipmi_ipmb_addr_t *) &info->addr; ipmb->slave_addr += 2; if ((info->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) || (ipmb->slave_addr > info->end_addr)) { /* We've hit the end, we can quit now. */ if (info->done_handler) info->done_handler(domain, 0, info->cb_data); remove_bus_scans_running(domain, info); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); goto out; } info->missed_responses = 0; if (in_ipmb_ignores(domain, ipmb->channel, ipmb->slave_addr)) goto next_addr_nolock; retry_addr: rv = ipmi_send_command_addr(domain, &(info->addr), info->addr_len, &(info->msg), devid_bc_rsp_handler, info, NULL); if (rv) goto next_addr_nolock; out: if (mc) i_ipmi_mc_put(mc); i_ipmi_domain_put(domain); return IPMI_MSG_ITEM_NOT_USED; } int ipmi_start_ipmb_mc_scan(ipmi_domain_t *domain, int channel, unsigned int start_addr, unsigned int end_addr, ipmi_domain_cb done_handler, void *cb_data) { mc_ipmb_scan_info_t *info; int rv; ipmi_ipmb_addr_t *ipmb; CHECK_DOMAIN_LOCK(domain); if (channel >= MAX_IPMI_USED_CHANNELS) return EINVAL; if ((domain->chan[channel].medium != 1) && !(start_addr == 0x20 || end_addr == 0x20)) /* Make sure it is IPMB, or the BMC address. */ return ENOSYS; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; memset(info, 0, sizeof(*info)); info->domain = domain; ipmb = (ipmi_ipmb_addr_t *) &info->addr; ipmb->addr_type = IPMI_IPMB_BROADCAST_ADDR_TYPE; ipmb->channel = channel; ipmb->slave_addr = start_addr; ipmb->lun = 0; info->addr_len = sizeof(*ipmb); info->msg.netfn = IPMI_APP_NETFN; info->msg.cmd = IPMI_GET_DEVICE_ID_CMD; info->msg.data = NULL; info->msg.data_len = 0; info->end_addr = end_addr; info->done_handler = done_handler; info->cb_data = cb_data; info->missed_responses = 0; info->os_hnd = domain->os_hnd; rv = info->os_hnd->alloc_timer(info->os_hnd, &info->timer); if (rv) goto out_err; rv = ipmi_create_lock(domain, &info->lock); if (rv) goto out_err; rv = ENOSYS; /* Return err if no scans done */ /* Skip addresses we must ignore. */ while (in_ipmb_ignores(domain, ipmb->channel, ipmb->slave_addr)) { /* ipmb->slave_addr is 8 bits, so we can't do a <= comparison as it will overflow after 254. */ if (ipmb->slave_addr == end_addr) goto out_err; ipmb->slave_addr += 2; } while (rv) { rv = ipmi_send_command_addr(domain, &info->addr, info->addr_len, &(info->msg), devid_bc_rsp_handler, info, NULL); if (rv) { if (ipmb->slave_addr == end_addr) goto out_err; ipmb->slave_addr += 2; } } if (rv) goto out_err; else add_bus_scans_running(domain, info); return 0; out_err: if (info->timer) info->os_hnd->free_timer(info->os_hnd, info->timer); if (info->lock) ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return 0; /* Since the done handler is always called, always return true. Bus scans always succeed. */ } int ipmi_start_si_scan(ipmi_domain_t *domain, int si_num, ipmi_domain_cb done_handler, void *cb_data) { mc_ipmb_scan_info_t *info; ipmi_system_interface_addr_t *si; int rv; info = ipmi_mem_alloc(sizeof(mc_ipmb_scan_info_t)); if (!info) return ENOMEM; memset(info, 0, sizeof(*info)); info->domain = domain; si = (void *) &info->addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = si_num; si->lun = 0; info->addr_len = sizeof(*si); info->msg.netfn = IPMI_APP_NETFN; info->msg.cmd = IPMI_GET_DEVICE_ID_CMD; info->msg.data = NULL; info->msg.data_len = 0; info->done_handler = done_handler; info->cb_data = cb_data; info->missed_responses = 0; info->os_hnd = domain->os_hnd; rv = info->os_hnd->alloc_timer(info->os_hnd, &info->timer); if (rv) goto out_err; rv = ipmi_create_lock(domain, &info->lock); if (rv) goto out_err; rv = ipmi_send_command_addr(domain, &info->addr, info->addr_len, &(info->msg), devid_bc_rsp_handler, info, NULL); if (rv) goto out_err; else add_bus_scans_running(domain, info); return 0; out_err: if (info->timer) info->os_hnd->free_timer(info->os_hnd, info->timer); if (info->lock) ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return rv; } static void mc_scan_done(ipmi_domain_t *domain, int err, void *cb_data) { ipmi_domain_cb bus_scan_handler; void *bus_scan_handler_cb_data; ipmi_lock(domain->mc_lock); domain->scanning_bus_count--; if (domain->scanning_bus_count) { i_ipmi_put_domain_fully_up(domain, "mc_scan_done"); ipmi_unlock(domain->mc_lock); return; } bus_scan_handler = domain->bus_scan_handler; bus_scan_handler_cb_data = domain->bus_scan_handler_cb_data; ipmi_unlock(domain->mc_lock); if (bus_scan_handler) bus_scan_handler(domain, 0, bus_scan_handler_cb_data); i_ipmi_put_domain_fully_up(domain, "mc_scan_done"); } void i_ipmi_start_mc_scan_one(ipmi_domain_t *domain, int chan, int first, int last) { int rv; i_ipmi_get_domain_fully_up(domain, "i_ipmi_start_mc_scan_one"); domain->scanning_bus_count++; rv = ipmi_start_ipmb_mc_scan(domain, chan, first, last, mc_scan_done, NULL); if (rv) { domain->scanning_bus_count--; i_ipmi_put_domain_fully_up(domain, "i_ipmi_start_mc_scan_one"); } } static int cmp_int(const void *v1, const void *v2) { const int *i1 = v1; const int *i2 = v2; if (*i1 < *i2) return -1; else if (*i1 > *i2) return 1; else return 0; } void ipmi_domain_start_full_ipmb_scan(ipmi_domain_t *domain) { int i, j; int rv; int got_bmc = 0; if (domain->in_shutdown) return; ipmi_lock(domain->mc_lock); if (!domain->do_bus_scan || (!ipmi_option_IPMB_scan(domain))) { /* Always scan the local BMC(s). */ for (i=0; iconn[i]) continue; for (j=0; jchan[j].medium != IPMI_CHANNEL_MEDIUM_IPMB) continue; i_ipmi_start_mc_scan_one(domain, j, domain->con_ipmb_addr[i][j], domain->con_ipmb_addr[i][j]); break; } if (j == MAX_IPMI_USED_CHANNELS) { /* Didn't find a valid channel, just scan 0 to get one. */ i_ipmi_start_mc_scan_one(domain, 0, domain->con_ipmb_addr[i][0], domain->con_ipmb_addr[i][0]); } } ipmi_unlock(domain->mc_lock); return; } if (domain->scanning_bus_count) { ipmi_unlock(domain->mc_lock); return; } /* If a connections supports sysaddress scanning, then scan the system address for that connection. */ for (i=0; icon_up[i]) && domain->conn[i]->scan_sysaddr) { i_ipmi_get_domain_fully_up(domain, "ipmi_domain_start_full_ipmb_scan"); domain->scanning_bus_count++; rv = ipmi_start_si_scan(domain, i, mc_scan_done, NULL); if (rv) { domain->scanning_bus_count--; i_ipmi_put_domain_fully_up(domain, "ipmi_domain_start_full_ipmb_scan"); } } } /* Now start the IPMB scans. */ for (i=0; ichan[i].medium == IPMI_CHANNEL_MEDIUM_IPMB) { if (!got_bmc) { got_bmc = 1; /* Always scan the normal BMC first. */ i_ipmi_start_mc_scan_one(domain, i, 0x20, 0x20); i_ipmi_start_mc_scan_one(domain, i, 0x10, 0xf0); } else { /* This is unfortunately complicated. We only want the BMC to show up in one place, so we only scan the BMC's address on the first one. If we have a system with two connections (two BMCs), we want to make sure they don't show up on each others lists. So except for the first IPMB, we ignore all BMC IPMB addresses. */ int ignore_addr[MAX_CONS]; int num_ignore = 0; int cstart = 0x10; for (j=0; jconn[j]) continue; ignore_addr[num_ignore] = domain->con_ipmb_addr[j][i]; num_ignore++; } qsort(ignore_addr, num_ignore, sizeof(int), cmp_int); for (j=0; jmc_lock); } static void refetch_sdr_handler(ipmi_sdr_info_t *sdrs, int err, int changed, unsigned int count, void *cb_data) { ipmi_domain_t *domain = cb_data; if (changed) { ipmi_entity_scan_sdrs(domain, NULL, domain->entities, domain->main_sdrs); ipmi_sensor_handle_sdrs(domain, NULL, domain->main_sdrs); ipmi_detect_ents_presence_changes(domain->entities, 1); i_ipmi_entities_report_sdrs_read(domain->entities); } } static void check_main_sdrs(ipmi_domain_t *domain) { if (ipmi_option_SDRs(domain)) ipmi_sdr_fetch(domain->main_sdrs, refetch_sdr_handler, domain); } static void domain_audit(void *cb_data, os_hnd_timer_id_t *id) { struct timeval timeout; audit_domain_info_t *info = cb_data; ipmi_domain_t *domain = info->domain; int rv; ipmi_lock(info->lock); if (info->cancelled) { ipmi_unlock(info->lock); info->os_hnd->free_timer(info->os_hnd, id); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return; } rv = i_ipmi_domain_get(domain); if (rv) goto out; if (domain->got_invalid_dev_id) { /* Failure getting device id, just try again. */ domain_send_mc_id(domain); goto out_start_timer; } /* Only operate if we know a connection is up. */ if (! domain->connection_up) goto out_start_timer; /* Rescan all the presence sensors to make sure they are valid. */ ipmi_detect_domain_presence_changes(domain, 1); ipmi_domain_start_full_ipmb_scan(domain); /* Also check to see if the SDRs have changed. */ check_main_sdrs(domain); out_start_timer: timeout.tv_sec = domain->audit_domain_interval; timeout.tv_usec = 0; domain->os_hnd->start_timer(domain->os_hnd, id, &timeout, domain_audit, info); i_ipmi_domain_put(domain); out: ipmi_unlock(info->lock); } /*********************************************************************** * * Incoming event handling. * **********************************************************************/ typedef struct event_sensor_info_s { int err; ipmi_event_t *event; } event_sensor_info_t; void event_sensor_cb(ipmi_sensor_t *sensor, void *cb_data) { event_sensor_info_t *info = cb_data; /* It's an event for a specific sensor, and the sensor exists. */ info->err = ipmi_sensor_event(sensor, info->event); } void i_ipmi_domain_system_event_handler(ipmi_domain_t *domain, ipmi_mc_t *ev_mc, ipmi_event_t *event) { int rv = 1; ipmi_time_t timestamp = ipmi_event_get_timestamp(event); unsigned int type = ipmi_event_get_type(event); /* We do not need any locking to assure that events are delivered in order (from the same SEL). Indeed, locking here wouldn't help. But the event-fetching mechanisms are guaranteed to be single-threaded, so ordering is always preserved there. */ if (DEBUG_EVENTS) { ipmi_mcid_t mcid = ipmi_event_get_mcid(event); unsigned int record_id = ipmi_event_get_record_id(event); unsigned int data_len = ipmi_event_get_data_len(event); const unsigned char *data; ipmi_log(IPMI_LOG_DEBUG_START, "Event recid mc (0x%x):%4.4x type:%2.2x timestamp %lld:", mcid.mc_num, record_id, type, (long long) timestamp); if (data_len) { ipmi_log(IPMI_LOG_DEBUG_CONT, "\n "); data = ipmi_event_get_data_ptr(event); dump_hex(data, data_len); } ipmi_log(IPMI_LOG_DEBUG_END, " "); } /* Let the OEM handler for the MC that the message came from have a go at it first. Note that OEM handlers must look at the time themselves. */ if (i_ipmi_mc_check_sel_oem_event_handler(ev_mc, event)) return; /* It's a system event record from an MC, and the timestamp is later than our startup timestamp. */ if ((type == 0x02) && !ipmi_event_is_old(event)) { /* It's a standard IPMI event. */ ipmi_mc_t *mc; ipmi_sensor_id_t id; event_sensor_info_t info; const unsigned char *data; mc = i_ipmi_event_get_generating_mc(domain, ev_mc, event); if (!mc) goto out; /* Let the OEM handler for the MC that sent the event try next. */ if (i_ipmi_mc_check_oem_event_handler(mc, event)) { i_ipmi_mc_put(mc); return; } /* The OEM code didn't handle it. */ data = ipmi_event_get_data_ptr(event); id.mcid = ipmi_mc_convert_to_id(mc); id.lun = data[5] & 0x3; id.sensor_num = data[8]; info.event = event; rv = ipmi_sensor_pointer_cb(id, event_sensor_cb, &info); if (!rv) rv = info.err; i_ipmi_mc_put(mc); } out: /* It's an event from system software, or the info couldn't be found. */ if (rv) ipmi_handle_unhandled_event(domain, event); } static void ll_event_handler(ipmi_con_t *ipmi, const ipmi_addr_t *addr, unsigned int addr_len, ipmi_event_t *event, void *cb_data) { ipmi_domain_t *domain = cb_data; ipmi_mc_t *mc; int rv; ipmi_system_interface_addr_t si; rv = i_ipmi_domain_get(domain); if (rv) return; /* Convert the address to the proper one if it comes from a specific connection. */ if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { int i; for (i=0; iconn[i] == ipmi) break; } if (i == MAX_CONS) goto out; addr = (ipmi_addr_t *) &si; si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si.channel = i; si.lun = 0; addr_len = sizeof(si); } /* It came from an MC, so find the MC. */ mc = i_ipmi_find_mc_by_addr(domain, addr, addr_len); if (!mc) goto out; ipmi_event_set_mcid(event, ipmi_mc_convert_to_id(mc)); if (event == NULL) { /* The incoming event didn't carry the full event information. Just scan for events in the MC's SEL. */ ipmi_mc_reread_sel(mc, NULL, NULL); } else { /* Add it to the mc's event log. */ rv = i_ipmi_mc_sel_event_add(mc, event); if (rv != EEXIST) /* Call the handler on it if it wasn't already in there. */ i_ipmi_domain_system_event_handler(domain, mc, event); } i_ipmi_mc_put(mc); out: i_ipmi_domain_put(domain); } typedef struct call_event_handler_s { ipmi_domain_t *domain; ipmi_event_t *event; } call_event_handler_t; static int call_event_handler(void *cb_data, void *item1, void *item2) { call_event_handler_t *info = cb_data; ipmi_event_handler_cb handler = item1; handler(info->domain, info->event, item2); return LOCKED_LIST_ITER_CONTINUE; } void ipmi_handle_unhandled_event(ipmi_domain_t *domain, ipmi_event_t *event) { call_event_handler_t info; info.domain = domain; info.event = event; locked_list_iterate(domain->event_handlers, call_event_handler, &info); } int ipmi_domain_add_event_handler(ipmi_domain_t *domain, ipmi_event_handler_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); if (locked_list_add(domain->event_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_event_handler(ipmi_domain_t *domain, ipmi_event_handler_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); if (locked_list_remove(domain->event_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct event_handler_cl_info_s { ipmi_event_handler_cb handler; void *handler_data; } event_handler_cl_info_t; static int iterate_event_handler_cl(void *cb_data, void *item1, void *item2) { event_handler_cl_info_t *info = cb_data; ipmi_event_handler_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_event_handler_cl_handlers(ipmi_domain_t *domain, ipmi_event_handler_cb handler, void *handler_data) { event_handler_cl_info_t info; info.handler = handler; info.handler_data = handler_data; locked_list_iterate(domain->event_handlers_cl, iterate_event_handler_cl, &info); } int ipmi_domain_add_event_handler_cl(ipmi_domain_t *domain, ipmi_event_handler_cl_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); if (locked_list_add(domain->event_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_event_handler_cl(ipmi_domain_t *domain, ipmi_event_handler_cl_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); if (locked_list_remove(domain->event_handlers_cl, handler, cb_data)) return 0; else return EINVAL; } int ipmi_domain_disable_events(ipmi_domain_t *domain) { int rv; int return_rv = 0; int i; CHECK_DOMAIN_LOCK(domain); for (i=0; iconn[i]->remove_event_handler(domain->conn[i], ll_event_handler, domain); if (!return_rv) return_rv = rv; } return return_rv; } int ipmi_domain_enable_events(ipmi_domain_t *domain) { int return_rv = 0; int rv; int i; CHECK_DOMAIN_LOCK(domain); for (i=0; iconn[i]) continue; rv = domain->conn[i]->add_event_handler(domain->conn[i], ll_event_handler, domain); if (!return_rv) return_rv = rv; } return return_rv; } /*********************************************************************** * * SEL handling * **********************************************************************/ int ipmi_domain_del_event(ipmi_domain_t *domain, ipmi_event_t *event, ipmi_domain_cb done_handler, void *cb_data) { return ipmi_event_delete(event, done_handler, cb_data); } typedef struct next_event_handler_info_s { ipmi_event_t *rv; const ipmi_event_t *event; ipmi_mcid_t event_mcid; int found_curr_mc; int do_prev; /* If going backwards, this will be 1. */ } next_event_handler_info_t; static void next_event_handler(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { next_event_handler_info_t *info = cb_data; ipmi_mcid_t mcid = ipmi_mc_convert_to_id(mc); if (info->rv) /* We've found an event already, just return. */ return; if (info->do_prev) { if (info->found_curr_mc) /* We've found the MC that had the event, but it didn't have any more events. Look for last events now. */ info->rv = ipmi_mc_last_event(mc); else if (ipmi_cmp_mc_id(info->event_mcid, mcid) == 0) { info->found_curr_mc = 1; info->rv = ipmi_mc_prev_event(mc, info->event); } } else { if (info->found_curr_mc) /* We've found the MC that had the event, but it didn't have any more events. Look for first events now. */ info->rv = ipmi_mc_first_event(mc); else if (ipmi_cmp_mc_id(info->event_mcid, mcid) == 0) { info->found_curr_mc = 1; info->rv = ipmi_mc_next_event(mc, info->event); } } } ipmi_event_t * ipmi_domain_first_event(ipmi_domain_t *domain) { next_event_handler_info_t info; CHECK_DOMAIN_LOCK(domain); info.rv = NULL; info.event = NULL; info.found_curr_mc = 1; info.do_prev = 0; ipmi_domain_iterate_mcs(domain, next_event_handler, &info); return info.rv; } ipmi_event_t * ipmi_domain_last_event(ipmi_domain_t *domain) { next_event_handler_info_t info; CHECK_DOMAIN_LOCK(domain); info.rv = NULL; info.event = NULL; info.found_curr_mc = 1; info.do_prev = 1; ipmi_domain_iterate_mcs_rev(domain, next_event_handler, &info); return info.rv; } ipmi_event_t * ipmi_domain_next_event(ipmi_domain_t *domain, const ipmi_event_t *event) { next_event_handler_info_t info; CHECK_DOMAIN_LOCK(domain); info.rv = NULL; info.event = event; info.found_curr_mc = 0; info.do_prev = 0; info.event_mcid = ipmi_event_get_mcid(event); ipmi_domain_iterate_mcs(domain, next_event_handler, &info); return info.rv; } ipmi_event_t * ipmi_domain_prev_event(ipmi_domain_t *domain, const ipmi_event_t *event) { next_event_handler_info_t info; CHECK_DOMAIN_LOCK(domain); info.rv = NULL; info.event = event; info.found_curr_mc = 0; info.do_prev = 1; info.event_mcid = ipmi_event_get_mcid(event); ipmi_domain_iterate_mcs_rev(domain, next_event_handler, &info); return info.rv; } static void sel_count_handler(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { int *count = cb_data; *count += ipmi_mc_sel_count(mc); } int ipmi_domain_sel_count(ipmi_domain_t *domain, unsigned int *count) { CHECK_DOMAIN_LOCK(domain); *count = 0; ipmi_domain_iterate_mcs(domain, sel_count_handler, count); return 0; } static void sel_entries_used_handler(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { int *count = cb_data; *count += ipmi_mc_sel_entries_used(mc); } int ipmi_domain_sel_entries_used(ipmi_domain_t *domain, unsigned int *count) { CHECK_DOMAIN_LOCK(domain); *count = 0; ipmi_domain_iterate_mcs(domain, sel_entries_used_handler, count); return 0; } static void set_sel_rescan_time(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { ipmi_mc_set_sel_rescan_time(mc, domain->default_sel_rescan_time); } void ipmi_domain_set_sel_rescan_time(ipmi_domain_t *domain, unsigned int seconds) { CHECK_DOMAIN_LOCK(domain); domain->default_sel_rescan_time = seconds; ipmi_domain_iterate_mcs(domain, set_sel_rescan_time, NULL); } unsigned int ipmi_domain_get_sel_rescan_time(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); return domain->default_sel_rescan_time; } /* Code to explicitly reread all the SELs in the domain. */ typedef struct sels_reread_s { /* The number of pending requests. */ int count; /* The actual number of MCs we tried to request from .*/ int tried; /* This is the last error that occurred. */ int err; ipmi_domain_cb handler; void *cb_data; /* We may have multiple threads going at the data from multiple SEL reads, so we need to protect the data. */ ipmi_lock_t *lock; ipmi_domain_t *domain; } sels_reread_t; static void reread_sel_handler(ipmi_mc_t *mc, int err, void *cb_data) { sels_reread_t *info = cb_data; int count; int rv; ipmi_lock(info->lock); info->count--; count = info->count; if (err) info->err = err; ipmi_unlock(info->lock); if (count == 0) { /* We were the last one, call the main handler. */ /* First validate the domain. */ rv = i_ipmi_domain_get(info->domain); if (rv) info->domain = NULL; if (info->handler) info->handler(info->domain, info->err, info->cb_data); ipmi_destroy_lock(info->lock); if (info->domain) i_ipmi_domain_put(info->domain); ipmi_mem_free(info); } } static void reread_sels_handler(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { sels_reread_t *info = cb_data; int rv; if (ipmi_mc_sel_device_support(mc)) { info->tried++; rv = ipmi_mc_reread_sel(mc, reread_sel_handler, info); if (rv) info->err = rv; else info->count++; } } int ipmi_domain_reread_sels(ipmi_domain_t *domain, ipmi_domain_cb handler, void *cb_data) { sels_reread_t *info; int rv; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; rv = ipmi_create_lock(domain, &info->lock); if (rv) { ipmi_mem_free(info); return rv; } info->count = 0; info->tried = 0; info->err = 0; info->domain = domain; info->handler = handler; info->cb_data = cb_data; ipmi_lock(info->lock); rv = ipmi_domain_iterate_mcs(domain, reread_sels_handler, info); if (rv) { ipmi_unlock(info->lock); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return rv; } if ((info->tried > 0) && (info->count == 0)) { /* We tried to do an SEL fetch, but failed to actually accomplish any. Return an error. */ rv = info->err; ipmi_unlock(info->lock); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return rv; } if (info->count == 0) { /* No requests, so return an error. */ ipmi_unlock(info->lock); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return ENOSYS; } ipmi_unlock(info->lock); return 0; } /*********************************************************************** * * Generic handling of entities and MCs. * **********************************************************************/ int ipmi_detect_domain_presence_changes(ipmi_domain_t *domain, int force) { int rv; CHECK_DOMAIN_LOCK(domain); rv = ipmi_detect_ents_presence_changes(domain->entities, force); return rv; } os_handler_t * ipmi_domain_get_os_hnd(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); return domain->os_hnd; } ipmi_entity_info_t * ipmi_domain_get_entities(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); return domain->entities; } void i_ipmi_get_sdr_sensors(ipmi_domain_t *domain, ipmi_mc_t *mc, ipmi_sensor_t ***sensors, unsigned int *count) { if (mc) { i_ipmi_mc_get_sdr_sensors(mc, sensors, count); } else { CHECK_DOMAIN_LOCK(domain); *sensors = domain->sensors_in_main_sdr; *count = domain->sensors_in_main_sdr_count; } } void i_ipmi_set_sdr_sensors(ipmi_domain_t *domain, ipmi_mc_t *mc, ipmi_sensor_t **sensors, unsigned int count) { if (mc) { i_ipmi_mc_set_sdr_sensors(mc, sensors, count); } else { CHECK_DOMAIN_LOCK(domain); domain->sensors_in_main_sdr = sensors; domain->sensors_in_main_sdr_count = count; } } void * i_ipmi_get_sdr_entities(ipmi_domain_t *domain, ipmi_mc_t *mc) { if (mc) { return i_ipmi_mc_get_sdr_entities(mc); } else { CHECK_DOMAIN_LOCK(domain); return domain->entities_in_main_sdr; } } void i_ipmi_set_sdr_entities(ipmi_domain_t *domain, ipmi_mc_t *mc, void *entities) { if (mc) { i_ipmi_mc_set_sdr_entities(mc, entities); } else { CHECK_DOMAIN_LOCK(domain); domain->entities_in_main_sdr = entities; } } int ipmi_domain_add_entity_update_handler(ipmi_domain_t *domain, ipmi_domain_entity_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); return ipmi_entity_info_add_update_handler(domain->entities, handler, cb_data); } int ipmi_domain_remove_entity_update_handler(ipmi_domain_t *domain, ipmi_domain_entity_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); return ipmi_entity_info_remove_update_handler(domain->entities, handler, cb_data); } int ipmi_domain_add_entity_update_handler_cl(ipmi_domain_t *domain, ipmi_domain_entity_cl_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); return ipmi_entity_info_add_update_handler_cl(domain->entities, handler, cb_data); } int ipmi_domain_remove_entity_update_handler_cl(ipmi_domain_t *domain, ipmi_domain_entity_cl_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); return ipmi_entity_info_remove_update_handler_cl(domain->entities, handler, cb_data); } int ipmi_domain_iterate_entities(ipmi_domain_t *domain, ipmi_entity_ptr_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); ipmi_entities_iterate_entities(domain->entities, handler, cb_data); return 0; } int ipmi_domain_iterate_mcs(ipmi_domain_t *domain, ipmi_domain_iterate_mcs_cb handler, void *cb_data) { int i, j; CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->mc_lock); for (i=0; isys_intf_mcs[i]; if (mc && !i_ipmi_mc_get(mc)) { ipmi_unlock(domain->mc_lock); handler(domain, mc, cb_data); i_ipmi_mc_put(mc); ipmi_lock(domain->mc_lock); } } for (i=0; iipmb_mcs[i]); for (j=0; jsize; j++) { ipmi_mc_t *mc = tab->mcs[j]; if (mc && !i_ipmi_mc_get(mc)) { ipmi_unlock(domain->mc_lock); handler(domain, mc, cb_data); i_ipmi_mc_put(mc); ipmi_lock(domain->mc_lock); } } } ipmi_unlock(domain->mc_lock); return 0; } int ipmi_domain_iterate_mcs_rev(ipmi_domain_t *domain, ipmi_domain_iterate_mcs_cb handler, void *cb_data) { int i, j; CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->mc_lock); for (i=IPMB_HASH-1; i>=0; i--) { mc_table_t *tab = &(domain->ipmb_mcs[i]); for (j=tab->size-1; j>=0; j--) { ipmi_mc_t *mc = tab->mcs[j]; if (mc && !i_ipmi_mc_get(mc)) { ipmi_unlock(domain->mc_lock); handler(domain, mc, cb_data); i_ipmi_mc_put(mc); ipmi_lock(domain->mc_lock); } } } for (i=MAX_CONS-1; i>=0; i--) { ipmi_mc_t *mc = domain->sys_intf_mcs[i]; if (mc && !i_ipmi_mc_get(mc)) { ipmi_unlock(domain->mc_lock); handler(domain, mc, cb_data); i_ipmi_mc_put(mc); ipmi_lock(domain->mc_lock); } } ipmi_unlock(domain->mc_lock); return 0; } #if SAVE_SDR_CODE_ENABLE typedef struct sdrs_saved_info_s { ipmi_domain_t *domain; ipmi_domain_cb done; void *cb_data; } sdrs_saved_info_t; static void sdrs_saved(ipmi_sdr_info_t *sdrs, int err, void *cb_data) { sdrs_saved_info_t *info = cb_data; info->done(info->domain, err, info->cb_data); ipmi_mem_free(info); } int ipmi_domain_store_entities(ipmi_domain_t *domain, ipmi_domain_cb done, void *cb_data) { int rv; ipmi_sdr_info_t *stored_sdrs; sdrs_saved_info_t *info; /* FIXME - this is certainly broken. */ CHECK_DOMAIN_LOCK(domain); info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; /* Create an SDR repository to store. */ rv = ipmi_sdr_info_alloc(domain, NULL, 0, 0, &stored_sdrs); if (rv) { ipmi_mem_free(info); return rv; } /* Now store a channel SDR in case we are less than 1.5. */ { ipmi_sdr_t sdr; int i; sdr.major_version = 1; sdr.minor_version = 0; sdr.type = 0x14; /* */ sdr.length = 11; for (i=0; i<8; i++) { /* FIXME - what about the LUN and transmit support? */ if (domain->chan[i].protocol) { sdr.data[i] = (domain->chan[i].protocol | (domain->chan[i].xmit_support << 7) | (domain->chan[i].recv_lun << 4)); } else { sdr.data[i] = 0; } } sdr.data[8] = domain->msg_int_type; sdr.data[9] = domain->event_msg_int_type; sdr.data[10] = 0; rv = ipmi_sdr_add(stored_sdrs, &sdr); if (rv) goto out_err; } rv = ipmi_entity_append_to_sdrs(domain->entities, stored_sdrs); if (rv) goto out_err; info->domain = domain; info->done = done; info->cb_data = cb_data; rv = ipmi_sdr_save(stored_sdrs, sdrs_saved, info); out_err: if (rv) ipmi_mem_free(info); ipmi_sdr_info_destroy(stored_sdrs, NULL, NULL); return rv; } #endif /*********************************************************************** * * ID handling * **********************************************************************/ ipmi_domain_id_t ipmi_domain_convert_to_id(ipmi_domain_t *domain) { ipmi_domain_id_t val; CHECK_DOMAIN_LOCK(domain); val.domain = domain; return val; } int ipmi_domain_pointer_cb(ipmi_domain_id_t id, ipmi_domain_ptr_cb handler, void *cb_data) { int rv; ipmi_domain_t *domain; domain = id.domain; rv = i_ipmi_domain_get(domain); if (!rv) { handler(domain, cb_data); i_ipmi_domain_put(domain); } return rv; } int ipmi_cmp_domain_id(ipmi_domain_id_t id1, ipmi_domain_id_t id2) { if (id1.domain > id2.domain) return 1; if (id1.domain < id2.domain) return -1; return 0; } void ipmi_domain_id_set_invalid(ipmi_domain_id_t *id) { id->domain = NULL; } int ipmi_domain_id_is_invalid(const ipmi_domain_id_t *id) { return (id->domain == NULL); } /*********************************************************************** * * Handle global OEM callbacks for new domain MCs. * **********************************************************************/ typedef struct mc_oem_handlers_s { unsigned int manufacturer_id; unsigned int first_product_id; unsigned int last_product_id; ipmi_oem_domain_match_handler_cb handler; ipmi_oem_domain_shutdown_handler_cb shutdown; void *cb_data; } mc_oem_handlers_t; static locked_list_t *mc_oem_handlers; int ipmi_domain_register_oem_handler(unsigned int manufacturer_id, unsigned int product_id, ipmi_oem_domain_match_handler_cb handler, ipmi_oem_domain_shutdown_handler_cb shutdown, void *cb_data) { mc_oem_handlers_t *new_item; int rv; /* This might be called before initialization, so be 100% sure. */ rv = i_ipmi_domain_init(); if (rv) return rv; new_item = ipmi_mem_alloc(sizeof(*new_item)); if (!new_item) return ENOMEM; new_item->manufacturer_id = manufacturer_id; new_item->first_product_id = product_id; new_item->last_product_id = product_id; new_item->handler = handler; new_item->shutdown = shutdown; new_item->cb_data = cb_data; if (! locked_list_add(mc_oem_handlers, new_item, NULL)) { ipmi_mem_free(new_item); return ENOMEM; } return 0; } int ipmi_domain_register_oem_handler_range(unsigned int manufacturer_id, unsigned int first_product_id, unsigned int last_product_id, ipmi_oem_domain_match_handler_cb handler, ipmi_oem_domain_shutdown_handler_cb shutdown, void *cb_data) { mc_oem_handlers_t *new_item; int rv; /* This might be called before initialization, so be 100% sure. */ rv = i_ipmi_mc_init(); if (rv) return rv; new_item = ipmi_mem_alloc(sizeof(*new_item)); if (!new_item) return ENOMEM; new_item->manufacturer_id = manufacturer_id; new_item->first_product_id = first_product_id; new_item->last_product_id = last_product_id; new_item->handler = handler; new_item->shutdown = shutdown; new_item->cb_data = cb_data; if (! locked_list_add(mc_oem_handlers, new_item, NULL)) { ipmi_mem_free(new_item); return ENOMEM; } return 0; } typedef struct handler_cmp_s { int rv; unsigned int manufacturer_id; unsigned int first_product_id; unsigned int last_product_id; ipmi_domain_t *domain; } handler_cmp_t; static int mc_oem_handler_cmp_dereg(void *cb_data, void *item1, void *item2) { mc_oem_handlers_t *hndlr = item1; handler_cmp_t *cmp = cb_data; if ((hndlr->manufacturer_id == cmp->manufacturer_id) && (hndlr->first_product_id <= cmp->first_product_id) && (hndlr->last_product_id >= cmp->last_product_id)) { cmp->rv = 0; locked_list_remove(mc_oem_handlers, item1, item2); ipmi_mem_free(hndlr); return LOCKED_LIST_ITER_STOP; } return LOCKED_LIST_ITER_CONTINUE; } int ipmi_domain_deregister_oem_handler(unsigned int manufacturer_id, unsigned int product_id) { handler_cmp_t tmp; tmp.rv = ENOENT; tmp.manufacturer_id = manufacturer_id; tmp.first_product_id = product_id; tmp.last_product_id = product_id; locked_list_iterate(mc_oem_handlers, mc_oem_handler_cmp_dereg, &tmp); return tmp.rv; } int ipmi_domain_deregister_oem_handler_range(unsigned int manufacturer_id, unsigned int first_product_id, unsigned int last_product_id) { handler_cmp_t tmp; tmp.rv = ENOENT; tmp.manufacturer_id = manufacturer_id; tmp.first_product_id = first_product_id; tmp.last_product_id = last_product_id; locked_list_iterate(mc_oem_handlers, mc_oem_handler_cmp_dereg, &tmp); return tmp.rv; } static int mc_oem_handler_call(void *cb_data, void *item1, void *item2) { mc_oem_handlers_t *hndlr = item1; handler_cmp_t *cmp = cb_data; if ((hndlr->manufacturer_id == cmp->manufacturer_id) && (hndlr->first_product_id <= cmp->first_product_id) && (hndlr->last_product_id >= cmp->last_product_id)) { cmp->rv = hndlr->handler(cmp->domain, hndlr->cb_data); return LOCKED_LIST_ITER_STOP; } return LOCKED_LIST_ITER_CONTINUE; } static int check_mc_oem_handlers(ipmi_domain_t *domain) { handler_cmp_t tmp; tmp.rv = 0; tmp.manufacturer_id = ipmi_mc_manufacturer_id(domain->si_mc); tmp.first_product_id = ipmi_mc_product_id(domain->si_mc); tmp.last_product_id = tmp.first_product_id; tmp.domain = domain; locked_list_iterate(mc_oem_handlers, mc_oem_handler_call, &tmp); return tmp.rv; } int ipmi_domain_set_sdrs_fixup_handler(ipmi_domain_t *domain, ipmi_domain_oem_fixup_sdrs_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); domain->fixup_sdrs_handler = handler; domain->fixup_sdrs_cb_data = cb_data; return 0; } /*********************************************************************** * * Connection setup and handling * **********************************************************************/ int ipmi_domain_set_main_SDRs_read_handler(ipmi_domain_t *domain, ipmi_domain_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->domain_lock); domain->SDRs_read_handler = handler; domain->SDRs_read_handler_cb_data = cb_data; ipmi_unlock(domain->domain_lock); return 0; } int ipmi_domain_set_con_up_handler(ipmi_domain_t *domain, ipmi_domain_ptr_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->domain_lock); domain->con_up_handler = handler; domain->con_up_handler_cb_data = cb_data; ipmi_unlock(domain->domain_lock); return 0; } int ipmi_domain_set_bus_scan_handler(ipmi_domain_t *domain, ipmi_domain_cb handler, void *cb_data) { CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->mc_lock); domain->bus_scan_handler = handler; domain->bus_scan_handler_cb_data = cb_data; ipmi_unlock(domain->mc_lock); return 0; } static void conn_close(ipmi_con_t *ipmi, void *cb_data) { ipmi_domain_close_done_cb close_done; void *my_cb_data; ipmi_domain_t *domain = cb_data; int done = 0; ipmi_lock(domain->domain_lock); domain->close_count--; done = domain->close_count <= 0; ipmi_unlock(domain->domain_lock); if (!done) return; remove_known_domain(domain); close_done = domain->close_done; my_cb_data = domain->close_done_cb_data; cleanup_domain(domain); if (close_done) close_done(my_cb_data); } static void real_close_connection(ipmi_domain_t *domain) { ipmi_con_t *ipmi[MAX_CONS]; int i; for (i=0; iconn[i]; if (!ipmi[i]) continue; /* Remove all the handlers. */ domain->conn[i]->remove_event_handler(domain->conn[i], ll_event_handler, domain); domain->conn[i]->remove_con_change_handler(domain->conn[i], ll_con_changed, domain); domain->conn[i]->remove_ipmb_addr_handler(domain->conn[i], ll_addr_changed, domain); domain->conn[i] = NULL; } /* No lock needed here, this is single threaded until we start actually closing the connections. */ domain->close_count = 0; for (i=0; iclose_count++; } for (i=0; iregister_stat_handler) ipmi[i]->unregister_stat_handler(ipmi[i], domain->con_stat_info); ipmi[i]->close_connection_done(ipmi[i], conn_close, domain); } } } int ipmi_domain_close(ipmi_domain_t *domain, ipmi_domain_close_done_cb close_done, void *cb_data) { CHECK_DOMAIN_LOCK(domain); if (domain->in_shutdown) return EINVAL; domain->in_shutdown = 1; domain->close_done = close_done; domain->close_done_cb_data = cb_data; locked_list_remove(domains_list, domain, NULL); /* We don't actually do the destroy here, since the domain should be in use. We wait until the usecount goes to zero. */ return 0; } int ipmi_domain_add_connect_change_handler(ipmi_domain_t *domain, ipmi_domain_con_cb handler, void *cb_data) { if (locked_list_add(domain->con_change_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_connect_change_handler(ipmi_domain_t *domain, ipmi_domain_con_cb handler, void *cb_data) { if (locked_list_remove(domain->con_change_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct con_change_cl_info_s { ipmi_domain_con_cb handler; void *handler_data; } con_change_cl_info_t; static int iterate_con_change_cl(void *cb_data, void *item1, void *item2) { con_change_cl_info_t *info = cb_data; ipmi_domain_con_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_con_change_cl_handlers(ipmi_domain_t *domain, ipmi_domain_con_cb handler, void *handler_data) { con_change_cl_info_t info; info.handler = handler; info.handler_data = handler_data; locked_list_iterate(domain->con_change_cl_handlers, iterate_con_change_cl, &info); } int ipmi_domain_add_connect_change_handler_cl(ipmi_domain_t *domain, ipmi_domain_con_cl_cb handler, void *cb_data) { if (locked_list_add(domain->con_change_cl_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_connect_change_handler_cl(ipmi_domain_t *domain, ipmi_domain_con_cl_cb handler, void *cb_data) { if (locked_list_remove(domain->con_change_cl_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct con_change_info_s { ipmi_domain_t *domain; int err; unsigned int conn_num; unsigned int port_num; int still_connected; } con_change_info_t; static int iterate_con_changes(void *cb_data, void *item1, void *item2) { con_change_info_t *info = cb_data; ipmi_domain_con_cb handler = item1; handler(info->domain, info->err, info->conn_num, info->port_num, info->still_connected, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_con_change(ipmi_domain_t *domain, int err, unsigned int conn_num, unsigned int port_num, int still_connected) { con_change_info_t info = {domain, err, conn_num, port_num, still_connected}; locked_list_iterate(domain->con_change_handlers, iterate_con_changes, &info); } static void call_con_fails(ipmi_domain_t *domain, int err, unsigned int conn_num, unsigned int port_num, int still_connected) { ipmi_lock(domain->con_lock); domain->connecting = 0; if (err) { /* Nothing really to do, can't start anything up, just report it. */ ipmi_unlock(domain->con_lock); } else if (domain->in_startup) { domain->in_startup = 0; ipmi_unlock(domain->con_lock); } else ipmi_unlock(domain->con_lock); call_con_change(domain, err, conn_num, port_num, still_connected); } static void con_up_complete(ipmi_domain_t *domain) { int i, j; ipmi_domain_ptr_cb con_up_handler; void *con_up_handler_cb_data; ipmi_domain_cb SDRs_read_handler; void *SDRs_read_handler_cb_data; if (domain->in_shutdown) return; /* This is an unusual looking piece of code, but is required for systems that do not implement the get channel command. For those, it is required that channel 0 be an IPMB channel. Basically, if all of the channel info commands failed, set channel 0 to IPMB. */ for (i=0; ichan_set[i]) break; } if (i == MAX_IPMI_USED_CHANNELS) { domain->chan[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; domain->chan[0].xmit_support = 1; domain->chan[0].recv_lun = 0; domain->chan[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; domain->chan[0].session_support = IPMI_CHANNEL_SESSION_LESS; domain->chan[0].vendor_id = IPMI_ENTERPRISE_NUMBER; domain->chan[0].aux_info = 0; } domain->connection_up = 1; if (domain->working_conn != -1) domain->con_up[domain->working_conn] = 1; for (i=0; iport_up[j][i] == 1) call_con_fails(domain, 0, i, j, 1); } } ipmi_lock(domain->domain_lock); con_up_handler = domain->con_up_handler; con_up_handler_cb_data = domain->con_up_handler_cb_data; ipmi_unlock(domain->domain_lock); if (con_up_handler) con_up_handler(domain, con_up_handler_cb_data); ipmi_domain_start_full_ipmb_scan(domain); ipmi_detect_ents_presence_changes(domain->entities, 1); ipmi_entity_scan_sdrs(domain, NULL, domain->entities, domain->main_sdrs); ipmi_sensor_handle_sdrs(domain, NULL, domain->main_sdrs); ipmi_lock(domain->domain_lock); SDRs_read_handler = domain->SDRs_read_handler; SDRs_read_handler_cb_data = domain->SDRs_read_handler_cb_data; ipmi_unlock(domain->domain_lock); if (SDRs_read_handler) SDRs_read_handler(domain, 0, SDRs_read_handler_cb_data); i_ipmi_entities_report_sdrs_read(domain->entities); i_ipmi_put_domain_fully_up(domain, "con_up_complete"); } static void chan_info_rsp_handler(ipmi_mc_t *mc, ipmi_msg_t *rsp, void *rsp_data) { int rv = 0; long curr = (long) rsp_data; ipmi_domain_t *domain; if (!mc) return; domain = ipmi_mc_get_domain(mc); if (rsp->data[0] != 0) { rv = IPMI_IPMI_ERR_VAL(rsp->data[0]); } else if (rsp->data_len < 8) { rv = EINVAL; } if (rv) { /* Got an error, invalidate the channel. IPMI requires that 1.5 systems implement this command if they have channels. */ memset(&domain->chan[curr], 0, sizeof(domain->chan[curr])); /* Keep going, there may be more channels. */ } else { domain->chan_set[curr] = 1; /* Get the info from the channel info response. */ domain->chan[curr].medium = rsp->data[2] & 0x7f; domain->chan[curr].xmit_support = rsp->data[2] >> 7; domain->chan[curr].recv_lun = (rsp->data[2] >> 4) & 0x7; domain->chan[curr].protocol = rsp->data[3] & 0x1f; domain->chan[curr].session_support = rsp->data[4] >> 6; domain->chan[curr].vendor_id = (rsp->data[5] | (rsp->data[6] << 8) | (rsp->data[7] << 16)); domain->chan[curr].aux_info = rsp->data[8] | (rsp->data[9] << 8); } curr++; if (curr < MAX_IPMI_USED_CHANNELS) { ipmi_msg_t cmd_msg; unsigned char cmd_data[1]; cmd_msg.netfn = IPMI_APP_NETFN; cmd_msg.cmd = IPMI_GET_CHANNEL_INFO_CMD; cmd_msg.data = cmd_data; cmd_msg.data_len = 1; cmd_data[0] = curr; rv = ipmi_mc_send_command(mc, 0, &cmd_msg, chan_info_rsp_handler, (void *) curr); } else { goto chan_info_done; } if (rv) { call_con_fails(domain, rv, 0, 0, 0); return; } return; chan_info_done: domain->msg_int_type = 0xff; domain->event_msg_int_type = 0xff; con_up_complete(domain); } static int get_channels(ipmi_domain_t *domain) { int rv; if (domain->in_shutdown) return ECANCELED; if ((domain->major_version > 1) || ((domain->major_version == 1) && (domain->minor_version >= 5))) { ipmi_msg_t cmd_msg; unsigned char cmd_data[1]; /* IPMI 1.5 or later, use a get channel command. */ cmd_msg.netfn = IPMI_APP_NETFN; cmd_msg.cmd = IPMI_GET_CHANNEL_INFO_CMD; cmd_msg.data = cmd_data; cmd_msg.data_len = 1; cmd_data[0] = 0; i_ipmi_mc_get(domain->si_mc); rv = ipmi_mc_send_command(domain->si_mc, 0, &cmd_msg, chan_info_rsp_handler, (void *) 0); i_ipmi_mc_put(domain->si_mc); } else { ipmi_sdr_t sdr; /* Get the channel info record. */ rv = ipmi_get_sdr_by_type(domain->main_sdrs, 0x14, &sdr); if (rv) { domain->chan_set[0] = 1; /* Add a dummy channel zero and finish. */ domain->chan[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; domain->chan[0].xmit_support = 1; domain->chan[0].recv_lun = 0; domain->chan[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; domain->chan[0].session_support = IPMI_CHANNEL_SESSION_LESS; domain->chan[0].vendor_id = IPMI_ENTERPRISE_NUMBER; domain->chan[0].aux_info = 0; domain->msg_int_type = 0xff; domain->event_msg_int_type = 0xff; domain->msg_int_type = 0xff; domain->event_msg_int_type = 0xff; rv = 0; } else { int i; for (i=0; ichan_set[i] = 1; domain->chan[i].medium = IPMI_CHANNEL_MEDIUM_IPMB; domain->chan[i].xmit_support = 1; domain->chan[i].recv_lun = 0; domain->chan[i].protocol = protocol; domain->chan[i].session_support= IPMI_CHANNEL_SESSION_LESS; domain->chan[i].vendor_id = IPMI_ENTERPRISE_NUMBER; domain->chan[i].aux_info = 0; } } domain->msg_int_type = sdr.data[8]; domain->event_msg_int_type = sdr.data[9]; } con_up_complete(domain); } return rv; } static void sdr_handler(ipmi_sdr_info_t *sdrs, int err, int changed, unsigned int count, void *cb_data) { ipmi_domain_t *domain = cb_data; int rv; if (err) { /* Just report an error, it shouldn't be a big deal if this fails. */ ipmi_log(IPMI_LOG_WARNING, "%sdomain.c(sdr_handler): " "Could not get main SDRs, error 0x%x", DOMAIN_NAME(domain), err); } if (domain->fixup_sdrs_handler) domain->fixup_sdrs_handler(domain, domain->main_sdrs, domain->fixup_sdrs_cb_data); rv = get_channels(domain); if (rv) call_con_fails(domain, rv, 0, 0, 0); } static void got_guid(ipmi_mc_t *mc, ipmi_msg_t *rsp, void *rsp_data) { ipmi_domain_t *domain = rsp_data; int rv; if (!mc) return; /* domain went away while processing. */ if ((rsp->data[0] == 0) && (rsp->data_len >= 17)) { /* We have a GUID, save it */ ipmi_mc_set_guid(mc, rsp->data+1); } if (domain->SDR_repository_support && ipmi_option_SDRs(domain)) { rv = ipmi_sdr_fetch(domain->main_sdrs, sdr_handler, domain); } else { rv = get_channels(domain); } if (rv) call_con_fails(domain, rv, 0, 0, 0); } static void domain_oem_handlers_checked(ipmi_domain_t *domain, int err, void *cb_data) { ipmi_msg_t msg; int rv; /* FIXME - handle errors setting up OEM comain information. */ msg.netfn = IPMI_APP_NETFN; msg.cmd = IPMI_GET_SYSTEM_GUID_CMD; msg.data_len = 0; msg.data = NULL; i_ipmi_mc_get(domain->si_mc); rv = ipmi_mc_send_command(domain->si_mc, 0, &msg, got_guid, domain); i_ipmi_mc_put(domain->si_mc); if (rv) call_con_fails(domain, rv, 0, 0, 0); } static void got_dev_id(ipmi_mc_t *mc, ipmi_msg_t *rsp, void *rsp_data) { ipmi_domain_t *domain = rsp_data; int rv; if (!mc) return; /* domain went away while processing. */ rv = i_ipmi_mc_get_device_id_data_from_rsp(mc, rsp); if (rv) { /* At least the get device id has to work. */ if ((rsp->data[0] == 0) && (rsp->data_len >= 6)) { int major_version = rsp->data[5] & 0xf; int minor_version = (rsp->data[5] >> 4) & 0xf; if (major_version < 1) { ipmi_log(IPMI_LOG_ERR_INFO, "%sdomain.c(got_dev_id): " "IPMI version of the BMC is %d.%d, which is older" " than OpenIPMI supports", DOMAIN_NAME(domain), major_version, minor_version); domain->got_invalid_dev_id = 1; call_con_fails(domain, ENOSYS, 0, 0, 0); return; } } ipmi_log(IPMI_LOG_ERR_INFO, "%sdomain.c(got_dev_id): " "Invalid return from IPMI Get Device ID, something is" " seriously wrong with the BMC", DOMAIN_NAME(domain)); domain->got_invalid_dev_id = 1; call_con_fails(domain, rv, 0, 0, 0); return; } domain->got_invalid_dev_id = 0; /* Get the information from the MC, not the message, since it may have been fixed up. */ domain->major_version = ipmi_mc_major_version(mc); domain->minor_version = ipmi_mc_minor_version(mc); domain->SDR_repository_support = ipmi_mc_sdr_repository_support(mc); if (((domain->major_version < 1) || (domain->major_version > 2)) || ((domain->major_version == 1) && (domain->minor_version != 5) && (domain->minor_version != 0)) || ((domain->major_version == 2) && (domain->minor_version != 0))) { ipmi_log(IPMI_LOG_WARNING, "%sdomain.c(got_dev_id): " "IPMI version of the BMC is %d.%d, which is not directly" " supported by OpenIPMI. It may work, but there may be" " issues.", DOMAIN_NAME(domain), domain->major_version, domain->minor_version); } if (domain->major_version < 1) { /* We only support 1.0 and greater. */ domain->got_invalid_dev_id = 0; call_con_fails(domain, EINVAL, 0, 0, 0); return; } if (ipmi_option_OEM_init(domain)) { rv = check_oem_handlers(domain, domain_oem_handlers_checked, NULL); if (rv) call_con_fails(domain, rv, 0, 0, 0); rv = check_mc_oem_handlers(domain); if (rv) call_con_fails(domain, rv, 0, 0, 0); } else { domain_oem_handlers_checked(domain, 0, NULL); } } static int domain_send_mc_id(ipmi_domain_t *domain) { ipmi_msg_t msg; int rv; msg.netfn = IPMI_APP_NETFN; msg.cmd = IPMI_GET_DEVICE_ID_CMD; msg.data_len = 0; msg.data = NULL; i_ipmi_mc_get(domain->si_mc); rv = ipmi_mc_send_command(domain->si_mc, 0, &msg, got_dev_id, domain); i_ipmi_mc_put(domain->si_mc); return rv; } static int start_con_up(ipmi_domain_t *domain) { ipmi_lock(domain->con_lock); if (domain->connecting || domain->connection_up) { ipmi_unlock(domain->con_lock); return 0; } domain->connecting = 1; ipmi_unlock(domain->con_lock); return domain_send_mc_id(domain); } static void start_activate_timer(ipmi_domain_t *domain); static void initial_ipmb_addr_cb(ipmi_con_t *ipmi, int err, const unsigned char ipmb_addr[], unsigned int num_ipmb_addr, int active, unsigned int hacks, void *cb_data) { ipmi_domain_t *domain = cb_data; int u; int rv; rv = i_ipmi_domain_get(domain); if (rv) /* So the connection failed. So what, there's nothing to talk to. */ return; u = get_con_num(domain, ipmi); if (u == -1) goto out_unlock; if (err) { call_con_fails(domain, err, u, 0, domain->connection_up); goto out_unlock; } /* If we are not activating connections, just use whatever we get and don't worry if it is active or not. */ if (! domain->option_activate_if_possible) active = 1; if (active) { domain->working_conn = u; rv = start_con_up(domain); if (rv) call_con_fails(domain, rv, u, 0, domain->connection_up); } else { /* Start the timer to activate the connection, if necessary. */ start_activate_timer(domain); } out_unlock: i_ipmi_domain_put(domain); } static void activate_timer_cb(void *cb_data, os_hnd_timer_id_t *id) { activate_timer_info_t *info = cb_data; ipmi_domain_t *domain = info->domain; int to_activate; int u; int rv; ipmi_lock(info->lock); if (info->cancelled) { info->os_hnd->free_timer(info->os_hnd, id); ipmi_unlock(info->lock); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); return; } info->running = 0; rv = i_ipmi_domain_get(domain); if (rv) /* Domain is gone, just give up. */ goto out_unlock; /* If no one is active, activate one. */ to_activate = -1; for (u=0; uconn[u] || !domain->con_up[u]) { continue; } if (domain->con_active[u]) { to_activate = u; break; } to_activate = u; } u = to_activate; if ((u != -1) && domain->option_activate_if_possible && ! domain->con_active[u] && domain->conn[u]->set_active_state) { /* If we didn't find an active connection, but we found a working one, activate it. Note that we may re-activate the connection that just went inactive if it is the only working connection. */ domain->conn[u]->set_active_state( domain->conn[u], 1, ll_addr_changed, domain); } i_ipmi_domain_put(domain); out_unlock: ipmi_unlock(info->lock); } int ipmi_domain_activate_connection(ipmi_domain_t *domain, unsigned int connection) { CHECK_DOMAIN_LOCK(domain); if ((connection >= MAX_CONS) || !domain->conn[connection]) return EINVAL; if (!domain->conn[connection]->set_active_state || !domain->option_activate_if_possible) return ENOSYS; domain->conn[connection]->set_active_state(domain->conn[connection], 1, ll_addr_changed, domain); /* The other connections will be deactivated when this one activates, if that is required. */ return 0; } int ipmi_domain_is_connection_active(ipmi_domain_t *domain, unsigned int connection, unsigned int *active) { CHECK_DOMAIN_LOCK(domain); if ((connection >= MAX_CONS) || !domain->conn[connection]) return EINVAL; *active = domain->con_active[connection]; return 0; } int ipmi_domain_is_connection_up(ipmi_domain_t *domain, unsigned int connection, unsigned int *up) { int port; unsigned int val; CHECK_DOMAIN_LOCK(domain); if ((connection >= MAX_CONS) || !domain->conn[connection]) return EINVAL; val = 0; for (port=0; portport_up[port][connection] == 1) val = 1; } *up = val; return 0; } int ipmi_domain_num_connection_ports(ipmi_domain_t *domain, unsigned int connection, unsigned int *ports) { int port; unsigned int val = 0; CHECK_DOMAIN_LOCK(domain); if ((connection >= MAX_CONS) || !domain->conn[connection]) return EINVAL; for (port=0; portport_up[port][connection] != -1) val = port+1; } *ports = val; return 0; } int ipmi_domain_is_connection_port_up(ipmi_domain_t *domain, unsigned int connection, unsigned int port, unsigned int *up) { CHECK_DOMAIN_LOCK(domain); if ((connection >= MAX_CONS) || !domain->conn[connection]) return EINVAL; if (port >= MAX_PORTS_PER_CON) return EINVAL; if (domain->port_up[port][connection] == -1) return ENOSYS; *up = domain->port_up[port][connection]; return 0; } int ipmi_domain_get_port_info(ipmi_domain_t *domain, unsigned int connection, unsigned int port, char *info, int *info_len) { CHECK_DOMAIN_LOCK(domain); if ((connection >= MAX_CONS) || !domain->conn[connection]) return EINVAL; if (port >= MAX_PORTS_PER_CON) return EINVAL; if (!domain->conn[connection]->get_port_info) return ENOSYS; return domain->conn[connection]->get_port_info(domain->conn[connection], port, info, info_len); } int i_ipmi_domain_get_connection(ipmi_domain_t *domain, int con_num, ipmi_con_t **con) { if (con_num >= MAX_CONS) return EINVAL; *con = domain->conn[con_num]; return 0; } void ipmi_domain_iterate_connections(ipmi_domain_t *domain, ipmi_connection_ptr_cb handler, void *cb_data) { int i; CHECK_DOMAIN_LOCK(domain); for (i=0; iconn[i]) handler(domain, i, cb_data); } } ipmi_args_t * ipmi_domain_get_connection_args(ipmi_domain_t *domain, unsigned int con) { CHECK_DOMAIN_LOCK(domain); if (con >= MAX_CONS) return NULL; if (!domain->conn[con]) return NULL; if (! domain->conn[con]->get_startup_args) return NULL; return domain->conn[con]->get_startup_args(domain->conn[con]); } char * ipmi_domain_get_connection_type(ipmi_domain_t *domain, unsigned int connection) { CHECK_DOMAIN_LOCK(domain); if (connection >= MAX_CONS) return NULL; if (!domain->conn[connection]) return NULL; return domain->conn[connection]->con_type; } ipmi_con_t * ipmi_domain_get_connection(ipmi_domain_t *domain, unsigned int connection) { CHECK_DOMAIN_LOCK(domain); if (connection >= MAX_CONS) return NULL; if (!domain->conn[connection]) return NULL; if (! domain->conn[connection]->use_connection) return NULL; domain->conn[connection]->use_connection(domain->conn[connection]); return domain->conn[connection]; } /* If the activate timer is not running, then start it. This allows some time for other connections to become active before we go off and start activating things. We wait a random amount of time so that if we get into a war with another program about who is active, someone will eventually win. */ static void start_activate_timer(ipmi_domain_t *domain) { ipmi_lock(domain->activate_timer_info->lock); if (!domain->activate_timer_info->running) { struct timeval tv; domain->os_hnd->get_random(domain->os_hnd, &tv.tv_sec, sizeof(tv.tv_sec)); /* Wait a random value between 5 and 15 seconds */ tv.tv_sec = (tv.tv_sec % 10) + 5; tv.tv_usec = 0; domain->os_hnd->start_timer(domain->os_hnd, domain->activate_timer, &tv, activate_timer_cb, domain->activate_timer_info); domain->activate_timer_info->running = 1; } ipmi_unlock(domain->activate_timer_info->lock); } static void ll_addr_changed(ipmi_con_t *ipmi, int err, const unsigned char ipmb_addr[], unsigned int num_ipmb_addr, int active, unsigned int hacks, void *cb_data) { ipmi_domain_t *domain = cb_data; int rv; int u; int start_connection; unsigned char old_addr[MAX_IPMI_USED_CHANNELS]; unsigned int i; rv = i_ipmi_domain_get(domain); if (rv) /* So the connection failed. So what, there's nothing to talk to. */ return; if (err) goto out_unlock; u = get_con_num(domain, ipmi); if (u == -1) goto out_unlock; memcpy(old_addr, domain->con_ipmb_addr[u], sizeof(old_addr)); for (i=0; icon_ipmb_addr[u][i] = ipmb_addr[i]; } if (!domain->in_startup) { /* Only scan the IPMBs if we are not in startup. Otherwise things get reported before we are ready. */ for (i=0; icon_ipmb_addr[u] != 0) ipmi_start_ipmb_mc_scan(domain, i, old_addr[i], old_addr[i], NULL, NULL); } /* Scan the new address. Even though the address may not have changed, it may have changed modes and need to be rescanned. */ ipmi_start_ipmb_mc_scan(domain, i, ipmb_addr[i], ipmb_addr[i], NULL, NULL); } } /* If we are not activating connections, just use whatever we get and don't worry if it is active or not. */ if (! domain->option_activate_if_possible) active = 1; start_connection = (active && (first_active_con(domain) == -1)); if (domain->con_active[u] != active) { domain->con_active[u] = active; if (active) { /* Deactivate all the other connections, if they support it. */ for (u=0; uworking_conn || !domain->conn[u] || !domain->con_up[u]) { continue; } if (domain->conn[u]->set_active_state && domain->option_activate_if_possible) { domain->conn[u]->set_active_state( domain->conn[u], 0, ll_addr_changed, domain); } } } else { /* The connection went inactive, route message from it to the current working connection. */ reroute_cmds(domain, u, domain->working_conn); } } else if (active) { /* Always pick the last working active connection to use. */ domain->working_conn = u; } else if (domain->conn[u]->set_active_state && domain->option_activate_if_possible) { /* Start the timer to activate the connection, if necessary. */ start_activate_timer(domain); } if (start_connection) { /* We now have an active connection and we didn't before, attempt to start up the connection. */ rv = start_con_up(domain); if (rv) call_con_fails(domain, rv, u, 0, domain->connection_up); } out_unlock: i_ipmi_domain_put(domain); } static void ll_con_changed(ipmi_con_t *ipmi, int err, unsigned int port_num, int still_connected, void *cb_data) { ipmi_domain_t *domain = cb_data; int rv; int u; if (port_num >= MAX_PORTS_PER_CON) { ipmi_log(IPMI_LOG_SEVERE, "%sdomain.c(ll_con_changed): Got port number %d," " but %d is the max number of ports", DOMAIN_NAME(domain), port_num, MAX_PORTS_PER_CON); return; } rv = i_ipmi_domain_get(domain); if (rv) /* So the connection failed. So what, there's nothing to talk to. */ return; u = get_con_num(domain, ipmi); if (u == -1) goto out_unlock; if (err == ENOENT) domain->port_up[port_num][u] = -1; else if (err) domain->port_up[port_num][u] = 0; else domain->port_up[port_num][u] = 1; /* If we are not starting up, if we gain or lose a connection then scan the address. */ if ((!domain->in_startup) && (ipmi->scan_sysaddr)) ipmi_start_si_scan(domain, u, NULL, NULL); if (still_connected) { domain->con_up[u] = 1; if (domain->connecting) { /* If we are connecting, don't report it, it will be reported when the connection is finished. */ } else if (domain->connection_up) { /* We already have a connection, just report this. */ call_con_change(domain, err, u, port_num, domain->connection_up); } else { /* We don't have a working connection, so start up the process. */ domain->working_conn = u; if (domain->conn[u]->get_ipmb_addr) /* If we can fetch the IPMB address, see if this is an active connection first. */ rv = domain->conn[u]->get_ipmb_addr(domain->conn[u], initial_ipmb_addr_cb, domain); else /* When a connection comes back up, start the process of getting SDRs, scanning the bus, and the like. */ rv = start_con_up(domain); if (rv) call_con_fails(domain, rv, u, port_num, domain->connection_up); } } else { /* A connection failed, try to find a working connection and activate it, if necessary. */ domain->con_up[u] = 0; domain->working_conn = first_working_con(domain); if (domain->working_conn == -1) domain->connection_up = 0; else if ((!domain->con_active[domain->working_conn]) && (domain->conn[domain->working_conn]->set_active_state) && domain->option_activate_if_possible) { domain->conn[domain->working_conn]->set_active_state( domain->conn[domain->working_conn], 1, ll_addr_changed, domain); } else { reroute_cmds(domain, u, domain->working_conn); } call_con_fails(domain, err, u, port_num, domain->connection_up); } out_unlock: i_ipmi_domain_put(domain); } int ipmi_option_SDRs(ipmi_domain_t *domain) { return domain->option_all || domain->option_SDRs; } int ipmi_option_SEL(ipmi_domain_t *domain) { return domain->option_all || domain->option_SEL; } int ipmi_option_FRUs(ipmi_domain_t *domain) { return domain->option_all || domain->option_FRUs; } int ipmi_option_IPMB_scan(ipmi_domain_t *domain) { if (domain->option_local_only) return 0; return domain->option_all || domain->option_IPMB_scan; } int ipmi_option_OEM_init(ipmi_domain_t *domain) { return domain->option_all || domain->option_OEM_init; } int ipmi_option_set_event_rcvr(ipmi_domain_t *domain) { if (domain->option_local_only) return 0; return domain->option_set_event_rcvr; } int ipmi_option_set_sel_time(ipmi_domain_t *domain) { return domain->option_set_sel_time; } int ipmi_option_use_cache(ipmi_domain_t *domain) { return domain->option_use_cache; } int ipmi_option_activate_if_possible(ipmi_domain_t *domain) { return domain->option_activate_if_possible; } int ipmi_option_local_only(ipmi_domain_t *domain) { return domain->option_local_only; } void i_ipmi_option_set_local_only_if_not_specified(ipmi_domain_t *domain, int val) { if (domain->option_local_only_set) return; domain->option_local_only = val != 0; } int ipmi_open_domain(const char *name, ipmi_con_t *con[], unsigned int num_con, ipmi_domain_con_cb con_change_handler, void *con_change_cb_data, ipmi_domain_ptr_cb domain_fully_up, void *domain_fully_up_cb_data, ipmi_open_option_t *options, unsigned int num_options, ipmi_domain_id_t *new_domain) { int rv; ipmi_domain_t *domain = NULL; unsigned int i; if ((num_con < 1) || (num_con > MAX_CONS)) return EINVAL; rv = setup_domain(name, con, num_con, options, num_options, &domain); if (rv) return rv; domain->domain_fully_up = domain_fully_up; domain->domain_fully_up_cb_data = domain_fully_up_cb_data; domain->fully_up_count = 1; for (i=0; iadd_con_change_handler(con[i], ll_con_changed, domain); if (rv) goto out_err; rv = con[i]->add_ipmb_addr_handler(con[i], ll_addr_changed, domain); if (rv) goto out_err; } add_known_domain(domain); if (con_change_handler) { rv = ipmi_domain_add_connect_change_handler(domain, con_change_handler, con_change_cb_data); if (rv) goto out_err; } for (i=0; iget_num_ports) { int m = con[i]->get_num_ports(con[i]); int j; for (j=0; jport_up[j][i] = 0; } else /* Only one port 0 */ domain->port_up[0][i] = 0; rv = con[i]->start_con(con[i]); if (rv) break; } if (rv) goto out_err; if (new_domain) *new_domain = ipmi_domain_convert_to_id(domain); if (! locked_list_add(domains_list, domain, NULL)) { ipmi_log(IPMI_LOG_SEVERE, "%sdomain.c(sdr_handler): " "Out of memory, could not add domain to the domains list", DOMAIN_NAME(domain)); } call_domain_change(domain, IPMI_ADDED); i_ipmi_domain_put(domain); return rv; out_err: for (i=0; iremove_con_change_handler(con[i], ll_con_changed, domain); con[i]->remove_ipmb_addr_handler(con[i], ll_addr_changed, domain); if (con[i]->register_stat_handler) con[i]->unregister_stat_handler(con[i], domain->con_stat_info); } remove_known_domain(domain); cleanup_domain(domain); return rv; } /*********************************************************************** * * Handle misc data about domains. * **********************************************************************/ typedef struct domains_iter_s { ipmi_domain_ptr_cb handler; void *cb_data; } domains_iter_t; static int iterate_domains(void *cb_data, void *item1, void *item2) { domains_iter_t *info = cb_data; ipmi_domain_t *domain = item1; int rv; rv = i_ipmi_domain_get(domain); if (!rv) { info->handler(item1, info->cb_data); i_ipmi_domain_put(domain); } return LOCKED_LIST_ITER_CONTINUE; } void ipmi_domain_iterate_domains(ipmi_domain_ptr_cb handler, void *cb_data) { domains_iter_t info; if (!handler) return; if (!domains_list) return; info.handler = handler; info.cb_data = cb_data; locked_list_iterate(domains_list, iterate_domains, &info); } ipmi_sdr_info_t * ipmi_domain_get_main_sdrs(ipmi_domain_t *domain) { return domain->main_sdrs; } int ipmi_domain_get_num_channels(ipmi_domain_t *domain, int *val) { CHECK_DOMAIN_LOCK(domain); *val = MAX_IPMI_USED_CHANNELS; return 0; } int ipmi_domain_get_channel(ipmi_domain_t *domain, int index, ipmi_chan_info_t *chan) { CHECK_DOMAIN_LOCK(domain); if (index >= MAX_IPMI_USED_CHANNELS) return EINVAL; *chan = domain->chan[index]; return 0; } int ipmi_domain_get_guid(ipmi_domain_t *domain, unsigned char *guid) { int rv; i_ipmi_mc_get(domain->si_mc); rv = ipmi_mc_get_guid(domain->si_mc, guid); i_ipmi_mc_put(domain->si_mc); return rv; } int ipmi_domain_con_up(ipmi_domain_t *domain) { CHECK_DOMAIN_LOCK(domain); return domain->connection_up; } static void check_event_rcvr(ipmi_domain_t *domain, ipmi_mc_t *mc, void *cb_data) { unsigned int *addr = cb_data; if (*addr) return; if (!ipmi_mc_ipmb_event_receiver_support(mc)) return; if (ipmi_mc_get_channel(mc) == IPMI_BMC_CHANNEL) return; *addr = ipmi_mc_get_address(mc); } int ipmi_domain_get_event_rcvr(ipmi_domain_t *domain) { unsigned int addr = 0; ipmi_domain_iterate_mcs(domain, check_event_rcvr, &addr); return addr; } const char * i_ipmi_domain_name(const ipmi_domain_t *domain) { return domain->name; } int ipmi_domain_get_name(ipmi_domain_t *domain, char *name, int length) { int slen; if (length <= 0) return 0; /* Never changes, no lock needed. */ slen = strlen(domain->name); if (slen == 0) { if (name) *name = '\0'; goto out; } slen -= 1; /* Remove the trailing ' ' */ if (slen >= length) { slen = length - 1; } if (name) { memcpy(name, domain->name, slen); name[slen] = '\0'; } out: return slen; } void ipmi_domain_set_oem_data(ipmi_domain_t *domain, void *oem_data, ipmi_domain_destroy_oem_data_cb destroyer) { domain->oem_data = oem_data; domain->oem_data_destroyer = destroyer; } void * ipmi_domain_get_oem_data(ipmi_domain_t *domain) { return domain->oem_data; } enum ipmi_domain_type ipmi_domain_get_type(ipmi_domain_t *domain) { return domain->domain_type; } void ipmi_domain_set_type(ipmi_domain_t *domain, enum ipmi_domain_type dtype) { domain->domain_type = dtype; } unsigned int ipmi_domain_get_unique_num(ipmi_domain_t *domain) { unsigned int rv; ipmi_lock(domain->domain_lock); rv = domain->uniq_num; domain->uniq_num++; ipmi_unlock(domain->domain_lock); return rv; } /*OEM-specific sensors handling*/ int ipmi_domain_add_new_sensor_handler(ipmi_domain_t *domain, ipmi_domain_sensor_cb handler, void *cb_data) { if (locked_list_add(domain->new_sensor_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_domain_remove_new_sensor_handler(ipmi_domain_t *domain, ipmi_domain_sensor_cb handler, void *cb_data) { if (locked_list_remove(domain->new_sensor_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct new_sensor_handler_info_s { ipmi_domain_t *domain; ipmi_sensor_t *sensor; } new_sensor_handler_info_t; static int call_new_sensor_handler(void *cb_data, void *item1, void *item2) { new_sensor_handler_info_t *info = cb_data; ipmi_domain_sensor_cb handler = item1; handler(info->domain, info->sensor, item2); return 0; } int i_call_new_sensor_handlers(ipmi_domain_t *domain, ipmi_sensor_t *sensor) { new_sensor_handler_info_t info; info.domain = domain; info.sensor = sensor; locked_list_iterate(domain->new_sensor_handlers, call_new_sensor_handler, &info); return 0; } /*********************************************************************** * * Handling anonmymous attributes for domains * **********************************************************************/ struct ipmi_domain_attr_s { char *name; void *data; ipmi_lock_t *lock; unsigned int refcount; ipmi_domain_attr_kill_cb destroy; void *cb_data; }; static int destroy_attr(void *cb_data, void *item1, void *item2) { ipmi_domain_t *domain = cb_data; ipmi_domain_attr_t *attr = item1; locked_list_remove(domain->attr, item1, item2); ipmi_domain_attr_put(attr); return LOCKED_LIST_ITER_CONTINUE; } typedef struct domain_attr_cmp_s { char *name; ipmi_domain_attr_t *attr; } domain_attr_cmp_t; static int domain_attr_cmp(void *cb_data, void *item1, void *item2) { domain_attr_cmp_t *info = cb_data; ipmi_domain_attr_t *attr = item1; if (strcmp(info->name, attr->name) == 0) { info->attr = attr; return LOCKED_LIST_ITER_STOP; } return LOCKED_LIST_ITER_CONTINUE; } int ipmi_domain_register_attribute(ipmi_domain_t *domain, char *name, ipmi_domain_attr_init_cb init, ipmi_domain_attr_kill_cb destroy, void *cb_data, ipmi_domain_attr_t **attr) { ipmi_domain_attr_t *val = NULL; domain_attr_cmp_t info; int rv = 0; locked_list_entry_t *entry; info.name = name; info.attr = NULL; locked_list_lock(domain->attr); locked_list_iterate_nolock(domain->attr, domain_attr_cmp, &info); if (info.attr) { ipmi_lock(info.attr->lock); info.attr->refcount++; ipmi_unlock(info.attr->lock); *attr = info.attr; goto out_unlock; } val = ipmi_mem_alloc(sizeof(*val)); if (!val) { rv = ENOMEM; goto out_unlock; } val->name = ipmi_strdup(name); if (!val->name) { ipmi_mem_free(val); rv = ENOMEM; goto out_unlock; } entry = locked_list_alloc_entry(); if (!entry) { ipmi_mem_free(val->name); ipmi_mem_free(val); rv = ENOMEM; goto out_unlock; } rv = ipmi_create_lock(domain, &val->lock); if (rv) { locked_list_free_entry(entry); ipmi_mem_free(val->name); ipmi_mem_free(val); goto out_unlock; } val->refcount = 2; val->destroy = destroy; val->cb_data = cb_data; val->data = NULL; if (init) { rv = init(domain, cb_data, &val->data); if (rv) { ipmi_destroy_lock(val->lock); locked_list_free_entry(entry); ipmi_mem_free(val->name); ipmi_mem_free(val); rv = ENOMEM; goto out_unlock; } } locked_list_add_entry_nolock(domain->attr, val, NULL, entry); *attr = val; out_unlock: locked_list_unlock(domain->attr); return rv; } int ipmi_domain_find_attribute(ipmi_domain_t *domain, char *name, ipmi_domain_attr_t **attr) { domain_attr_cmp_t info; if (!domain->attr) return EINVAL; /* Attributes are immutable, no lock is required. */ info.name = name; info.attr = NULL; locked_list_iterate(domain->attr, domain_attr_cmp, &info); if (info.attr) { ipmi_lock(info.attr->lock); info.attr->refcount++; ipmi_unlock(info.attr->lock); *attr = info.attr; return 0; } return EINVAL; } void * ipmi_domain_attr_get_data(ipmi_domain_attr_t *attr) { return attr->data; } void ipmi_domain_attr_put(ipmi_domain_attr_t *attr) { ipmi_lock(attr->lock); attr->refcount--; if (attr->refcount > 0) { ipmi_unlock(attr->lock); return; } ipmi_unlock(attr->lock); if (attr->destroy) attr->destroy(attr->cb_data, attr->data); ipmi_destroy_lock(attr->lock); ipmi_mem_free(attr->name); ipmi_mem_free(attr); } typedef struct find_attr_s { char *name; ipmi_domain_attr_t **attr; int rv; } find_attr_t; static void find_attr_2(ipmi_domain_t *domain, void *cb_data) { find_attr_t *info = cb_data; info->rv = ipmi_domain_find_attribute(domain, info->name, info->attr); } int ipmi_domain_id_find_attribute(ipmi_domain_id_t domain_id, char *name, ipmi_domain_attr_t **attr) { find_attr_t info = { name, attr, 0 }; int rv; rv = ipmi_domain_pointer_cb(domain_id, find_attr_2, &info); if (!rv) rv = info.rv; return rv; } /*********************************************************************** * * Statistics * **********************************************************************/ struct ipmi_domain_stat_s { char *name; char *instance; ipmi_lock_t *lock; unsigned int count; ipmi_domain_stat_t *stat; unsigned int refcount; }; static int destroy_stat(void *cb_data, void *item1, void *item2) { ipmi_domain_t *domain = cb_data; ipmi_domain_stat_t *stat = item1; locked_list_remove(domain->stats, item1, item2); ipmi_domain_stat_put(stat); return LOCKED_LIST_ITER_CONTINUE; } typedef struct domain_stat_cmp_s { const char *name; const char *instance; ipmi_domain_stat_t *stat; } domain_stat_cmp_t; static int domain_stat_cmp(void *cb_data, void *item1, void *item2) { domain_stat_cmp_t *info = cb_data; ipmi_domain_stat_t *stat = item1; if ((strcmp(info->name, stat->name) == 0) && (strcmp(info->instance, stat->instance) == 0)) { info->stat = stat; return LOCKED_LIST_ITER_STOP; } return LOCKED_LIST_ITER_CONTINUE; } int ipmi_domain_stat_register(ipmi_domain_t *domain, const char *name, const char *instance, ipmi_domain_stat_t **stat) { ipmi_domain_stat_t *val = NULL; domain_stat_cmp_t info; int rv = 0; locked_list_entry_t *entry; info.name = name; info.instance = instance; info.stat = NULL; locked_list_lock(domain->stats); locked_list_iterate_nolock(domain->stats, domain_stat_cmp, &info); if (info.stat) { ipmi_lock(info.stat->lock); info.stat->refcount++; ipmi_unlock(info.stat->lock); *stat = info.stat; goto out_unlock; } val = ipmi_mem_alloc(sizeof(*val)); if (!val) { rv = ENOMEM; goto out_unlock; } val->name = ipmi_strdup(name); if (!val->name) { ipmi_mem_free(val); rv = ENOMEM; goto out_unlock; } val->instance = ipmi_strdup(instance); if (!val->instance) { ipmi_mem_free(val->name); ipmi_mem_free(val); rv = ENOMEM; goto out_unlock; } entry = locked_list_alloc_entry(); if (!entry) { ipmi_mem_free(val->instance); ipmi_mem_free(val->name); ipmi_mem_free(val); rv = ENOMEM; goto out_unlock; } rv = ipmi_create_lock(domain, &val->lock); if (rv) { locked_list_free_entry(entry); ipmi_mem_free(val->instance); ipmi_mem_free(val->name); ipmi_mem_free(val); goto out_unlock; } val->refcount = 2; val->count = 0; locked_list_add_entry_nolock(domain->stats, val, NULL, entry); *stat = val; out_unlock: locked_list_unlock(domain->stats); return 0; } int ipmi_domain_find_stat(ipmi_domain_t *domain, const char *name, const char *instance, ipmi_domain_stat_t **stat) { domain_stat_cmp_t info; int rv = ENOENT; info.name = name; info.instance = instance; info.stat = NULL; locked_list_lock(domain->stats); locked_list_iterate_nolock(domain->stats, domain_stat_cmp, &info); if (info.stat) { ipmi_lock(info.stat->lock); info.stat->refcount++; ipmi_unlock(info.stat->lock); *stat = info.stat; rv = 0; } locked_list_unlock(domain->stats); return rv; } void ipmi_domain_stat_put(ipmi_domain_stat_t *stat) { ipmi_lock(stat->lock); stat->refcount--; if (stat->refcount > 0) { ipmi_unlock(stat->lock); return; } ipmi_unlock(stat->lock); ipmi_destroy_lock(stat->lock); ipmi_mem_free(stat->name); ipmi_mem_free(stat->instance); ipmi_mem_free(stat); } void ipmi_domain_stat_add(ipmi_domain_stat_t *stat, int amount) { ipmi_lock(stat->lock); stat->count += amount; ipmi_unlock(stat->lock); } unsigned int ipmi_domain_stat_get(ipmi_domain_stat_t *stat) { unsigned int rv; ipmi_lock(stat->lock); rv = stat->count; ipmi_unlock(stat->lock); return rv; } unsigned int ipmi_domain_stat_get_and_zero(ipmi_domain_stat_t *stat) { unsigned int rv; ipmi_lock(stat->lock); rv = stat->count; stat->count = 0; ipmi_unlock(stat->lock); return rv; } const char * ipmi_domain_stat_get_name(ipmi_domain_stat_t *stat) { return stat->name; } const char * ipmi_domain_stat_get_instance(ipmi_domain_stat_t *stat) { return stat->instance; } typedef struct stat_iterate_s { ipmi_domain_t *domain; const char *name; const char *instance; ipmi_stat_cb handler; void *cb_data; } stat_iterate_t; static int domain_stat_iter_pre(void *cb_data, void *item1, void *item2) { stat_iterate_t *info = cb_data; ipmi_domain_stat_t *stat = item1; if (info->name && (strcmp(info->name, stat->name) != 0)) return LOCKED_LIST_ITER_SKIP; if (info->instance && (strcmp(info->instance, stat->instance) != 0)) return LOCKED_LIST_ITER_SKIP; ipmi_lock(stat->lock); stat->refcount++; ipmi_unlock(stat->lock); return LOCKED_LIST_ITER_CONTINUE; } static int domain_stat_iter(void *cb_data, void *item1, void *item2) { stat_iterate_t *info = cb_data; ipmi_domain_stat_t *stat = item1; /* Prefunc already matched, this is a good one. */ info->handler(info->domain, stat, info->cb_data); ipmi_domain_stat_put(stat); return LOCKED_LIST_ITER_CONTINUE; } void ipmi_domain_stat_iterate(ipmi_domain_t *domain, const char *name, const char *instance, ipmi_stat_cb handler, void *cb_data) { stat_iterate_t info; info.domain = domain; info.name = name; info.instance = instance; info.handler = handler; info.cb_data = cb_data; locked_list_iterate_prefunc(domain->stats, domain_stat_iter_pre, domain_stat_iter, &info); } /*********************************************************************** * * Initialization and shutdown * **********************************************************************/ int i_ipmi_domain_init(void) { int rv; if (domains_initialized) return 0; mc_oem_handlers = locked_list_alloc(ipmi_get_global_os_handler()); if (!mc_oem_handlers) return ENOMEM; domain_change_handlers = locked_list_alloc(ipmi_get_global_os_handler()); if (!domain_change_handlers) return ENOMEM; domains_list = locked_list_alloc(ipmi_get_global_os_handler()); if (!domains_list) { locked_list_destroy(domain_change_handlers); return ENOMEM; } oem_handlers = alloc_ilist(); if (!oem_handlers) { locked_list_destroy(domain_change_handlers); locked_list_destroy(domains_list); domains_list = NULL; return ENOMEM; } rv = ipmi_create_global_lock(&domains_lock); if (rv) { locked_list_destroy(domain_change_handlers); locked_list_destroy(domains_list); domains_list = NULL; free_ilist(oem_handlers); oem_handlers = NULL; return rv; } domains_initialized = 1; return 0; } void i_ipmi_domain_shutdown(void) { domains_initialized = 0; locked_list_destroy(domain_change_handlers); locked_list_destroy(mc_oem_handlers); locked_list_destroy(domains_list); domains_list = NULL; free_ilist(oem_handlers); oem_handlers = NULL; ipmi_destroy_lock(domains_lock); domains_lock = NULL; } /*********************************************************************** * * Cruft * **********************************************************************/ struct ipmi_domain_mc_upd_s { ipmi_domain_mc_upd_cb handler; void *cb_data; struct ipmi_domain_mc_upd_s *next, *prev; }; int ipmi_domain_register_mc_update_handler(ipmi_domain_t *domain, ipmi_domain_mc_upd_cb handler, void *cb_data, struct ipmi_domain_mc_upd_s **id) { struct ipmi_domain_mc_upd_s *info; int rv; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; rv = ipmi_domain_add_mc_updated_handler(domain, handler, cb_data); if (rv) { ipmi_mem_free(info); } else { info->handler = handler; info->cb_data = cb_data; ipmi_lock(domain->domain_lock); info->next = domain->mc_upd_cruft; info->prev = NULL; domain->mc_upd_cruft = info; ipmi_unlock(domain->domain_lock); if (id) *id = info; } return rv; } void ipmi_domain_remove_mc_update_handler(ipmi_domain_t *domain, struct ipmi_domain_mc_upd_s *id) { ipmi_domain_remove_mc_updated_handler(domain, id->handler, id->cb_data); ipmi_lock(domain->domain_lock); if (id->next) id->next->prev = id->prev; if (id->prev) id->prev->next = id->next; else domain->mc_upd_cruft = id->next; ipmi_unlock(domain->domain_lock); ipmi_mem_free(id); } struct ipmi_event_handler_id_s { ipmi_event_handler_cb handler; void *event_data; struct ipmi_event_handler_id_s *next, *prev; }; int ipmi_register_for_events(ipmi_domain_t *domain, ipmi_event_handler_cb handler, void *event_data, struct ipmi_event_handler_id_s **id) { struct ipmi_event_handler_id_s *info; int rv; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; rv = ipmi_domain_add_event_handler(domain, handler, event_data); if (rv) { ipmi_mem_free(info); } else { info->handler = handler; info->event_data = event_data; ipmi_lock(domain->domain_lock); info->next = domain->event_cruft; info->prev = NULL; domain->event_cruft = info; ipmi_unlock(domain->domain_lock); if (id) *id = info; } return rv; } int ipmi_deregister_for_events(ipmi_domain_t *domain, struct ipmi_event_handler_id_s *id) { int rv; rv = ipmi_domain_remove_event_handler(domain, id->handler, id->event_data); ipmi_lock(domain->domain_lock); if (id->next) id->next->prev = id->prev; if (id->prev) id->prev->next = id->next; else domain->event_cruft = id->next; ipmi_unlock(domain->domain_lock); ipmi_mem_free(id); return rv; } struct ipmi_domain_con_change_s { ipmi_domain_con_cb handler; void *cb_data; struct ipmi_domain_con_change_s *next, *prev; }; static int ipmi_domain_add_con_change_handler_nd(ipmi_domain_t *domain, ipmi_domain_con_cb handler, void *cb_data, struct ipmi_domain_con_change_s **id) { struct ipmi_domain_con_change_s *info; int rv; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; rv = ipmi_domain_add_connect_change_handler(domain, handler, cb_data); if (rv) { ipmi_mem_free(info); } else { info->handler = handler; info->cb_data = cb_data; ipmi_lock(domain->domain_lock); info->next = domain->con_change_cruft; info->prev = NULL; domain->con_change_cruft = info; ipmi_unlock(domain->domain_lock); if (id) *id = info; } return rv; } int ipmi_domain_add_con_change_handler(ipmi_domain_t *domain, ipmi_domain_con_cb handler, void *cb_data, struct ipmi_domain_con_change_s **id) { return ipmi_domain_add_con_change_handler_nd(domain, handler, cb_data, id); } void ipmi_domain_remove_con_change_handler(ipmi_domain_t *domain, struct ipmi_domain_con_change_s *id) { ipmi_domain_remove_connect_change_handler(domain, id->handler, id->cb_data); ipmi_lock(domain->domain_lock); if (id->next) id->next->prev = id->prev; if (id->prev) id->prev->next = id->next; else domain->con_change_cruft = id->next; ipmi_unlock(domain->domain_lock); ipmi_mem_free(id); } int ipmi_init_domain(ipmi_con_t *con[], unsigned int num_con, ipmi_domain_con_cb con_change_handler, void *con_change_cb_data, struct ipmi_domain_con_change_s **con_change_id, ipmi_domain_id_t *new_domain) { int rv; ipmi_domain_t *domain; unsigned int i; if ((num_con < 1) || (num_con > MAX_CONS)) return EINVAL; rv = setup_domain("", con, num_con, NULL, 0, &domain); if (rv) return rv; domain->in_startup = 1; for (i=0; iadd_con_change_handler(con[i], ll_con_changed, domain); if (rv) return rv; rv = con[i]->add_ipmb_addr_handler(con[i], ll_addr_changed, domain); if (rv) return rv; } add_known_domain(domain); if (con_change_handler) { rv = ipmi_domain_add_con_change_handler_nd(domain, con_change_handler, con_change_cb_data, con_change_id); if (rv) goto out_err; } for (i=0; istart_con(con[i]); if (rv) goto out_err; if (new_domain) *new_domain = ipmi_domain_convert_to_id(domain); if (! locked_list_add(domains_list, domain, NULL)) { ipmi_log(IPMI_LOG_SEVERE, "%sdomain.c(sdr_handler): " "Out of memory, could not add domain to the domains list", DOMAIN_NAME(domain)); } out: i_ipmi_domain_put(domain); return rv; out_err: for (i=0; iremove_con_change_handler(con[i], ll_con_changed, domain); con[i]->remove_ipmb_addr_handler(con[i], ll_addr_changed, domain); if (con[i]->register_stat_handler) con[i]->unregister_stat_handler(con[i], domain->con_stat_info); } remove_known_domain(domain); cleanup_domain(domain); goto out; } int ipmi_domain_set_entity_update_handler(ipmi_domain_t *domain, ipmi_domain_entity_cb handler, void *cb_data) { int rv = 0; CHECK_DOMAIN_LOCK(domain); ipmi_lock(domain->domain_lock); if (domain->cruft_entity_update_handler) ipmi_entity_info_remove_update_handler (domain->entities, domain->cruft_entity_update_handler, domain->cruft_entity_update_cb_data); domain->cruft_entity_update_handler = handler; domain->cruft_entity_update_cb_data = cb_data; if (handler) rv = ipmi_entity_info_add_update_handler(domain->entities, handler, cb_data); ipmi_unlock(domain->domain_lock); return rv; } int ipmi_close_connection(ipmi_domain_t *domain, ipmi_domain_close_done_cb close_done, void *cb_data) { return ipmi_domain_close(domain, close_done, cb_data); } static void free_domain_cruft(ipmi_domain_t *domain) { while (domain->mc_upd_cruft) { struct ipmi_domain_mc_upd_s *to_free; to_free = domain->mc_upd_cruft; domain->mc_upd_cruft = to_free->next; ipmi_mem_free(to_free); } while (domain->event_cruft) { struct ipmi_event_handler_id_s *to_free; to_free = domain->event_cruft; domain->event_cruft = to_free->next; ipmi_mem_free(to_free); } while (domain->con_change_cruft) { struct ipmi_domain_con_change_s *to_free; to_free = domain->con_change_cruft; domain->con_change_cruft = to_free->next; ipmi_mem_free(to_free); } }