/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include static bool fsp_surv_state = false; static bool fsp_surv_ack_pending = false; static u64 surv_timer; static u64 surv_ack_timer; static u32 surv_state_param; static struct lock surv_lock = LOCK_UNLOCKED; #define FSP_SURV_ACK_TIMEOUT 120 /* surv ack timeout in seconds */ DEFINE_LOG_ENTRY(OPAL_RC_SURVE_INIT, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_MISCELLANEOUS_INFO_ONLY, NULL); DEFINE_LOG_ENTRY(OPAL_RC_SURVE_STATUS, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_MISCELLANEOUS_INFO_ONLY, NULL); DEFINE_LOG_ENTRY(OPAL_RC_SURVE_ACK, OPAL_MISC_ERR_EVT, OPAL_SURVEILLANCE, OPAL_SURVEILLANCE_ERR, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_MISCELLANEOUS_INFO_ONLY, NULL); static void fsp_surv_ack(struct fsp_msg *msg) { uint8_t val; if (!msg->resp) return; val = (msg->resp->word1 >> 8) & 0xff; if (val == 0) { /* reset the pending flag */ prlog(PR_DEBUG, "SURV: Received heartbeat acknowledge from FSP\n"); lock(&surv_lock); fsp_surv_ack_pending = false; unlock(&surv_lock); } else prlog(PR_ERR, "SURV: Heartbeat Acknowledgment error from FSP\n"); fsp_freemsg(msg); } static void fsp_surv_check_timeout(void) { u64 now = mftb(); /* * We just checked fsp_surv_ack_pending to be true in fsp_surv_hbeat * and we haven't dropped the surv_lock between then and now. So, we * just go ahead and check timeouts. */ if (tb_compare(now, surv_ack_timer) == TB_AAFTERB) { /* XXX: We should be logging a PEL to the host, assuming * the FSP is dead, pending a R/R. */ log_simple_error(&e_info(OPAL_RC_SURVE_ACK), "SURV: Surv ACK timed out; initiating R/R\n"); /* Reset the pending trigger too */ fsp_surv_ack_pending = false; fsp_trigger_reset(); } return; } /* Send surveillance heartbeat based on a timebase trigger */ static void fsp_surv_hbeat(void) { u64 now = mftb(); /* Check if an ack is pending... if so, don't send the ping just yet */ if (fsp_surv_ack_pending) { fsp_surv_check_timeout(); return; } /* add timebase callbacks */ /* * XXX This packet needs to be pushed to FSP in an interval * less than 120s that's advertised to FSP. * * Verify if the command building format and call is fine. */ if (surv_timer == 0 || (tb_compare(now, surv_timer) == TB_AAFTERB) || (tb_compare(now, surv_timer) == TB_AEQUALB)) { prlog(PR_DEBUG, "SURV: Sending the hearbeat command to FSP\n"); fsp_queue_msg(fsp_mkmsg(FSP_CMD_SURV_HBEAT, 1, 120), fsp_surv_ack); fsp_surv_ack_pending = true; surv_timer = now + secs_to_tb(60); surv_ack_timer = now + secs_to_tb(FSP_SURV_ACK_TIMEOUT); } } static void fsp_surv_poll(void *data __unused) { if (!fsp_surv_state) return; lock(&surv_lock); fsp_surv_hbeat(); unlock(&surv_lock); } static void fsp_surv_got_param(uint32_t param_id __unused, int err_len, void *data __unused) { if (err_len != 4) { log_simple_error(&e_info(OPAL_RC_SURVE_STATUS), "SURV: Error (%d) retrieving surv status; initiating R/R\n", err_len); fsp_trigger_reset(); return; } printf("SURV: Status from FSP: %d\n", surv_state_param); if (!(surv_state_param & 0x01)) return; lock(&surv_lock); fsp_surv_state = true; /* Also send one heartbeat now. The next one will not happen * until we hit the OS. */ fsp_surv_hbeat(); unlock(&surv_lock); } void fsp_surv_query(void) { int rc; printf("SURV: Querying FSP's surveillance status\n"); /* Reset surveillance settings */ lock(&surv_lock); fsp_surv_state = false; surv_timer = 0; surv_ack_timer = 0; unlock(&surv_lock); /* Query FPS for surveillance state */ rc = fsp_get_sys_param(SYS_PARAM_SURV, &surv_state_param, 4, fsp_surv_got_param, NULL); if (rc) { log_simple_error(&e_info(OPAL_RC_SURVE_INIT), "SURV: Error %d queueing param request\n", rc); } } static bool fsp_surv_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg) { assert(msg == NULL); switch (cmd_sub_mod) { case FSP_RESET_START: printf("SURV: Disabling surveillance\n"); fsp_surv_state = false; fsp_surv_ack_pending = false; return true; case FSP_RELOAD_COMPLETE: fsp_surv_query(); return true; } return false; } static struct fsp_client fsp_surv_client_rr = { .message = fsp_surv_msg_rr, }; /* This is called at boot time */ void fsp_init_surveillance(void) { /* Always register the poller, so we don't have to add/remove * it on reset-reload or change of surveillance state. Also the * poller list has no locking so we don't want to play with it * at runtime. */ opal_add_poller(fsp_surv_poll, NULL); /* Register for the reset/reload event */ fsp_register_client(&fsp_surv_client_rr, FSP_MCLASS_RR_EVENT); /* Send query to FSP */ fsp_surv_query(); }