diff mbox

[v4,08/17] spapr_events: re-use EPOW event infrastructure for hotplug events

Message ID 1419337831-16552-9-git-send-email-mdroth@linux.vnet.ibm.com
State New
Headers show

Commit Message

Michael Roth Dec. 23, 2014, 12:30 p.m. UTC
From: Nathan Fontenot <nfont@linux.vnet.ibm.com>

This extends the data structures currently used to report EPOW events to
gets via the check-exception RTAS interfaces to also include event types
for hotplug/unplug events.

This is currently undocumented and being finalized for inclusion in PAPR
specification, but we implement this here as an extension for guest
userspace tools to implement (existing guest kernels simply log these
events via a sysfs interface that's read by rtas_errd).

Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 hw/ppc/spapr.c         |   2 +-
 hw/ppc/spapr_events.c  | 211 ++++++++++++++++++++++++++++++++++++++++---------
 include/hw/ppc/spapr.h |   5 +-
 3 files changed, 177 insertions(+), 41 deletions(-)

Comments

David Gibson Jan. 19, 2015, 4:31 a.m. UTC | #1
On Tue, Dec 23, 2014 at 06:30:22AM -0600, Michael Roth wrote:
> From: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> 
> This extends the data structures currently used to report EPOW events to
> gets via the check-exception RTAS interfaces to also include event types
> for hotplug/unplug events.
> 
> This is currently undocumented and being finalized for inclusion in PAPR
> specification, but we implement this here as an extension for guest
> userspace tools to implement (existing guest kernels simply log these
> events via a sysfs interface that's read by rtas_errd).
> 
> Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> ---
>  hw/ppc/spapr.c         |   2 +-
>  hw/ppc/spapr_events.c  | 211 ++++++++++++++++++++++++++++++++++++++++---------
>  include/hw/ppc/spapr.h |   5 +-
>  3 files changed, 177 insertions(+), 41 deletions(-)
> 
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index 361b914..1bc5773 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -1601,7 +1601,7 @@ static void ppc_spapr_init(MachineState *machine)
>      spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
>                                              kernel_size, kernel_le,
>                                              boot_device, kernel_cmdline,
> -                                            spapr->epow_irq);
> +                                            spapr->check_exception_irq);
>      assert(spapr->fdt_skel != NULL);
>  }
>  
> diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
> index 1b6157d..ebbf3a4 100644
> --- a/hw/ppc/spapr_events.c
> +++ b/hw/ppc/spapr_events.c
> @@ -32,6 +32,9 @@
>  
>  #include "hw/ppc/spapr.h"
>  #include "hw/ppc/spapr_vio.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci-host/spapr.h"
> +#include "hw/ppc/spapr_drc.h"
>  
>  #include <libfdt.h>
>  
> @@ -77,6 +80,7 @@ struct rtas_error_log {
>  #define   RTAS_LOG_TYPE_ECC_UNCORR              0x00000009
>  #define   RTAS_LOG_TYPE_ECC_CORR                0x0000000a
>  #define   RTAS_LOG_TYPE_EPOW                    0x00000040
> +#define   RTAS_LOG_TYPE_HOTPLUG                 0x000000e5
>      uint32_t extended_length;
>  } QEMU_PACKED;
>  
> @@ -166,6 +170,38 @@ struct epow_log_full {
>      struct rtas_event_log_v6_epow epow;
>  } QEMU_PACKED;
>  
> +struct rtas_event_log_v6_hp {
> +#define RTAS_LOG_V6_SECTION_ID_HOTPLUG              0x4850 /* HP */
> +    struct rtas_event_log_v6_section_header hdr;
> +    uint8_t hotplug_type;
> +#define RTAS_LOG_V6_HP_TYPE_CPU                          1
> +#define RTAS_LOG_V6_HP_TYPE_MEMORY                       2
> +#define RTAS_LOG_V6_HP_TYPE_SLOT                         3
> +#define RTAS_LOG_V6_HP_TYPE_PHB                          4
> +#define RTAS_LOG_V6_HP_TYPE_PCI                          5
> +    uint8_t hotplug_action;
> +#define RTAS_LOG_V6_HP_ACTION_ADD                        1
> +#define RTAS_LOG_V6_HP_ACTION_REMOVE                     2
> +    uint8_t hotplug_identifier;
> +#define RTAS_LOG_V6_HP_ID_DRC_NAME                       1
> +#define RTAS_LOG_V6_HP_ID_DRC_INDEX                      2
> +#define RTAS_LOG_V6_HP_ID_DRC_COUNT                      3
> +    uint8_t reserved;
> +    union {
> +        uint32_t index;
> +        uint32_t count;
> +        char name[1];
> +    } drc;
> +} QEMU_PACKED;
> +
> +struct hp_log_full {
> +    struct rtas_error_log hdr;
> +    struct rtas_event_log_v6 v6hdr;
> +    struct rtas_event_log_v6_maina maina;
> +    struct rtas_event_log_v6_mainb mainb;
> +    struct rtas_event_log_v6_hp hp;
> +} QEMU_PACKED;
> +
>  #define EVENT_MASK_INTERNAL_ERRORS           0x80000000
>  #define EVENT_MASK_EPOW                      0x40000000
>  #define EVENT_MASK_HOTPLUG                   0x10000000
> @@ -181,29 +217,61 @@ struct epow_log_full {
>          }                                                          \
>      } while (0)
>  
> -void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
> +void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
>  {
> -    uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
> -    uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
> +    uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
> +    uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
>  
>      _FDT((fdt_begin_node(fdt, "event-sources")));
>  
>      _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
>      _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
>      _FDT((fdt_property(fdt, "interrupt-ranges",
> -                       epow_irq_ranges, sizeof(epow_irq_ranges))));
> +                       irq_ranges, sizeof(irq_ranges))));
>  
>      _FDT((fdt_begin_node(fdt, "epow-events")));
> -    _FDT((fdt_property(fdt, "interrupts",
> -                       epow_interrupts, sizeof(epow_interrupts))));
> +    _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
>      _FDT((fdt_end_node(fdt)));
>  
>      _FDT((fdt_end_node(fdt)));
>  }
>  
>  static struct epow_log_full *pending_epow;
> +static struct hp_log_full *pending_hp;
>  static uint32_t next_plid;
>  
> +static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
> +{
> +    v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
> +        | RTAS_LOG_V6_B0_BIGENDIAN;
> +    v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
> +        | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
> +    v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
> +}
> +
> +static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
> +                             int section_count)
> +{
> +    struct tm tm;
> +    int year;
> +
> +    maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
> +    maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
> +    /* FIXME: section version, subtype and creator id? */
> +    qemu_get_timedate(&tm, spapr->rtc_offset);
> +    year = tm.tm_year + 1900;
> +    maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
> +                                       | (to_bcd(year % 100) << 16)
> +                                       | (to_bcd(tm.tm_mon + 1) << 8)
> +                                       | to_bcd(tm.tm_mday));
> +    maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
> +                                       | (to_bcd(tm.tm_min) << 16)
> +                                       | (to_bcd(tm.tm_sec) << 8));
> +    maina->creator_id = 'H'; /* Hypervisor */
> +    maina->section_count = section_count;
> +    maina->plid = next_plid++;
> +}
> +
>  static void spapr_powerdown_req(Notifier *n, void *opaque)
>  {
>      sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
> @@ -212,8 +280,6 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
>      struct rtas_event_log_v6_maina *maina;
>      struct rtas_event_log_v6_mainb *mainb;
>      struct rtas_event_log_v6_epow *epow;
> -    struct tm tm;
> -    int year;
>  
>      if (pending_epow) {
>          /* For now, we just throw away earlier events if two come
> @@ -237,27 +303,8 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
>      hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
>                                         - sizeof(pending_epow->hdr));
>  
> -    v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
> -        | RTAS_LOG_V6_B0_BIGENDIAN;
> -    v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
> -        | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
> -    v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
> -
> -    maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
> -    maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
> -    /* FIXME: section version, subtype and creator id? */
> -    qemu_get_timedate(&tm, spapr->rtc_offset);
> -    year = tm.tm_year + 1900;
> -    maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
> -                                       | (to_bcd(year % 100) << 16)
> -                                       | (to_bcd(tm.tm_mon + 1) << 8)
> -                                       | to_bcd(tm.tm_mday));
> -    maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
> -                                       | (to_bcd(tm.tm_min) << 16)
> -                                       | (to_bcd(tm.tm_sec) << 8));
> -    maina->creator_id = 'H'; /* Hypervisor */
> -    maina->section_count = 3; /* Main-A, Main-B and EPOW */
> -    maina->plid = next_plid++;
> +    spapr_init_v6hdr(v6hdr);
> +    spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */);
>  
>      mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
>      mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
> @@ -274,7 +321,82 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
>      epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
>      epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
>  
> -    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
> +    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
> +}
> +
> +static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
> +{
> +    struct hp_log_full *new_hp;
> +    struct rtas_error_log *hdr;
> +    struct rtas_event_log_v6 *v6hdr;
> +    struct rtas_event_log_v6_maina *maina;
> +    struct rtas_event_log_v6_mainb *mainb;
> +    struct rtas_event_log_v6_hp *hp;
> +    sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
> +    sPAPRDRConnectorType drc_type = drck->get_type(drc);
> +
> +    new_hp = g_malloc0(sizeof(struct hp_log_full));
> +    hdr = &new_hp->hdr;
> +    v6hdr = &new_hp->v6hdr;
> +    maina = &new_hp->maina;
> +    mainb = &new_hp->mainb;
> +    hp = &new_hp->hp;
> +
> +    hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
> +                               | RTAS_LOG_SEVERITY_EVENT
> +                               | RTAS_LOG_DISPOSITION_NOT_RECOVERED
> +                               | RTAS_LOG_OPTIONAL_PART_PRESENT
> +                               | RTAS_LOG_INITIATOR_HOTPLUG
> +                               | RTAS_LOG_TYPE_HOTPLUG);
> +    hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
> +                                       - sizeof(new_hp->hdr));
> +
> +    spapr_init_v6hdr(v6hdr);
> +    spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
> +
> +    mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
> +    mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
> +    mainb->subsystem_id = 0x80; /* External environment */
> +    mainb->event_severity = 0x00; /* Informational / non-error */
> +    mainb->event_subtype = 0x00; /* Normal shutdown */
> +
> +    hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
> +    hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
> +    hp->hdr.section_version = 1; /* includes extended modifier */
> +    hp->hotplug_action = hp_action;
> +
> +
> +    switch (drc_type) {
> +    case SPAPR_DR_CONNECTOR_TYPE_PCI:
> +        hp->drc.index = cpu_to_be32(drck->get_index(drc));
> +        hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
> +        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
> +        break;
> +    default:
> +        /* skip notification for unknown connector types */
> +        g_free(new_hp);
> +        return;
> +    }
> +
> +    if (pending_hp) {
> +        /* Just toss any pending hotplug events for now, this will
> +         * need to be fixed later on.
> +         */

So, we can get away with a 1-element queue for EPOW, because they're
just triggering a shutdown - so once the first one's processed, any
others aren't going to matter.  For hotplug you really do need a
proper queue.

> +        g_free(pending_hp);
> +    }
> +    pending_hp = new_hp;
> +
> +    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
> +}
> +
> +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
> +{
> +    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
> +}
> +
> +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
> +{
> +    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
>  }
>  
>  static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
> @@ -298,15 +420,26 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
>          xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
>      }
>  
> -    if ((mask & EVENT_MASK_EPOW) && pending_epow) {
> -        if (sizeof(*pending_epow) < len) {
> -            len = sizeof(*pending_epow);
> -        }
> +    if (mask & EVENT_MASK_EPOW) {
> +        if (pending_epow) {
> +            if (sizeof(*pending_epow) < len) {
> +                len = sizeof(*pending_epow);
> +            }
>  
> -        cpu_physical_memory_write(buf, pending_epow, len);
> -        g_free(pending_epow);
> -        pending_epow = NULL;
> -        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> +            cpu_physical_memory_write(buf, pending_epow, len);
> +            g_free(pending_epow);
> +            pending_epow = NULL;
> +            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> +        } else if (pending_hp) {

So.. the hotplug messages are a different type from EPOW, but are
still selected by EVENT_MASK_EPOW?  Seems a bit odd.

> +            if (sizeof(*pending_hp) < len) {
> +                len = sizeof(*pending_hp);
> +            }
> +
> +            cpu_physical_memory_write(buf, pending_hp, len);
> +            g_free(pending_hp);
> +            pending_hp = NULL;
> +            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> +        }
>      } else {
>          rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
>      }
> @@ -314,7 +447,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
>  
>  void spapr_events_init(sPAPREnvironment *spapr)
>  {
> -    spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false);
> +    spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
>      spapr->epow_notifier.notify = spapr_powerdown_req;
>      qemu_register_powerdown_notifier(&spapr->epow_notifier);
>      spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
> diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
> index b4daa42..4d50e74 100644
> --- a/include/hw/ppc/spapr.h
> +++ b/include/hw/ppc/spapr.h
> @@ -3,6 +3,7 @@
>  
>  #include "sysemu/dma.h"
>  #include "hw/ppc/xics.h"
> +#include "hw/ppc/spapr_drc.h"
>  
>  struct VIOsPAPRBus;
>  struct sPAPRPHBState;
> @@ -30,7 +31,7 @@ typedef struct sPAPREnvironment {
>      struct PPCTimebase tb;
>      bool has_graphics;
>  
> -    uint32_t epow_irq;
> +    uint32_t check_exception_irq;
>      Notifier epow_notifier;
>  
>      /* Migration state */
> @@ -486,5 +487,7 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname,
>                   uint32_t liobn, uint64_t window, uint32_t size);
>  int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
>                        sPAPRTCETable *tcet);
> +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
> +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
>  
>  #endif /* !defined (__HW_SPAPR_H__) */
Michael Roth Jan. 26, 2015, 4:56 p.m. UTC | #2
Quoting David Gibson (2015-01-18 22:31:23)
> On Tue, Dec 23, 2014 at 06:30:22AM -0600, Michael Roth wrote:
> > From: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> > 
> > This extends the data structures currently used to report EPOW events to
> > gets via the check-exception RTAS interfaces to also include event types
> > for hotplug/unplug events.
> > 
> > This is currently undocumented and being finalized for inclusion in PAPR
> > specification, but we implement this here as an extension for guest
> > userspace tools to implement (existing guest kernels simply log these
> > events via a sysfs interface that's read by rtas_errd).
> > 
> > Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> > Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> > ---
> >  hw/ppc/spapr.c         |   2 +-
> >  hw/ppc/spapr_events.c  | 211 ++++++++++++++++++++++++++++++++++++++++---------
> >  include/hw/ppc/spapr.h |   5 +-
> >  3 files changed, 177 insertions(+), 41 deletions(-)
> > 
> > diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> > index 361b914..1bc5773 100644
> > --- a/hw/ppc/spapr.c
> > +++ b/hw/ppc/spapr.c
> > @@ -1601,7 +1601,7 @@ static void ppc_spapr_init(MachineState *machine)
> >      spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
> >                                              kernel_size, kernel_le,
> >                                              boot_device, kernel_cmdline,
> > -                                            spapr->epow_irq);
> > +                                            spapr->check_exception_irq);
> >      assert(spapr->fdt_skel != NULL);
> >  }
> >  
> > diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
> > index 1b6157d..ebbf3a4 100644
> > --- a/hw/ppc/spapr_events.c
> > +++ b/hw/ppc/spapr_events.c
> > @@ -32,6 +32,9 @@
> >  
> >  #include "hw/ppc/spapr.h"
> >  #include "hw/ppc/spapr_vio.h"
> > +#include "hw/pci/pci.h"
> > +#include "hw/pci-host/spapr.h"
> > +#include "hw/ppc/spapr_drc.h"
> >  
> >  #include <libfdt.h>
> >  
> > @@ -77,6 +80,7 @@ struct rtas_error_log {
> >  #define   RTAS_LOG_TYPE_ECC_UNCORR              0x00000009
> >  #define   RTAS_LOG_TYPE_ECC_CORR                0x0000000a
> >  #define   RTAS_LOG_TYPE_EPOW                    0x00000040
> > +#define   RTAS_LOG_TYPE_HOTPLUG                 0x000000e5
> >      uint32_t extended_length;
> >  } QEMU_PACKED;
> >  
> > @@ -166,6 +170,38 @@ struct epow_log_full {
> >      struct rtas_event_log_v6_epow epow;
> >  } QEMU_PACKED;
> >  
> > +struct rtas_event_log_v6_hp {
> > +#define RTAS_LOG_V6_SECTION_ID_HOTPLUG              0x4850 /* HP */
> > +    struct rtas_event_log_v6_section_header hdr;
> > +    uint8_t hotplug_type;
> > +#define RTAS_LOG_V6_HP_TYPE_CPU                          1
> > +#define RTAS_LOG_V6_HP_TYPE_MEMORY                       2
> > +#define RTAS_LOG_V6_HP_TYPE_SLOT                         3
> > +#define RTAS_LOG_V6_HP_TYPE_PHB                          4
> > +#define RTAS_LOG_V6_HP_TYPE_PCI                          5
> > +    uint8_t hotplug_action;
> > +#define RTAS_LOG_V6_HP_ACTION_ADD                        1
> > +#define RTAS_LOG_V6_HP_ACTION_REMOVE                     2
> > +    uint8_t hotplug_identifier;
> > +#define RTAS_LOG_V6_HP_ID_DRC_NAME                       1
> > +#define RTAS_LOG_V6_HP_ID_DRC_INDEX                      2
> > +#define RTAS_LOG_V6_HP_ID_DRC_COUNT                      3
> > +    uint8_t reserved;
> > +    union {
> > +        uint32_t index;
> > +        uint32_t count;
> > +        char name[1];
> > +    } drc;
> > +} QEMU_PACKED;
> > +
> > +struct hp_log_full {
> > +    struct rtas_error_log hdr;
> > +    struct rtas_event_log_v6 v6hdr;
> > +    struct rtas_event_log_v6_maina maina;
> > +    struct rtas_event_log_v6_mainb mainb;
> > +    struct rtas_event_log_v6_hp hp;
> > +} QEMU_PACKED;
> > +
> >  #define EVENT_MASK_INTERNAL_ERRORS           0x80000000
> >  #define EVENT_MASK_EPOW                      0x40000000
> >  #define EVENT_MASK_HOTPLUG                   0x10000000
> > @@ -181,29 +217,61 @@ struct epow_log_full {
> >          }                                                          \
> >      } while (0)
> >  
> > -void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
> > +void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
> >  {
> > -    uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
> > -    uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
> > +    uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
> > +    uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
> >  
> >      _FDT((fdt_begin_node(fdt, "event-sources")));
> >  
> >      _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
> >      _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
> >      _FDT((fdt_property(fdt, "interrupt-ranges",
> > -                       epow_irq_ranges, sizeof(epow_irq_ranges))));
> > +                       irq_ranges, sizeof(irq_ranges))));
> >  
> >      _FDT((fdt_begin_node(fdt, "epow-events")));
> > -    _FDT((fdt_property(fdt, "interrupts",
> > -                       epow_interrupts, sizeof(epow_interrupts))));
> > +    _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
> >      _FDT((fdt_end_node(fdt)));
> >  
> >      _FDT((fdt_end_node(fdt)));
> >  }
> >  
> >  static struct epow_log_full *pending_epow;
> > +static struct hp_log_full *pending_hp;
> >  static uint32_t next_plid;
> >  
> > +static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
> > +{
> > +    v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
> > +        | RTAS_LOG_V6_B0_BIGENDIAN;
> > +    v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
> > +        | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
> > +    v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
> > +}
> > +
> > +static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
> > +                             int section_count)
> > +{
> > +    struct tm tm;
> > +    int year;
> > +
> > +    maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
> > +    maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
> > +    /* FIXME: section version, subtype and creator id? */
> > +    qemu_get_timedate(&tm, spapr->rtc_offset);
> > +    year = tm.tm_year + 1900;
> > +    maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
> > +                                       | (to_bcd(year % 100) << 16)
> > +                                       | (to_bcd(tm.tm_mon + 1) << 8)
> > +                                       | to_bcd(tm.tm_mday));
> > +    maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
> > +                                       | (to_bcd(tm.tm_min) << 16)
> > +                                       | (to_bcd(tm.tm_sec) << 8));
> > +    maina->creator_id = 'H'; /* Hypervisor */
> > +    maina->section_count = section_count;
> > +    maina->plid = next_plid++;
> > +}
> > +
> >  static void spapr_powerdown_req(Notifier *n, void *opaque)
> >  {
> >      sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
> > @@ -212,8 +280,6 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
> >      struct rtas_event_log_v6_maina *maina;
> >      struct rtas_event_log_v6_mainb *mainb;
> >      struct rtas_event_log_v6_epow *epow;
> > -    struct tm tm;
> > -    int year;
> >  
> >      if (pending_epow) {
> >          /* For now, we just throw away earlier events if two come
> > @@ -237,27 +303,8 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
> >      hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
> >                                         - sizeof(pending_epow->hdr));
> >  
> > -    v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
> > -        | RTAS_LOG_V6_B0_BIGENDIAN;
> > -    v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
> > -        | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
> > -    v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
> > -
> > -    maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
> > -    maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
> > -    /* FIXME: section version, subtype and creator id? */
> > -    qemu_get_timedate(&tm, spapr->rtc_offset);
> > -    year = tm.tm_year + 1900;
> > -    maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
> > -                                       | (to_bcd(year % 100) << 16)
> > -                                       | (to_bcd(tm.tm_mon + 1) << 8)
> > -                                       | to_bcd(tm.tm_mday));
> > -    maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
> > -                                       | (to_bcd(tm.tm_min) << 16)
> > -                                       | (to_bcd(tm.tm_sec) << 8));
> > -    maina->creator_id = 'H'; /* Hypervisor */
> > -    maina->section_count = 3; /* Main-A, Main-B and EPOW */
> > -    maina->plid = next_plid++;
> > +    spapr_init_v6hdr(v6hdr);
> > +    spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */);
> >  
> >      mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
> >      mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
> > @@ -274,7 +321,82 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
> >      epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
> >      epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
> >  
> > -    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
> > +    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
> > +}
> > +
> > +static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
> > +{
> > +    struct hp_log_full *new_hp;
> > +    struct rtas_error_log *hdr;
> > +    struct rtas_event_log_v6 *v6hdr;
> > +    struct rtas_event_log_v6_maina *maina;
> > +    struct rtas_event_log_v6_mainb *mainb;
> > +    struct rtas_event_log_v6_hp *hp;
> > +    sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
> > +    sPAPRDRConnectorType drc_type = drck->get_type(drc);
> > +
> > +    new_hp = g_malloc0(sizeof(struct hp_log_full));
> > +    hdr = &new_hp->hdr;
> > +    v6hdr = &new_hp->v6hdr;
> > +    maina = &new_hp->maina;
> > +    mainb = &new_hp->mainb;
> > +    hp = &new_hp->hp;
> > +
> > +    hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
> > +                               | RTAS_LOG_SEVERITY_EVENT
> > +                               | RTAS_LOG_DISPOSITION_NOT_RECOVERED
> > +                               | RTAS_LOG_OPTIONAL_PART_PRESENT
> > +                               | RTAS_LOG_INITIATOR_HOTPLUG
> > +                               | RTAS_LOG_TYPE_HOTPLUG);
> > +    hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
> > +                                       - sizeof(new_hp->hdr));
> > +
> > +    spapr_init_v6hdr(v6hdr);
> > +    spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
> > +
> > +    mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
> > +    mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
> > +    mainb->subsystem_id = 0x80; /* External environment */
> > +    mainb->event_severity = 0x00; /* Informational / non-error */
> > +    mainb->event_subtype = 0x00; /* Normal shutdown */
> > +
> > +    hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
> > +    hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
> > +    hp->hdr.section_version = 1; /* includes extended modifier */
> > +    hp->hotplug_action = hp_action;
> > +
> > +
> > +    switch (drc_type) {
> > +    case SPAPR_DR_CONNECTOR_TYPE_PCI:
> > +        hp->drc.index = cpu_to_be32(drck->get_index(drc));
> > +        hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
> > +        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
> > +        break;
> > +    default:
> > +        /* skip notification for unknown connector types */
> > +        g_free(new_hp);
> > +        return;
> > +    }
> > +
> > +    if (pending_hp) {
> > +        /* Just toss any pending hotplug events for now, this will
> > +         * need to be fixed later on.
> > +         */
> 
> So, we can get away with a 1-element queue for EPOW, because they're
> just triggering a shutdown - so once the first one's processed, any
> others aren't going to matter.  For hotplug you really do need a
> proper queue.

Yah, this was discussed in the past, but until now I didn't notice how
easy it would be to trigger this when hotplugging multiple devices from
a script or management harness of some sort. Should be simple enough to
fix for v5 though.

> 
> > +        g_free(pending_hp);
> > +    }
> > +    pending_hp = new_hp;
> > +
> > +    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
> > +}
> > +
> > +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
> > +{
> > +    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
> > +}
> > +
> > +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
> > +{
> > +    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
> >  }
> >  
> >  static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
> > @@ -298,15 +420,26 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
> >          xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
> >      }
> >  
> > -    if ((mask & EVENT_MASK_EPOW) && pending_epow) {
> > -        if (sizeof(*pending_epow) < len) {
> > -            len = sizeof(*pending_epow);
> > -        }
> > +    if (mask & EVENT_MASK_EPOW) {
> > +        if (pending_epow) {
> > +            if (sizeof(*pending_epow) < len) {
> > +                len = sizeof(*pending_epow);
> > +            }
> >  
> > -        cpu_physical_memory_write(buf, pending_epow, len);
> > -        g_free(pending_epow);
> > -        pending_epow = NULL;
> > -        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> > +            cpu_physical_memory_write(buf, pending_epow, len);
> > +            g_free(pending_epow);
> > +            pending_epow = NULL;
> > +            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> > +        } else if (pending_hp) {
> 
> So.. the hotplug messages are a different type from EPOW, but are
> still selected by EVENT_MASK_EPOW?  Seems a bit odd.

It's a little odd, but it's mainly just due to the way we surface the
hotplug event. Rather than requiring patched guest kernels, we opted
to re-use and generalize the EPOW IRQ to also surface hotplug-related
RTAS events, which is why we still expect the EVENT_MASK_EPOW when
returning an HP event via check-exception.

EPOW events have well-defined behavior in how they're exposed to
userspace via rtas_errd, which allows us to add hotplug support for
older guests via patched userspace tools.

> 
> > +            if (sizeof(*pending_hp) < len) {
> > +                len = sizeof(*pending_hp);
> > +            }
> > +
> > +            cpu_physical_memory_write(buf, pending_hp, len);
> > +            g_free(pending_hp);
> > +            pending_hp = NULL;
> > +            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> > +        }
> >      } else {
> >          rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
> >      }
> > @@ -314,7 +447,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
> >  
> >  void spapr_events_init(sPAPREnvironment *spapr)
> >  {
> > -    spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false);
> > +    spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
> >      spapr->epow_notifier.notify = spapr_powerdown_req;
> >      qemu_register_powerdown_notifier(&spapr->epow_notifier);
> >      spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
> > diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
> > index b4daa42..4d50e74 100644
> > --- a/include/hw/ppc/spapr.h
> > +++ b/include/hw/ppc/spapr.h
> > @@ -3,6 +3,7 @@
> >  
> >  #include "sysemu/dma.h"
> >  #include "hw/ppc/xics.h"
> > +#include "hw/ppc/spapr_drc.h"
> >  
> >  struct VIOsPAPRBus;
> >  struct sPAPRPHBState;
> > @@ -30,7 +31,7 @@ typedef struct sPAPREnvironment {
> >      struct PPCTimebase tb;
> >      bool has_graphics;
> >  
> > -    uint32_t epow_irq;
> > +    uint32_t check_exception_irq;
> >      Notifier epow_notifier;
> >  
> >      /* Migration state */
> > @@ -486,5 +487,7 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname,
> >                   uint32_t liobn, uint64_t window, uint32_t size);
> >  int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
> >                        sPAPRTCETable *tcet);
> > +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
> > +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
> >  
> >  #endif /* !defined (__HW_SPAPR_H__) */
> 
> -- 
> David Gibson                    | I'll have my music baroque, and my code
> david AT gibson.dropbear.id.au  | minimalist, thank you.  NOT _the_ _other_
>                                 | _way_ _around_!
> http://www.ozlabs.org/~dgibson
David Gibson Jan. 27, 2015, 5:27 a.m. UTC | #3
On Mon, Jan 26, 2015 at 10:56:51AM -0600, Michael Roth wrote:
> Quoting David Gibson (2015-01-18 22:31:23)
> > On Tue, Dec 23, 2014 at 06:30:22AM -0600, Michael Roth wrote:
> > > From: Nathan Fontenot <nfont@linux.vnet.ibm.com>
[snip]
> > > +static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
> > > +{
> > > +    struct hp_log_full *new_hp;
> > > +    struct rtas_error_log *hdr;
> > > +    struct rtas_event_log_v6 *v6hdr;
> > > +    struct rtas_event_log_v6_maina *maina;
> > > +    struct rtas_event_log_v6_mainb *mainb;
> > > +    struct rtas_event_log_v6_hp *hp;
> > > +    sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
> > > +    sPAPRDRConnectorType drc_type = drck->get_type(drc);
> > > +
> > > +    new_hp = g_malloc0(sizeof(struct hp_log_full));
> > > +    hdr = &new_hp->hdr;
> > > +    v6hdr = &new_hp->v6hdr;
> > > +    maina = &new_hp->maina;
> > > +    mainb = &new_hp->mainb;
> > > +    hp = &new_hp->hp;
> > > +
> > > +    hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
> > > +                               | RTAS_LOG_SEVERITY_EVENT
> > > +                               | RTAS_LOG_DISPOSITION_NOT_RECOVERED
> > > +                               | RTAS_LOG_OPTIONAL_PART_PRESENT
> > > +                               | RTAS_LOG_INITIATOR_HOTPLUG
> > > +                               | RTAS_LOG_TYPE_HOTPLUG);
> > > +    hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
> > > +                                       - sizeof(new_hp->hdr));
> > > +
> > > +    spapr_init_v6hdr(v6hdr);
> > > +    spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
> > > +
> > > +    mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
> > > +    mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
> > > +    mainb->subsystem_id = 0x80; /* External environment */
> > > +    mainb->event_severity = 0x00; /* Informational / non-error */
> > > +    mainb->event_subtype = 0x00; /* Normal shutdown */
> > > +
> > > +    hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
> > > +    hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
> > > +    hp->hdr.section_version = 1; /* includes extended modifier */
> > > +    hp->hotplug_action = hp_action;
> > > +
> > > +
> > > +    switch (drc_type) {
> > > +    case SPAPR_DR_CONNECTOR_TYPE_PCI:
> > > +        hp->drc.index = cpu_to_be32(drck->get_index(drc));
> > > +        hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
> > > +        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
> > > +        break;
> > > +    default:
> > > +        /* skip notification for unknown connector types */
> > > +        g_free(new_hp);
> > > +        return;
> > > +    }
> > > +
> > > +    if (pending_hp) {
> > > +        /* Just toss any pending hotplug events for now, this will
> > > +         * need to be fixed later on.
> > > +         */
> > 
> > So, we can get away with a 1-element queue for EPOW, because they're
> > just triggering a shutdown - so once the first one's processed, any
> > others aren't going to matter.  For hotplug you really do need a
> > proper queue.
> 
> Yah, this was discussed in the past, but until now I didn't notice how
> easy it would be to trigger this when hotplugging multiple devices from
> a script or management harness of some sort. Should be simple enough to
> fix for v5 though.

Ok, good.

> > > +        g_free(pending_hp);
> > > +    }
> > > +    pending_hp = new_hp;
> > > +
> > > +    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
> > > +}
> > > +
> > > +void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
> > > +{
> > > +    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
> > > +}
> > > +
> > > +void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
> > > +{
> > > +    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
> > >  }
> > >  
> > >  static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
> > > @@ -298,15 +420,26 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
> > >          xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
> > >      }
> > >  
> > > -    if ((mask & EVENT_MASK_EPOW) && pending_epow) {
> > > -        if (sizeof(*pending_epow) < len) {
> > > -            len = sizeof(*pending_epow);
> > > -        }
> > > +    if (mask & EVENT_MASK_EPOW) {
> > > +        if (pending_epow) {
> > > +            if (sizeof(*pending_epow) < len) {
> > > +                len = sizeof(*pending_epow);
> > > +            }
> > >  
> > > -        cpu_physical_memory_write(buf, pending_epow, len);
> > > -        g_free(pending_epow);
> > > -        pending_epow = NULL;
> > > -        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> > > +            cpu_physical_memory_write(buf, pending_epow, len);
> > > +            g_free(pending_epow);
> > > +            pending_epow = NULL;
> > > +            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
> > > +        } else if (pending_hp) {
> > 
> > So.. the hotplug messages are a different type from EPOW, but are
> > still selected by EVENT_MASK_EPOW?  Seems a bit odd.
> 
> It's a little odd, but it's mainly just due to the way we surface the
> hotplug event. Rather than requiring patched guest kernels, we opted
> to re-use and generalize the EPOW IRQ to also surface hotplug-related
> RTAS events, which is why we still expect the EVENT_MASK_EPOW when
> returning an HP event via check-exception.
> 
> EPOW events have well-defined behavior in how they're exposed to
> userspace via rtas_errd, which allows us to add hotplug support for
> older guests via patched userspace tools.

Ok.  Kind of a hack, but for good reasons.  I kind of think it needs a
comment, but I'm not sure where it would belong.
Bharata B Rao Jan. 28, 2015, 3:56 a.m. UTC | #4
On Mon, Jan 26, 2015 at 10:26 PM, Michael Roth
<mdroth@linux.vnet.ibm.com> wrote:
> Quoting David Gibson (2015-01-18 22:31:23)
>> On Tue, Dec 23, 2014 at 06:30:22AM -0600, Michael Roth wrote:
>> > +
>> > +static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
>> > +{
>> > +    struct hp_log_full *new_hp;
>> > +    struct rtas_error_log *hdr;
>> > +    struct rtas_event_log_v6 *v6hdr;
>> > +    struct rtas_event_log_v6_maina *maina;
>> > +    struct rtas_event_log_v6_mainb *mainb;
>> > +    struct rtas_event_log_v6_hp *hp;
>> > +    sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
>> > +    sPAPRDRConnectorType drc_type = drck->get_type(drc);
>> > +
>> > +    new_hp = g_malloc0(sizeof(struct hp_log_full));
>> > +    hdr = &new_hp->hdr;
>> > +    v6hdr = &new_hp->v6hdr;
>> > +    maina = &new_hp->maina;
>> > +    mainb = &new_hp->mainb;
>> > +    hp = &new_hp->hp;
>> > +
>> > +    hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
>> > +                               | RTAS_LOG_SEVERITY_EVENT
>> > +                               | RTAS_LOG_DISPOSITION_NOT_RECOVERED
>> > +                               | RTAS_LOG_OPTIONAL_PART_PRESENT
>> > +                               | RTAS_LOG_INITIATOR_HOTPLUG
>> > +                               | RTAS_LOG_TYPE_HOTPLUG);
>> > +    hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
>> > +                                       - sizeof(new_hp->hdr));
>> > +
>> > +    spapr_init_v6hdr(v6hdr);
>> > +    spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
>> > +
>> > +    mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
>> > +    mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
>> > +    mainb->subsystem_id = 0x80; /* External environment */
>> > +    mainb->event_severity = 0x00; /* Informational / non-error */
>> > +    mainb->event_subtype = 0x00; /* Normal shutdown */
>> > +
>> > +    hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
>> > +    hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
>> > +    hp->hdr.section_version = 1; /* includes extended modifier */
>> > +    hp->hotplug_action = hp_action;
>> > +
>> > +
>> > +    switch (drc_type) {
>> > +    case SPAPR_DR_CONNECTOR_TYPE_PCI:
>> > +        hp->drc.index = cpu_to_be32(drck->get_index(drc));
>> > +        hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
>> > +        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
>> > +        break;
>> > +    default:
>> > +        /* skip notification for unknown connector types */
>> > +        g_free(new_hp);
>> > +        return;
>> > +    }
>> > +
>> > +    if (pending_hp) {
>> > +        /* Just toss any pending hotplug events for now, this will
>> > +         * need to be fixed later on.
>> > +         */
>>
>> So, we can get away with a 1-element queue for EPOW, because they're
>> just triggering a shutdown - so once the first one's processed, any
>> others aren't going to matter.  For hotplug you really do need a
>> proper queue.
>
> Yah, this was discussed in the past, but until now I didn't notice how
> easy it would be to trigger this when hotplugging multiple devices from
> a script or management harness of some sort. Should be simple enough to
> fix for v5 though.

In case of memory hotplug, each LMB is associated with a drc. Hence a
request to hotplug a chunk of memory will be broken down into multiple
requests (each of min LMB size). So supporting a queue will be needed
and can be tested with memory hotplug code.

Regards,
Bharata.
diff mbox

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 361b914..1bc5773 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1601,7 +1601,7 @@  static void ppc_spapr_init(MachineState *machine)
     spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
                                             kernel_size, kernel_le,
                                             boot_device, kernel_cmdline,
-                                            spapr->epow_irq);
+                                            spapr->check_exception_irq);
     assert(spapr->fdt_skel != NULL);
 }
 
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 1b6157d..ebbf3a4 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -32,6 +32,9 @@ 
 
 #include "hw/ppc/spapr.h"
 #include "hw/ppc/spapr_vio.h"
+#include "hw/pci/pci.h"
+#include "hw/pci-host/spapr.h"
+#include "hw/ppc/spapr_drc.h"
 
 #include <libfdt.h>
 
@@ -77,6 +80,7 @@  struct rtas_error_log {
 #define   RTAS_LOG_TYPE_ECC_UNCORR              0x00000009
 #define   RTAS_LOG_TYPE_ECC_CORR                0x0000000a
 #define   RTAS_LOG_TYPE_EPOW                    0x00000040
+#define   RTAS_LOG_TYPE_HOTPLUG                 0x000000e5
     uint32_t extended_length;
 } QEMU_PACKED;
 
@@ -166,6 +170,38 @@  struct epow_log_full {
     struct rtas_event_log_v6_epow epow;
 } QEMU_PACKED;
 
+struct rtas_event_log_v6_hp {
+#define RTAS_LOG_V6_SECTION_ID_HOTPLUG              0x4850 /* HP */
+    struct rtas_event_log_v6_section_header hdr;
+    uint8_t hotplug_type;
+#define RTAS_LOG_V6_HP_TYPE_CPU                          1
+#define RTAS_LOG_V6_HP_TYPE_MEMORY                       2
+#define RTAS_LOG_V6_HP_TYPE_SLOT                         3
+#define RTAS_LOG_V6_HP_TYPE_PHB                          4
+#define RTAS_LOG_V6_HP_TYPE_PCI                          5
+    uint8_t hotplug_action;
+#define RTAS_LOG_V6_HP_ACTION_ADD                        1
+#define RTAS_LOG_V6_HP_ACTION_REMOVE                     2
+    uint8_t hotplug_identifier;
+#define RTAS_LOG_V6_HP_ID_DRC_NAME                       1
+#define RTAS_LOG_V6_HP_ID_DRC_INDEX                      2
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT                      3
+    uint8_t reserved;
+    union {
+        uint32_t index;
+        uint32_t count;
+        char name[1];
+    } drc;
+} QEMU_PACKED;
+
+struct hp_log_full {
+    struct rtas_error_log hdr;
+    struct rtas_event_log_v6 v6hdr;
+    struct rtas_event_log_v6_maina maina;
+    struct rtas_event_log_v6_mainb mainb;
+    struct rtas_event_log_v6_hp hp;
+} QEMU_PACKED;
+
 #define EVENT_MASK_INTERNAL_ERRORS           0x80000000
 #define EVENT_MASK_EPOW                      0x40000000
 #define EVENT_MASK_HOTPLUG                   0x10000000
@@ -181,29 +217,61 @@  struct epow_log_full {
         }                                                          \
     } while (0)
 
-void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
+void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
 {
-    uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
-    uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
+    uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
+    uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
 
     _FDT((fdt_begin_node(fdt, "event-sources")));
 
     _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
     _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
     _FDT((fdt_property(fdt, "interrupt-ranges",
-                       epow_irq_ranges, sizeof(epow_irq_ranges))));
+                       irq_ranges, sizeof(irq_ranges))));
 
     _FDT((fdt_begin_node(fdt, "epow-events")));
-    _FDT((fdt_property(fdt, "interrupts",
-                       epow_interrupts, sizeof(epow_interrupts))));
+    _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
     _FDT((fdt_end_node(fdt)));
 
     _FDT((fdt_end_node(fdt)));
 }
 
 static struct epow_log_full *pending_epow;
+static struct hp_log_full *pending_hp;
 static uint32_t next_plid;
 
+static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
+{
+    v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
+        | RTAS_LOG_V6_B0_BIGENDIAN;
+    v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
+        | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
+    v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
+}
+
+static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
+                             int section_count)
+{
+    struct tm tm;
+    int year;
+
+    maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
+    maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
+    /* FIXME: section version, subtype and creator id? */
+    qemu_get_timedate(&tm, spapr->rtc_offset);
+    year = tm.tm_year + 1900;
+    maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
+                                       | (to_bcd(year % 100) << 16)
+                                       | (to_bcd(tm.tm_mon + 1) << 8)
+                                       | to_bcd(tm.tm_mday));
+    maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
+                                       | (to_bcd(tm.tm_min) << 16)
+                                       | (to_bcd(tm.tm_sec) << 8));
+    maina->creator_id = 'H'; /* Hypervisor */
+    maina->section_count = section_count;
+    maina->plid = next_plid++;
+}
+
 static void spapr_powerdown_req(Notifier *n, void *opaque)
 {
     sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
@@ -212,8 +280,6 @@  static void spapr_powerdown_req(Notifier *n, void *opaque)
     struct rtas_event_log_v6_maina *maina;
     struct rtas_event_log_v6_mainb *mainb;
     struct rtas_event_log_v6_epow *epow;
-    struct tm tm;
-    int year;
 
     if (pending_epow) {
         /* For now, we just throw away earlier events if two come
@@ -237,27 +303,8 @@  static void spapr_powerdown_req(Notifier *n, void *opaque)
     hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
                                        - sizeof(pending_epow->hdr));
 
-    v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
-        | RTAS_LOG_V6_B0_BIGENDIAN;
-    v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
-        | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
-    v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
-
-    maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
-    maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
-    /* FIXME: section version, subtype and creator id? */
-    qemu_get_timedate(&tm, spapr->rtc_offset);
-    year = tm.tm_year + 1900;
-    maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
-                                       | (to_bcd(year % 100) << 16)
-                                       | (to_bcd(tm.tm_mon + 1) << 8)
-                                       | to_bcd(tm.tm_mday));
-    maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24)
-                                       | (to_bcd(tm.tm_min) << 16)
-                                       | (to_bcd(tm.tm_sec) << 8));
-    maina->creator_id = 'H'; /* Hypervisor */
-    maina->section_count = 3; /* Main-A, Main-B and EPOW */
-    maina->plid = next_plid++;
+    spapr_init_v6hdr(v6hdr);
+    spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */);
 
     mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
     mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
@@ -274,7 +321,82 @@  static void spapr_powerdown_req(Notifier *n, void *opaque)
     epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
     epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
 
-    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
+    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+}
+
+static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
+{
+    struct hp_log_full *new_hp;
+    struct rtas_error_log *hdr;
+    struct rtas_event_log_v6 *v6hdr;
+    struct rtas_event_log_v6_maina *maina;
+    struct rtas_event_log_v6_mainb *mainb;
+    struct rtas_event_log_v6_hp *hp;
+    sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+    sPAPRDRConnectorType drc_type = drck->get_type(drc);
+
+    new_hp = g_malloc0(sizeof(struct hp_log_full));
+    hdr = &new_hp->hdr;
+    v6hdr = &new_hp->v6hdr;
+    maina = &new_hp->maina;
+    mainb = &new_hp->mainb;
+    hp = &new_hp->hp;
+
+    hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
+                               | RTAS_LOG_SEVERITY_EVENT
+                               | RTAS_LOG_DISPOSITION_NOT_RECOVERED
+                               | RTAS_LOG_OPTIONAL_PART_PRESENT
+                               | RTAS_LOG_INITIATOR_HOTPLUG
+                               | RTAS_LOG_TYPE_HOTPLUG);
+    hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
+                                       - sizeof(new_hp->hdr));
+
+    spapr_init_v6hdr(v6hdr);
+    spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
+
+    mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
+    mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
+    mainb->subsystem_id = 0x80; /* External environment */
+    mainb->event_severity = 0x00; /* Informational / non-error */
+    mainb->event_subtype = 0x00; /* Normal shutdown */
+
+    hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
+    hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
+    hp->hdr.section_version = 1; /* includes extended modifier */
+    hp->hotplug_action = hp_action;
+
+
+    switch (drc_type) {
+    case SPAPR_DR_CONNECTOR_TYPE_PCI:
+        hp->drc.index = cpu_to_be32(drck->get_index(drc));
+        hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
+        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
+        break;
+    default:
+        /* skip notification for unknown connector types */
+        g_free(new_hp);
+        return;
+    }
+
+    if (pending_hp) {
+        /* Just toss any pending hotplug events for now, this will
+         * need to be fixed later on.
+         */
+        g_free(pending_hp);
+    }
+    pending_hp = new_hp;
+
+    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+}
+
+void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
+{
+    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
+}
+
+void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
+{
+    spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
 }
 
 static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -298,15 +420,26 @@  static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
         xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
     }
 
-    if ((mask & EVENT_MASK_EPOW) && pending_epow) {
-        if (sizeof(*pending_epow) < len) {
-            len = sizeof(*pending_epow);
-        }
+    if (mask & EVENT_MASK_EPOW) {
+        if (pending_epow) {
+            if (sizeof(*pending_epow) < len) {
+                len = sizeof(*pending_epow);
+            }
 
-        cpu_physical_memory_write(buf, pending_epow, len);
-        g_free(pending_epow);
-        pending_epow = NULL;
-        rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+            cpu_physical_memory_write(buf, pending_epow, len);
+            g_free(pending_epow);
+            pending_epow = NULL;
+            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+        } else if (pending_hp) {
+            if (sizeof(*pending_hp) < len) {
+                len = sizeof(*pending_hp);
+            }
+
+            cpu_physical_memory_write(buf, pending_hp, len);
+            g_free(pending_hp);
+            pending_hp = NULL;
+            rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+        }
     } else {
         rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
     }
@@ -314,7 +447,7 @@  static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
 
 void spapr_events_init(sPAPREnvironment *spapr)
 {
-    spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false);
+    spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
     spapr->epow_notifier.notify = spapr_powerdown_req;
     qemu_register_powerdown_notifier(&spapr->epow_notifier);
     spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index b4daa42..4d50e74 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -3,6 +3,7 @@ 
 
 #include "sysemu/dma.h"
 #include "hw/ppc/xics.h"
+#include "hw/ppc/spapr_drc.h"
 
 struct VIOsPAPRBus;
 struct sPAPRPHBState;
@@ -30,7 +31,7 @@  typedef struct sPAPREnvironment {
     struct PPCTimebase tb;
     bool has_graphics;
 
-    uint32_t epow_irq;
+    uint32_t check_exception_irq;
     Notifier epow_notifier;
 
     /* Migration state */
@@ -486,5 +487,7 @@  int spapr_dma_dt(void *fdt, int node_off, const char *propname,
                  uint32_t liobn, uint64_t window, uint32_t size);
 int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
                       sPAPRTCETable *tcet);
+void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
+void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
 
 #endif /* !defined (__HW_SPAPR_H__) */