diff mbox

[15/22] FSP/LEDS: Implement a set LED SPCN command queue

Message ID 20150205084103.12859.26553.stgit@localhost.localdomain
State Accepted
Headers show

Commit Message

Vasant Hegde Feb. 5, 2015, 8:41 a.m. UTC
Simultaneous FSP async commands and OPAL requests to change various
LED states will generate multiple SPCN commands back to back before
their callback get processed. With this, some of the SPCN requests
might get overridden with the newer ones and hence will be lost. This
patch implements a set LED SPCN command queue which will serialize
multiple SPCN commands generated either through FSP async or OPAL
requests. All new requests are added to the queue when any previous
SPCN command completion is still pending. These requests would be
de-queued and then processed inside the SPCN command callback function
sequentially. This ensures completion of all the generated LED state
change requests without any loss.

Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
 hw/fsp/fsp-leds.c |  141 ++++++++++++++++++++++++++++++++++++++++++-----------
 hw/fsp/fsp-leds.h |    8 +++
 2 files changed, 121 insertions(+), 28 deletions(-)
diff mbox

Patch

diff --git a/hw/fsp/fsp-leds.c b/hw/fsp/fsp-leds.c
index cf3328b..16c9b68 100644
--- a/hw/fsp/fsp-leds.c
+++ b/hw/fsp/fsp-leds.c
@@ -65,16 +65,21 @@  static void *led_buffer;
  */
 static struct list_head  cec_ledq;		/* CEC LED list */
 static struct list_head	 encl_ledq;	/* Enclosure LED list */
+static struct list_head  spcn_cmdq;	/* SPCN command queue */
 
 /* LED lock */
 static struct lock led_lock = LOCK_UNLOCKED;
+static struct lock spcn_cmd_lock = LOCK_UNLOCKED;
+
+static bool spcn_cmd_cmplt = true;	/* SPCN command complete */
 
 /* Last SPCN command */
 static u32 last_spcn_cmd;
 static int replay = 0;
 
-
+/* Forward declaration */
 static void fsp_read_leds_data_complete(struct fsp_msg *msg);
+static int process_led_state_change(void);
 
 DEFINE_LOG_ENTRY(OPAL_RC_LED_SPCN, OPAL_PLATFORM_ERR_EVT, OPAL_LED,
 		OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
@@ -282,7 +287,12 @@  static void fsp_spcn_set_led_completion(struct fsp_msg *msg)
 			prerror(PREFIX "Failed to queue FSP_RSP_SET_LED_STATE\n");
 		}
 	}
+
+	/* free msg */
 	fsp_freemsg(msg);
+
+	/* Process pending LED update request */
+	process_led_state_change();
 }
 
 /*
@@ -299,7 +309,7 @@  static void fsp_spcn_set_led_completion(struct fsp_msg *msg)
  *	char	lc_code[LOC_CODE_SIZE];
  *};
  */
-static int fsp_msg_set_led_state(char *loc_code, bool command, bool state)
+static int fsp_msg_set_led_state(struct led_set_cmd *spcn_cmd)
 {
 	struct spcn_led_data sled;
 	struct fsp_msg *msg = NULL;
@@ -309,8 +319,8 @@  static int fsp_msg_set_led_state(char *loc_code, bool command, bool state)
 	u32 cmd_hdr = 0;
 	int rc = -1;
 
-	sled.lc_len = strlen(loc_code);
-	strncpy(sled.lc_code, loc_code, sled.lc_len);
+	sled.lc_len = strlen(spcn_cmd->loc_code);
+	strncpy(sled.lc_code, spcn_cmd->loc_code, sled.lc_len);
 
 	/* Location code length + Location code + LED control */
 	data_len = LOC_CODE_LEN + sled.lc_len + LED_CONTROL_LEN;
@@ -318,7 +328,7 @@  static int fsp_msg_set_led_state(char *loc_code, bool command, bool state)
 		data_len;
 
 	/* Fetch the current state of LED */
-	led = fsp_find_cec_led(loc_code);
+	led = fsp_find_cec_led(spcn_cmd->loc_code);
 
 	/* LED not present */
 	if (led == NULL) {
@@ -350,35 +360,35 @@  static int fsp_msg_set_led_state(char *loc_code, bool command, bool state)
 	sled.state = led->status;
 
 	/* Update the exclussive LED bits  */
-	if (is_enclosure_led(loc_code)) {
-		if (command == LED_COMMAND_FAULT) {
-			if (state == LED_STATE_ON)
+	if (is_enclosure_led(spcn_cmd->loc_code)) {
+		if (spcn_cmd->command == LED_COMMAND_FAULT) {
+			if (spcn_cmd->state == LED_STATE_ON)
 				led->excl_bit |= FSP_LED_EXCL_FAULT;
-			if (state == LED_STATE_OFF)
+			if (spcn_cmd->state == LED_STATE_OFF)
 				led->excl_bit &= ~FSP_LED_EXCL_FAULT;
 		}
 
-		if (command == LED_COMMAND_IDENTIFY) {
-			if (state == LED_STATE_ON)
+		if (spcn_cmd->command == LED_COMMAND_IDENTIFY) {
+			if (spcn_cmd->state == LED_STATE_ON)
 				led->excl_bit |= FSP_LED_EXCL_IDENTIFY;
-			if (state == LED_STATE_OFF)
+			if (spcn_cmd->state == LED_STATE_OFF)
 				led->excl_bit &= ~FSP_LED_EXCL_IDENTIFY;
 		}
 	}
 
 	/* LED FAULT commad */
-	if (command == LED_COMMAND_FAULT) {
-		if (state == LED_STATE_ON)
+	if (spcn_cmd->command == LED_COMMAND_FAULT) {
+		if (spcn_cmd->state == LED_STATE_ON)
 			sled.state |= SPCN_LED_FAULT_MASK;
-		if (state == LED_STATE_OFF)
+		if (spcn_cmd->state == LED_STATE_OFF)
 			sled.state &= ~SPCN_LED_FAULT_MASK;
 	}
 
 	/* LED IDENTIFY command */
-	if (command == LED_COMMAND_IDENTIFY) {
-		if (state == LED_STATE_ON)
+	if (spcn_cmd->command == LED_COMMAND_IDENTIFY) {
+		if (spcn_cmd->state == LED_STATE_ON)
 			sled.state |= SPCN_LED_IDENTIFY_MASK;
-		if (state == LED_STATE_OFF)
+		if (spcn_cmd->state == LED_STATE_OFF)
 			sled.state &= ~SPCN_LED_IDENTIFY_MASK;
 	}
 
@@ -397,7 +407,7 @@  static int fsp_msg_set_led_state(char *loc_code, bool command, bool state)
 	 * set/reset an individual led (CEC or ENCL).
 	 */
 	lock(&led_lock);
-	update_led_list(loc_code, sled.state);
+	update_led_list(spcn_cmd->loc_code, sled.state);
 	msg->user_data = led;
 	unlock(&led_lock);
 
@@ -408,6 +418,87 @@  static int fsp_msg_set_led_state(char *loc_code, bool command, bool state)
 }
 
 /*
+ * process_led_state_change
+ *
+ * If the command queue is empty, it sets the 'spcn_cmd_cmplt' as true
+ * and just returns. Else it pops one element from the command queue
+ * and processes the command for the requested LED state change.
+ */
+static int process_led_state_change(void)
+{
+	struct led_set_cmd *spcn_cmd;
+	int rc = 0;
+
+	/*
+	 * The command queue is empty. This will only
+	 * happen during the SPCN command callback path
+	 * in which case we set 'spcn_cmd_cmplt' as true.
+	 */
+	lock(&spcn_cmd_lock);
+	if (list_empty(&spcn_cmdq)) {
+		spcn_cmd_cmplt = true;
+		unlock(&spcn_cmd_lock);
+		return rc;
+	}
+
+	spcn_cmd = list_pop(&spcn_cmdq, struct led_set_cmd, link);
+	unlock(&spcn_cmd_lock);
+
+	rc = fsp_msg_set_led_state(spcn_cmd);
+	if (rc)
+		log_simple_error(&e_info(OPAL_RC_LED_STATE),
+				 PREFIX "Set led state failed at LC=%s\n",
+				 spcn_cmd->loc_code);
+	free(spcn_cmd);
+	return rc;
+}
+
+/*
+ * queue_led_state_change
+ *
+ * FSP async command or OPAL based request for LED state change gets queued
+ * up in the command queue. If no previous SPCN command is pending, then it
+ * immediately pops up one element from the list and processes it. If previous
+ * SPCN commands are still pending then it just queues up and return. When the
+ * SPCN command callback gets to execute, it processes one element from the
+ * list and keeps the chain execution going. At last when there are no elements
+ * in the command queue it sets 'spcn_cmd_cmplt' as true again.
+ */
+static int queue_led_state_change(char *loc_code, u8 command, u8 state)
+{
+	struct led_set_cmd *cmd;
+	int rc = 0;
+
+	/* New request node */
+	cmd = zalloc(sizeof(struct led_set_cmd));
+	if (!cmd) {
+		prlog(PR_ERR, PREFIX
+		      "SPCN set command node allocation failed\n");
+		return -1;
+	}
+
+	/* Save the request */
+	strncpy(cmd->loc_code, loc_code, strlen(loc_code));
+	cmd->command = command;
+	cmd->state = state;
+
+	/* Add to the queue */
+	lock(&spcn_cmd_lock);
+	list_add_tail(&spcn_cmdq,  &cmd->link);
+
+	/* No previous SPCN command pending */
+	if (spcn_cmd_cmplt) {
+		spcn_cmd_cmplt = false;
+		unlock(&spcn_cmd_lock);
+		rc = process_led_state_change();
+		return rc;
+	}
+
+	unlock(&spcn_cmd_lock);
+	return rc;
+}
+
+/*
  * Write single location code information into the TCE outbound buffer
  *
  * Data layout
@@ -820,19 +911,12 @@  static void fsp_set_led_state(struct fsp_msg *msg)
 			if (!strcmp(led->loc_code, req.loc_code))
 				continue;
 
-			if (fsp_msg_set_led_state(led->loc_code,
-						  command, state))
-				log_simple_error(&e_info(OPAL_RC_LED_STATE),
-					PREFIX "Set led state failed at LC=%s\n",
-					led->loc_code);
+			queue_led_state_change(led->loc_code, command, state);
 		}
 		break;
 	case SET_IND_SINGLE_LOC_CODE:
 		/* Set led state for single descendent led */
-		if (fsp_msg_set_led_state(req.loc_code, command, state))
-			log_simple_error(&e_info(OPAL_RC_LED_STATE),
-				PREFIX "Set led state failed at LC=%s\n",
-				req.loc_code);
+		queue_led_state_change(req.loc_code, command, state);
 		break;
 	default:
 		resp = fsp_mkmsg(FSP_RSP_SET_LED_STATE |
@@ -1335,6 +1419,7 @@  void fsp_led_init(void)
 	/* Init the master lists */
 	list_head_init(&cec_ledq);
 	list_head_init(&encl_ledq);
+	list_head_init(&spcn_cmdq);
 
 	fsp_leds_query_spcn();
 	prlog(PR_TRACE, PREFIX "Init completed\n");
diff --git a/hw/fsp/fsp-leds.h b/hw/fsp/fsp-leds.h
index 4cf4816..a09a27e 100644
--- a/hw/fsp/fsp-leds.h
+++ b/hw/fsp/fsp-leds.h
@@ -108,6 +108,14 @@  struct fsp_set_ind_state_req {
 	char	loc_code[LOC_CODE_SIZE];
 };
 
+/* LED set SPCN command */
+struct led_set_cmd {
+	char	loc_code[LOC_CODE_SIZE];
+	u8	command;
+	u8	state;
+	struct	list_node link;
+};
+
 /* LED commands and state */
 #define LED_COMMAND_FAULT		1
 #define LED_COMMAND_IDENTIFY		0