Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpuidle: pseries: Add function to parse extended CEDE records

Currently we use CEDE with latency-hint 0 as the only other idle state
on a dedicated LPAR apart from the polling "snooze" state.

The platform might support additional extended CEDE idle states, which
can be discovered through the "ibm,get-system-parameter" rtas-call
made with CEDE_LATENCY_TOKEN.

This patch adds a function to obtain information about the extended
CEDE idle states from the platform and parse the contents to populate
an array of extended CEDE states. These idle states thus discovered
will be added to the cpuidle framework in the next patch.

dmesg on a POWER8 and POWER9 LPAR, demonstrating the output of parsing
the extended CEDE latency parameters are as follows

POWER8
[ 10.093279] xcede : xcede_record_size = 10
[ 10.093285] xcede : Record 0 : hint = 1, latency = 0x3c00 tb ticks, Wake-on-irq = 1
[ 10.093291] xcede : Record 1 : hint = 2, latency = 0x4e2000 tb ticks, Wake-on-irq = 0
[ 10.093297] cpuidle : Skipping the 2 Extended CEDE idle states

POWER9
[ 5.913180] xcede : xcede_record_size = 10
[ 5.913183] xcede : Record 0 : hint = 1, latency = 0x400 tb ticks, Wake-on-irq = 1
[ 5.913188] xcede : Record 1 : hint = 2, latency = 0x3e8000 tb ticks, Wake-on-irq = 0
[ 5.913193] cpuidle : Skipping the 2 Extended CEDE idle states

Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
[mpe: Make space for 16 records, drop memset, minor cleanup & formatting]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/1596087177-30329-3-git-send-email-ego@linux.vnet.ibm.com

authored by

Gautham R. Shenoy and committed by
Michael Ellerman
054e44ba 3af0ada7

+135 -1
+135 -1
drivers/cpuidle/cpuidle-pseries.c
··· 21 21 #include <asm/runlatch.h> 22 22 #include <asm/idle.h> 23 23 #include <asm/plpar_wrappers.h> 24 + #include <asm/rtas.h> 24 25 25 26 static struct cpuidle_driver pseries_idle_driver = { 26 27 .name = "pseries_idle", ··· 87 86 } 88 87 } 89 88 90 - #define NR_DEDICATED_STATES 2 /* snooze, CEDE */ 89 + /* 90 + * XCEDE: Extended CEDE states discovered through the 91 + * "ibm,get-systems-parameter" RTAS call with the token 92 + * CEDE_LATENCY_TOKEN 93 + */ 91 94 95 + /* 96 + * Section 7.3.16 System Parameters Option of PAPR version 2.8.1 has a 97 + * table with all the parameters to ibm,get-system-parameters. 98 + * CEDE_LATENCY_TOKEN corresponds to the token value for Cede Latency 99 + * Settings Information. 100 + */ 101 + #define CEDE_LATENCY_TOKEN 45 102 + 103 + /* 104 + * If the platform supports the cede latency settings information system 105 + * parameter it must provide the following information in the NULL terminated 106 + * parameter string: 107 + * 108 + * a. The first byte is the length “N” of each cede latency setting record minus 109 + * one (zero indicates a length of 1 byte). 110 + * 111 + * b. For each supported cede latency setting a cede latency setting record 112 + * consisting of the first “N” bytes as per the following table. 113 + * 114 + * ----------------------------- 115 + * | Field | Field | 116 + * | Name | Length | 117 + * ----------------------------- 118 + * | Cede Latency | 1 Byte | 119 + * | Specifier Value | | 120 + * ----------------------------- 121 + * | Maximum wakeup | | 122 + * | latency in | 8 Bytes | 123 + * | tb-ticks | | 124 + * ----------------------------- 125 + * | Responsive to | | 126 + * | external | 1 Byte | 127 + * | interrupts | | 128 + * ----------------------------- 129 + * 130 + * This version has cede latency record size = 10. 131 + * 132 + * The structure xcede_latency_payload represents a) and b) with 133 + * xcede_latency_record representing the table in b). 134 + * 135 + * xcede_latency_parameter is what gets returned by 136 + * ibm,get-systems-parameter RTAS call when made with 137 + * CEDE_LATENCY_TOKEN. 138 + * 139 + * These structures are only used to represent the data obtained by the RTAS 140 + * call. The data is in big-endian. 141 + */ 142 + struct xcede_latency_record { 143 + u8 hint; 144 + __be64 latency_ticks; 145 + u8 wake_on_irqs; 146 + } __packed; 147 + 148 + // Make space for 16 records, which "should be enough". 149 + struct xcede_latency_payload { 150 + u8 record_size; 151 + struct xcede_latency_record records[16]; 152 + } __packed; 153 + 154 + struct xcede_latency_parameter { 155 + __be16 payload_size; 156 + struct xcede_latency_payload payload; 157 + u8 null_char; 158 + } __packed; 159 + 160 + static unsigned int nr_xcede_records; 161 + static struct xcede_latency_parameter xcede_latency_parameter __initdata; 162 + 163 + static int __init parse_cede_parameters(void) 164 + { 165 + struct xcede_latency_payload *payload; 166 + u32 total_xcede_records_size; 167 + u8 xcede_record_size; 168 + u16 payload_size; 169 + int ret, i; 170 + 171 + ret = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, 172 + NULL, CEDE_LATENCY_TOKEN, __pa(&xcede_latency_parameter), 173 + sizeof(xcede_latency_parameter)); 174 + if (ret) { 175 + pr_err("xcede: Error parsing CEDE_LATENCY_TOKEN\n"); 176 + return ret; 177 + } 178 + 179 + payload_size = be16_to_cpu(xcede_latency_parameter.payload_size); 180 + payload = &xcede_latency_parameter.payload; 181 + 182 + xcede_record_size = payload->record_size + 1; 183 + 184 + if (xcede_record_size != sizeof(struct xcede_latency_record)) { 185 + pr_err("xcede: Expected record-size %lu. Observed size %u.\n", 186 + sizeof(struct xcede_latency_record), xcede_record_size); 187 + return -EINVAL; 188 + } 189 + 190 + pr_info("xcede: xcede_record_size = %d\n", xcede_record_size); 191 + 192 + /* 193 + * Since the payload_size includes the last NULL byte and the 194 + * xcede_record_size, the remaining bytes correspond to array of all 195 + * cede_latency settings. 196 + */ 197 + total_xcede_records_size = payload_size - 2; 198 + nr_xcede_records = total_xcede_records_size / xcede_record_size; 199 + 200 + for (i = 0; i < nr_xcede_records; i++) { 201 + struct xcede_latency_record *record = &payload->records[i]; 202 + u64 latency_ticks = be64_to_cpu(record->latency_ticks); 203 + u8 wake_on_irqs = record->wake_on_irqs; 204 + u8 hint = record->hint; 205 + 206 + pr_info("xcede: Record %d : hint = %u, latency = 0x%llx tb ticks, Wake-on-irq = %u\n", 207 + i, hint, latency_ticks, wake_on_irqs); 208 + } 209 + 210 + return 0; 211 + } 212 + 213 + #define NR_DEDICATED_STATES 2 /* snooze, CEDE */ 92 214 static u8 cede_latency_hint[NR_DEDICATED_STATES]; 93 215 94 216 static int dedicated_cede_loop(struct cpuidle_device *dev, ··· 343 219 return 0; 344 220 } 345 221 222 + static void __init parse_xcede_idle_states(void) 223 + { 224 + if (parse_cede_parameters()) 225 + return; 226 + 227 + pr_info("cpuidle : Skipping the %d Extended CEDE idle states\n", 228 + nr_xcede_records); 229 + } 230 + 346 231 /* 347 232 * pseries_idle_probe() 348 233 * Choose state table for shared versus dedicated partition ··· 373 240 cpuidle_state_table = shared_states; 374 241 max_idle_state = ARRAY_SIZE(shared_states); 375 242 } else { 243 + parse_xcede_idle_states(); 376 244 cpuidle_state_table = dedicated_states; 377 245 max_idle_state = NR_DEDICATED_STATES; 378 246 }