[Skiboot] [PATCH v5] occ: Poll OCC throttle status and queue OCC events to host

Shilpasri G Bhat shilpa.bhat at linux.vnet.ibm.com
Thu Jun 18 21:37:54 AEST 2015


Add a new class of message definition OPAL_MSG_OCC to
opal_message_type to notify the following OCC events to host:
1) OCC Reset
2) OCC Load
3) OCC Throttle Status Change

Add an opal poller to periodically read throttle status updated by OCC
for each chip and notify any change in throttle status to host. The
throttle status indicates the reason why OCC may have limited the max
Pstate of the chip.

Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
---
Changes from V4:
- Add 'occ_lock' to protect writes to 'occ_reset' and serialize queuing
  of OCC_RESET and OCC_THROTTLE to host
- Rename chip->prev_throttle to chip->throttle
- Remove the code to queue OCC_RESET in throttle_poll if
  occ_data->valid=0 and occ_reset=false. This was added to mitigate
  the effects of receiving simultaneous FSP_OCC_RESET messages. This
  race will be solved by the 'occ_lock'.

Changes from V3:
- Define OCC_MAX_THROTTLE_STATUS to hold the max throttle_status
  value.
- Replace global variable occ_msg with local variables
- Pass all the members of structure opal_occ_msg while queueing a
  message to host
- Queue an OCC_RESET message to host in throttle_poll if
  occ_data->valid=0 and occ_reset=false

Changes from V2:
- Added documentation of OPAL_MSG_OCC to doc/opal-api/opal-messages.txt
- Moved macros OCC_RESET, OCC_LOAD, OCC_THROTTLE to opal-api.h
- Added a structure opal_occ_msg.
- Replace opal_queue_msg() with _opal_queue_msg()

Changes from V1:
- Initialize prev_throttle to 0 instead of 0xF
- Add pr_log when opal_queue_msg() fails on OCC reset and load.
- Do not update occ_reset and prev_throttle if opal_queue_msg fails in
   occ_throttle_poll()
- Do not queue if throttle reason is greater than 5.

 doc/opal-api/opal-messages.txt | 43 +++++++++++++++++++
 hw/occ.c                       | 96 ++++++++++++++++++++++++++++++++++++++++++
 include/chip.h                 |  1 +
 include/opal-api.h             | 25 +++++++++++
 4 files changed, 165 insertions(+)

diff --git a/doc/opal-api/opal-messages.txt b/doc/opal-api/opal-messages.txt
index fdde247..3b1bb18 100644
--- a/doc/opal-api/opal-messages.txt
+++ b/doc/opal-api/opal-messages.txt
@@ -158,3 +158,46 @@ struct opal_prd_msg:
 
 Responses from the kernel use the same message format, but are passed
 through the opal_prd_msg call.
+
+OPAL_MSG_OCC
+------------
+
+This is used by OPAL to inform host about OCC events like OCC reset,
+OCC load and throttle status change by OCC which can indicate the
+host the reason for frequency throttling/unthrottling.
+
+#define OCC_RESET			0
+#define OCC_LOAD 			1
+#define OCC_THROTTLE 			2
+#define OCC_MAX_THROTTLE_STATUS		5
+/*
+ * struct opal_occ_msg:
+ * type: OCC_RESET, OCC_LOAD, OCC_THROTTLE
+ * chip: chip id
+ * throttle status: Indicates the reason why OCC may have limited
+ * the max Pstate of the chip.
+ * 0x00 = No throttle
+ * 0x01 = Power Cap
+ * 0x02 = Processor Over Temperature
+ * 0x03 = Power Supply Failure (currently not used)
+ * 0x04 = Over current (currently not used)
+ * 0x05 = OCC Reset (not reliable as some failures will not allow for
+ * OCC to update throttle status)
+ */
+struct opal_occ_msg {
+	__be64 type;
+	__be64 chip;
+	__be64 throttle_status;
+};
+
+Host should read opal_occ_msg.chip and opal_occ_msg.throttle_status
+only when opal_occ_msg.type = OCC_THROTTLE.
+If host receives OCC_THROTTLE after an OCC_RESET then this throttle
+message will have a special meaning which indicates that all the OCCs
+have become active after a reset. In such cases opal_occ_msg.chip and
+opal_occ_msg.throttle_status will be set to 0 and host should not use
+these values.
+
+If opal_occ_msg.type > 2 then host should ignore the message for now,
+new events can be defined for opal_occ_msg.type in the future versions
+of OPAL.
diff --git a/hw/occ.c b/hw/occ.c
index fe513cb..790efa6 100644
--- a/hw/occ.c
+++ b/hw/occ.c
@@ -24,6 +24,8 @@
 #include <timebase.h>
 #include <hostservices.h>
 #include <errorlog.h>
+#include <opal-api.h>
+#include <opal-msg.h>
 
 /* OCC Communication Area for PStates */
 
@@ -31,6 +33,13 @@
 
 #define MAX_PSTATES 256
 
+#define chip_occ_data(chip) \
+		((struct occ_pstate_table *)(chip->homer_base + \
+				P8_HOMER_SAPPHIRE_DATA_OFFSET))
+
+static bool occ_reset;
+static struct lock occ_lock = LOCK_UNLOCKED;
+
 struct occ_pstate_entry {
 	s8 id;
 	u8 flags;
@@ -302,6 +311,57 @@ static bool cpu_pstates_prepare_core(struct proc_chip *chip, struct cpu_thread *
 	return true;
 }
 
+static void occ_throttle_poll(void *data __unused)
+{
+	struct proc_chip *chip;
+	struct occ_pstate_table *occ_data;
+	struct opal_occ_msg occ_msg;
+	int rc;
+
+	if (!try_lock(&occ_lock))
+		return;
+	if (occ_reset) {
+		int inactive = 0;
+
+		for_each_chip(chip) {
+			occ_data = chip_occ_data(chip);
+			if (occ_data->valid != 1) {
+				inactive = 1;
+				break;
+			}
+		}
+		if (!inactive) {
+			/*
+			 * Queue OCC_THROTTLE with throttle status as 0 to
+			 * indicate all OCCs are active after a reset.
+			 */
+			occ_msg.type = OCC_THROTTLE;
+			occ_msg.chip = 0;
+			occ_msg.throttle_status = 0;
+			rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL, 3,
+					     (uint64_t *)&occ_msg);
+			if (!rc)
+				occ_reset = false;
+		}
+	} else {
+		for_each_chip(chip) {
+			occ_data = chip_occ_data(chip);
+			if ((occ_data->valid == 1) &&
+			    (chip->throttle != occ_data->throttle) &&
+			    (occ_data->throttle <= OCC_MAX_THROTTLE_STATUS)) {
+				occ_msg.type = OCC_THROTTLE;
+				occ_msg.chip = chip->id;
+				occ_msg.throttle_status = occ_data->throttle;
+				rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL,
+						     3, (uint64_t *)&occ_msg);
+				if (!rc)
+					chip->throttle = occ_data->throttle;
+			}
+		}
+	}
+	unlock(&occ_lock);
+}
+
 /* CPU-OCC PState init */
 /* Called after OCC init on P8 */
 void occ_pstates_init(void)
@@ -345,6 +405,11 @@ void occ_pstates_init(void)
 			cpu_pstates_prepare_core(chip, c, pstate_nom);
 		}
 	}
+
+	/* Add opal_poller to poll OCC throttle status of each chip */
+	for_each_chip(chip)
+		chip->throttle = 0;
+	opal_add_poller(occ_throttle_poll, NULL);
 }
 
 struct occ_load_req {
@@ -386,6 +451,14 @@ static void __occ_do_load(u8 scope, u32 dbob_id __unused, u32 seq_id)
 		prlog(PR_INFO, "OCC: Load: Fallback to preloaded image\n");
 		rc = 0;
 	} else if (!rc) {
+		struct opal_occ_msg occ_msg = { OCC_LOAD, 0, 0 };
+
+		rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL, 3,
+				     (uint64_t *)&occ_msg);
+		if (rc)
+			prlog(PR_INFO, "OCC: Failed to queue message %d\n",
+			      OCC_LOAD);
+
 		/* Success, start OCC */
 		rc = host_services_occ_start();
 	}
@@ -509,6 +582,8 @@ static void occ_do_reset(u8 scope, u32 dbob_id, u32 seq_id)
 		rc = 0;
 	}
 	if (!rc) {
+		struct opal_occ_msg occ_msg = { OCC_RESET, 0, 0 };
+
 		/* Send a single success response for all chips */
 		stat = fsp_mkmsg(FSP_CMD_RESET_OCC_STAT, 2, 0, seq_id);
 		if (stat)
@@ -519,6 +594,27 @@ static void occ_do_reset(u8 scope, u32 dbob_id, u32 seq_id)
 				"OCC: Error %d queueing FSP OCC RESET"
 					" STATUS message\n", rc);
 		}
+		lock(&occ_lock);
+		rc = _opal_queue_msg(OPAL_MSG_OCC, NULL, NULL, 3,
+				     (uint64_t *)&occ_msg);
+		if (rc)
+			prlog(PR_INFO, "OCC: Failed to queue message %d\n",
+			      OCC_RESET);
+		/*
+		 * Set 'valid' byte of chip_occ_data to 0 since OCC
+		 * may not clear this byte on a reset.
+		 * OCC will set the 'valid' byte to 1 when it becomes
+		 * active again.
+		 */
+		for_each_chip(chip) {
+			struct occ_pstate_table *occ_data;
+
+			occ_data = chip_occ_data(chip);
+			occ_data->valid = 0;
+			chip->throttle = 0;
+		}
+		occ_reset = true;
+		unlock(&occ_lock);
 	} else {
 
 		/*
diff --git a/include/chip.h b/include/chip.h
index 1b4f4c4..4cee692 100644
--- a/include/chip.h
+++ b/include/chip.h
@@ -147,6 +147,7 @@ struct proc_chip {
 	uint64_t		homer_size;
 	uint64_t		occ_common_base;
 	uint64_t		occ_common_size;
+	u8			throttle;
 
 	/* Must hold capi_lock to change */
 	u8			capp_phb3_attached_mask;
diff --git a/include/opal-api.h b/include/opal-api.h
index d58c8bf..bfad589 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -418,6 +418,7 @@ enum opal_msg_type {
 	OPAL_MSG_HMI_EVT,
 	OPAL_MSG_DPO,
 	OPAL_MSG_PRD,
+	OPAL_MSG_OCC,
 	OPAL_MSG_TYPE_MAX,
 };
 
@@ -887,6 +888,30 @@ struct opal_prd_msg {
 	};
 };
 
+#define OCC_RESET			0
+#define OCC_LOAD			1
+#define OCC_THROTTLE			2
+#define OCC_MAX_THROTTLE_STATUS		5
+/*
+ * struct opal_occ_msg:
+ * type: OCC_RESET, OCC_LOAD, OCC_THROTTLE
+ * chip: chip id
+ * throttle status: indicates the reason why OCC may have limited
+ * the max Pstate of the chip.
+ * 0x00 = No throttle
+ * 0x01 = Power Cap
+ * 0x02 = Processor Over Temperature
+ * 0x03 = Power Supply Failure (currently not used)
+ * 0x04 = Over current (currently not used)
+ * 0x05 = OCC Reset (not reliable as some failures will not allow for
+ * OCC to update throttle status)
+ */
+struct opal_occ_msg {
+	__be64 type;
+	__be64 chip;
+	__be64 throttle_status;
+};
+
 /*
  * SG entries
  *
-- 
1.9.3



More information about the Skiboot mailing list