qemu-ppc
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-ppc] [PATCH 1/1] target-ppc: Support rtas_get_sysparm(PROCESSO


From: Alexey Kardashevskiy
Subject: Re: [Qemu-ppc] [PATCH 1/1] target-ppc: Support rtas_get_sysparm(PROCESSOR_MODULE_INFO)
Date: Fri, 30 Oct 2015 21:42:22 +1100
User-agent: Mozilla/5.0 (X11; Linux i686 on x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0

On 10/25/2015 07:29 AM, Sukadev Bhattiprolu wrote:
Implement the RTAS_SYSPARM_PROCESSOR_MODULE_INFO parameter to
rtas_get_sysparm() call in qemu. This call returns the processor
core, chip, socket and module information as specified in
section 7.3.16.18 of PAPR v2.7.

We walk the /proc/device-tree to determine the number of chips,
cores and modules in the _host_ system and return this info to
the guest application that makes the rtas_get_sysparm() call.

We currently hard code the number of module_types to 1, but we
should determine that dynamically somehow later.

Thanks to input from Nishanth Aravamudan.

Signed-off-by: Sukadev Bhattiprolu <address@hidden>


Please, also cc: address@hidden

And when you post it for pkvm, please make sure this applies on top of the current powerkvm's tree.

---
  hw/ppc/Makefile.objs        |   1 +
  hw/ppc/spapr_rtas.c         |  39 +++++++
  hw/ppc/spapr_rtas_modinfo.c | 253 ++++++++++++++++++++++++++++++++++++++++++++
  hw/ppc/spapr_rtas_modinfo.h |  16 +++
  include/hw/ppc/spapr.h      |   1 +
  5 files changed, 310 insertions(+)
  create mode 100644 hw/ppc/spapr_rtas_modinfo.c
  create mode 100644 hw/ppc/spapr_rtas_modinfo.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index c1ffc77..57c6b02 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -4,6 +4,7 @@ obj-y += ppc.o ppc_booke.o
  obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
  obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
+obj-$(CONFIG_PSERIES) += spapr_rtas_modinfo.o
  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
  obj-y += spapr_pci_vfio.o
  endif
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 34b12a3..3356036 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -34,6 +34,8 @@
  #include "hw/ppc/spapr.h"
  #include "hw/ppc/spapr_vio.h"
  #include "qapi-event.h"
+
+#include "spapr_rtas_modinfo.h"
  #include "hw/boards.h"

  #include <libfdt.h>
@@ -228,6 +230,16 @@ static void rtas_stop_self(PowerPCCPU *cpu, 
sPAPRMachineState *spapr,
      env->msr = 0;
  }

+static void write_2_rtas_bytes(uint8_t *buf, int *next, int val)
+{
+        int i = *next;
+
+        buf[i++] = (val >> 8) & 0xFF;
+        buf[i++] = val & 0xFF;
+
+        *next = i;
+}


QEMU uses 4-spaces indents and no tabs. Please run scrips/checkpatch.pl on a patch before posting.


+
  static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
                                            sPAPRMachineState *spapr,
                                            uint32_t token, uint32_t nargs,
@@ -240,6 +252,33 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
      target_ulong ret = RTAS_OUT_SUCCESS;

      switch (parameter) {
+    case RTAS_SYSPARM_PROCESSOR_MODULE_INFO: {
+        int i;
+        int offset = 0;
+        int size;
+        uint8_t *param_val;
+        struct rtas_module_info modinfo;
+
+        if (rtas_get_module_info(&modinfo))
+           break;
+
+        size = sizeof(modinfo);
+        size += (modinfo.module_types - 1) * sizeof(struct rtas_socket_info);
+
+        param_val = g_malloc(size);
+
+        write_2_rtas_bytes(param_val, &offset, modinfo.module_types);


stw_be_phys(&address_space_memory, buffer + offset, modinfo.module_types)
etc...
and remove write_2_rtas_bytes.



+        for (i = 0; i < modinfo.module_types; i++) {
+            write_2_rtas_bytes(param_val, &offset, modinfo.si[i].sockets);
+            write_2_rtas_bytes(param_val, &offset, modinfo.si[i].chips);
+            write_2_rtas_bytes(param_val, &offset, 
modinfo.si[i].cores_per_chip);
+        }
+
+        rtas_st_buffer(buffer, length, param_val, offset);
+        g_free(param_val);
+        break;
+        }
+
      case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: {
          char *param_val = g_strdup_printf("MaxEntCap=%d,"
                                            "DesMem=%llu,"
diff --git a/hw/ppc/spapr_rtas_modinfo.c b/hw/ppc/spapr_rtas_modinfo.c
new file mode 100644
index 0000000..0babdf4
--- /dev/null
+++ b/hw/ppc/spapr_rtas_modinfo.c
@@ -0,0 +1,253 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>

stdlib is not needed.


+#include <string.h>
+#include <errno.h>
+
+/*
+ * TODO: <fts.h> doesn't like __USE_FILE_OFFSET64 but hopefully we don't


That's ugly :-/ Google is saying there is http://linux.die.net/man/3/ftw which looks newer => better.


+ *     need to deal with large file offsets in /proc/device-tree. If we
+ *     must, we should fall back to walking the tree with opendir().
+ *     Another alternative is nftw() but that requires us to use global
+ *     variables to keep the counts of chips/cores etc so we need to
+ *     serialize access to the globals.
+ */
+#undef __USE_FILE_OFFSET64
+#include <fts.h>
+
+#include <glib.h>
+#include "spapr_rtas_modinfo.h"
+
+/*
+ * Chip IDs and Module IDs can occur more than once in the devcie-tree

s/devcie-tree/device-tree/



+ * (eg: each core in a chip would have same chip id). To count number
+ * of unique chips, track and skip duplicate chip/modules ids that we
+ * may encounter.
+ */
+struct obj {
+    int idx;
+    struct obj *next;
+};


There are lovely things in glibc like g_hash_table_new_full(), etc, why not to use something like that rather than managing linked list yourself?

+
+
+/*
+ * Add the given object (chip or module) id to its list if its not
+ * not already there.
+ *
+ * Return 1 if object was added (i.e it was new). Return 0 otherwise.
+ */
+static int add_obj(struct obj **headp, int idx)
+{
+    struct obj *cur;
+
+    cur = *headp;
+    while(cur) {
+        if (cur->idx == idx)
+            return 0;
+        cur = cur->next;
+    }
+
+    cur = g_malloc(sizeof(struct obj));
+    if (!cur) {
+        fprintf(stderr, "%s: Unable to alloc struct obj\n", __func__);


New code uses error_report() instead of fprintf(stderr,...).


+        return 0;
+    }
+
+    cur->idx = idx;
+    cur->next = NULL;
+
+    if (*headp)
+        (*headp)->next = cur;
+    else
+        *headp = cur;
+
+    return 1;
+}
+
+static void free_obj_list(struct obj *head)
+{
+    struct obj *cur, *prev;
+
+    cur = head;
+    while(cur) {
+        prev = cur;
+        cur = cur->next;
+        g_free(prev);
+    }
+}
+
+static  int file_matches(FTSENT *ent, const char *file_name,
+            const char *parent_prefix, int level)
+{
+    char *parent_name;

It is used once and does not seem to improve things much.


+
+    if (!S_ISREG(ent->fts_statp->st_mode) || ent->fts_level != level)
+        return 0;
+
+    parent_name = ent->fts_parent->fts_name;
+    if (strncmp(parent_name, parent_prefix, strlen(parent_prefix)))
+        return 0;
+
+    if (strncmp(ent->fts_name, file_name, strlen(file_name)))
+        return 0;
+
+    return 1;
+}
+
+static int file_read_buf(char *file_name, char *buf, int len)
+{
+    int rc;
+    FILE *fp;
+
+    fp = fopen(file_name, "r");
+    if (!fp) {
+        fprintf(stderr, "%s: Error opening %s\n", __func__, file_name);
+        return -1;
+    }
+
+    rc = fread(buf, 1, len, fp);
+    fclose(fp);
+
+    if (rc != len)
+        return -1;
+
+    return 0;
+}
+
+/*
+ * Check if @ent corresponds a new CPU module. i.e if @ent corresponds to
+ * a file, 'ibm,hw-module-id' in a directory like (i.e fts_level == 2)
+ *
+ *     /proc/device-tree/xscom*
+ *
+ * If it is a module id file, read the module index (one word) in the file
+ * and check if it is a duplicate module index.
+ *
+ * Return 1 if it is a new module index. Return 0 in all other cases.
+ */
+static int is_new_module(struct obj **module_list, FTSENT *ent)

This looks very much the same as is_new_chip(), could use common helpers.

Also, these is_xxx() return actual booleans so make them "bool".


+{
+    int i;
+    int idx;
+    char buf[16];
+
+    if (!file_matches(ent, "ibm,hw-module-id", "xscom", 2))
+        return 0;
+
+    if (file_read_buf(ent->fts_path, buf, sizeof(int)) < 0)
+        return 0;
+
+    idx = 0;
+    for (i = 0; i < 4; i++)
+        idx |= (buf[i] << (3-i));
+


How will this work on big endian host? The file is BE, I suppose.

Use here and in similar places:

idx = ldl_be_p(buf)


+    return add_obj(module_list, idx);
+}
+
+/*
+ * Check if @ent corresponds a new CPU chip. i.e if @ent corresponds to
+ * a file, 'ibm,chip-id' in a directory like (i.e fts_level == 2)
+ *
+ *     /proc/device-tree/xscom*
+ *
+ * If it is a chip id file, read the chip index (one word) in the file
+ * and check if it is a duplicate chip index.
+ *
+ * Return 1 if it is a new chip index. Return 0 in all other cases.
+ */
+static int is_new_chip(struct obj **chip_list, FTSENT *ent)
+{
+    int i;
+    int idx;
+    char buf[16];
+
+    if (!file_matches(ent, "ibm,chip-id", "xscom", 2))
+        return 0;
+
+    if (file_read_buf(ent->fts_path, buf, sizeof(int)) < 0)
+        return 0;
+
+    idx = 0;
+    for (i = 0; i < 4; i++)
+        idx |= (buf[i] << (3-i));
+
+    return add_obj(chip_list, idx);
+}
+
+/*
+ * Check if @ent corresponds a CPU core.
+ *
+ * i.e check if @ent corresponds to 'device_type' file in a directory like
+ *
+ *     /proc/device-tree/cpus/PowerPC,POWER*
+ *
+ * (i.e fts_level == 3) and if the file contains the string "cpu".
+ *
+ * Return 1 if file corresponds to a CPU core. Return 0 in all other cases.
+ */
+static int is_cpu_core(FTSENT *ent)
+{
+    int rc;
+    char buf[16];
+
+    if (!file_matches(ent, "device_type", "PowerPC,POWER", 3))
+        return 0;
+
+    rc = file_read_buf(ent->fts_path, buf, 4);
+    if (rc < 0)
+        return 0;
+
+    if (strcmp(buf, "cpu"))
+        return 0;
+
+    return 1;
+}
+
+int rtas_get_module_info(struct rtas_module_info *modinfo)
+{
+    int num_cores;
+    int num_chips;
+    int num_modules;
+    const char *p1 = "/proc/device-tree";
+    char * const argv[] = { (char *)p1, NULL };
+    struct obj *chip_list;
+    struct obj *module_list;
+
+    FTS *handle;
+    FTSENT *ent;
+
+    handle = fts_open(argv, FTS_LOGICAL|FTS_NOCHDIR, NULL);
+    if (!handle) {
+        fprintf(stderr, "fts_open(): Error %s\n", strerror(errno));
+        return -1;
+    }
+
+    memset(modinfo, 0, sizeof(struct rtas_module_info));
+
+    num_cores = num_chips = num_modules = 0;
+    chip_list = module_list = NULL;
+
+    while (1) {
+        ent = fts_read(handle);
+        if (!ent)
+            break;
+
+        if (is_cpu_core(ent))
+            num_cores++;
+        else if (is_new_chip(&chip_list, ent))
+            num_chips++;
+        else if (is_new_module(&module_list, ent))
+            num_modules++;


These is_xxxx() look very similar, I am pretty sure they can be all incorporated into this loop and we won't loose anything.


+    }
+
+    modinfo->module_types = 1;
+    modinfo->si[0].sockets = num_modules;
+    modinfo->si[0].chips = num_chips;
+    modinfo->si[0].cores_per_chip = num_cores / num_chips;
+
+    free_obj_list(chip_list);
+    free_obj_list(module_list);
+
+    return 0;
+}
diff --git a/hw/ppc/spapr_rtas_modinfo.h b/hw/ppc/spapr_rtas_modinfo.h
new file mode 100644
index 0000000..65942f1
--- /dev/null
+++ b/hw/ppc/spapr_rtas_modinfo.h
@@ -0,0 +1,16 @@
+
+#ifndef uint16_t
+typedef unsigned short uint16_t;
+#endif

This is not binary format here, you pack this later in rtas_ibm_get_system_parameter(), so please use "unsigned" (or "unsigned short" if you really really want) here and below.


+
+struct rtas_socket_info {
+    uint16_t sockets;
+    uint16_t chips;
+    uint16_t cores_per_chip;
+};
+struct rtas_module_info {
+    uint16_t module_types;
+    struct rtas_socket_info si[1];
+};
+
+extern int rtas_get_module_info(struct rtas_module_info *topo);
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 5baa906..cfe7fa2 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -463,6 +463,7 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi);
  /* RTAS ibm,get-system-parameter token values */
  #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS      20
  #define RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE        42
+#define RTAS_SYSPARM_PROCESSOR_MODULE_INFO       43
  #define RTAS_SYSPARM_UUID                        48

  /* RTAS indicator/sensor types



--
Alexey



reply via email to

[Prev in Thread] Current Thread [Next in Thread]