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

[Xen-devel] [PATCH 10/13] xen/events: Refactor evtchn_to_irq array to be dynamically allocated



From: Malcolm Crossley <malcolm.crossley@xxxxxxxxxx>

Refactor static array evtchn_to_irq array to be dynamically allocated by
implementing get and set functions for accesses to the array.

Two new port ops are added: max_channels (maximum supported number of
event channels) and nr_channels (number of currently usable event
channels).  For the N-level ABI, these numbers are both the same as
the shared data structure is a fixed size. For the FIFO ABI, these
will be different as the event array is expanded dynamically.

This allows more than 65000 event channels so an unsigned short is no
longer sufficient for an event channel port number and unsigned int is
used instead.

Signed-off-by: Malcolm Crossley <malcolm.crossley@xxxxxxxxxx>
Signed-off-by: David Vrabel <david.vrabel@xxxxxxxxxx>
---
 drivers/xen/events/events.c          |  129 +++++++++++++++++++++++++---------
 drivers/xen/events/events_internal.h |   18 ++++-
 drivers/xen/events/n-level.c         |   11 +++-
 3 files changed, 121 insertions(+), 37 deletions(-)

diff --git a/drivers/xen/events/events.c b/drivers/xen/events/events.c
index cf1c7ba..8660459 100644
--- a/drivers/xen/events/events.c
+++ b/drivers/xen/events/events.c
@@ -77,12 +77,16 @@ static DEFINE_PER_CPU(int [NR_VIRQS], virq_to_irq) = {[0 
... NR_VIRQS-1] = -1};
 /* IRQ <-> IPI mapping */
 static DEFINE_PER_CPU(int [XEN_NR_IPIS], ipi_to_irq) = {[0 ... XEN_NR_IPIS-1] 
= -1};
 
-int *evtchn_to_irq;
+int **evtchn_to_irq;
 #ifdef CONFIG_X86
 static unsigned long *pirq_eoi_map;
 #endif
 static bool (*pirq_needs_eoi)(unsigned irq);
 
+#define EVTCHN_ROW(e)  (e / (PAGE_SIZE/sizeof(**evtchn_to_irq)))
+#define EVTCHN_COL(e)  (e % (PAGE_SIZE/sizeof(**evtchn_to_irq)))
+#define EVTCHN_PER_ROW (PAGE_SIZE / sizeof(**evtchn_to_irq))
+
 /* Xen will never allocate port zero for any purpose. */
 #define VALID_EVTCHN(chn)      ((chn) != 0)
 
@@ -92,6 +96,61 @@ static struct irq_chip xen_pirq_chip;
 static void enable_dynirq(struct irq_data *data);
 static void disable_dynirq(struct irq_data *data);
 
+static void clear_evtchn_to_irq_row(unsigned row)
+{
+       unsigned col;
+
+       for (col = 0; col < EVTCHN_PER_ROW; col++)
+               evtchn_to_irq[row][col] = -1;
+}
+
+static void clear_evtchn_to_irq_all(void)
+{
+       unsigned row;
+
+       for (row = 0; row < EVTCHN_ROW(xen_evtchn_max_channels()); row++) {
+               if (evtchn_to_irq[row] == NULL)
+                       continue;
+               clear_evtchn_to_irq_row(row);
+       }
+} 
+
+static int set_evtchn_to_irq(unsigned evtchn, unsigned irq)
+{
+       unsigned row;
+       unsigned col;
+
+       if (evtchn >= xen_evtchn_max_channels())
+               return -EINVAL;
+
+       row = EVTCHN_ROW(evtchn);
+       col = EVTCHN_COL(evtchn);
+
+       if (evtchn_to_irq[row] == NULL) {
+               /* Unallocated irq entries return -1 anyway */
+               if (irq == -1)
+                       return 0;
+
+               evtchn_to_irq[row] = (int *)get_zeroed_page(GFP_KERNEL);
+               if (evtchn_to_irq[row] == NULL)
+                       return -ENOMEM;
+
+               clear_evtchn_to_irq_row(row);
+       }
+
+       evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)] = irq;
+       return 0;
+}
+
+int get_evtchn_to_irq(unsigned evtchn)
+{
+       if (evtchn >= xen_evtchn_max_channels())
+               return -1;
+       if (evtchn_to_irq[EVTCHN_ROW(evtchn)] == NULL)
+               return -1;
+       return evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)];
+}
+
 /* Get info for IRQ */
 struct irq_info *info_for_irq(unsigned irq)
 {
@@ -102,7 +161,7 @@ struct irq_info *info_for_irq(unsigned irq)
 static int xen_irq_info_common_setup(struct irq_info *info,
                                     unsigned irq,
                                     enum xen_irq_type type,
-                                    unsigned short evtchn,
+                                    unsigned evtchn,
                                     unsigned short cpu)
 {
 
@@ -113,7 +172,8 @@ static int xen_irq_info_common_setup(struct irq_info *info,
        info->evtchn = evtchn;
        info->cpu = cpu;
 
-       evtchn_to_irq[evtchn] = irq;
+       if (set_evtchn_to_irq(evtchn, irq))
+               return -ENOMEM;
 
        irq_clear_status_flags(irq, IRQ_NOREQUEST|IRQ_NOAUTOEN);
 
@@ -121,7 +181,7 @@ static int xen_irq_info_common_setup(struct irq_info *info,
 }
 
 static int xen_irq_info_evtchn_setup(unsigned irq,
-                                    unsigned short evtchn)
+                                    unsigned evtchn)
 {
        struct irq_info *info = info_for_irq(irq);
 
@@ -130,7 +190,7 @@ static int xen_irq_info_evtchn_setup(unsigned irq,
 
 static int xen_irq_info_ipi_setup(unsigned cpu,
                                  unsigned irq,
-                                 unsigned short evtchn,
+                                 unsigned evtchn,
                                  enum ipi_vector ipi)
 {
        struct irq_info *info = info_for_irq(irq);
@@ -144,8 +204,8 @@ static int xen_irq_info_ipi_setup(unsigned cpu,
 
 static int xen_irq_info_virq_setup(unsigned cpu,
                                   unsigned irq,
-                                  unsigned short evtchn,
-                                  unsigned short virq)
+                                  unsigned evtchn,
+                                  unsigned virq)
 {
        struct irq_info *info = info_for_irq(irq);
 
@@ -157,9 +217,9 @@ static int xen_irq_info_virq_setup(unsigned cpu,
 }
 
 static int xen_irq_info_pirq_setup(unsigned irq,
-                                  unsigned short evtchn,
-                                  unsigned short pirq,
-                                  unsigned short gsi,
+                                  unsigned evtchn,
+                                  unsigned pirq,
+                                  unsigned gsi,
                                   uint16_t domid,
                                   unsigned char flags)
 {
@@ -186,7 +246,7 @@ static unsigned int evtchn_from_irq(unsigned irq)
 
 unsigned irq_from_evtchn(unsigned int evtchn)
 {
-       return evtchn_to_irq[evtchn];
+       return get_evtchn_to_irq(evtchn);
 }
 EXPORT_SYMBOL_GPL(irq_from_evtchn);
 
@@ -232,7 +292,7 @@ unsigned cpu_from_irq(unsigned irq)
 
 unsigned int cpu_from_evtchn(unsigned int evtchn)
 {
-       int irq = evtchn_to_irq[evtchn];
+       int irq = get_evtchn_to_irq(evtchn);
        unsigned ret = 0;
 
        if (irq != -1)
@@ -258,7 +318,7 @@ static bool pirq_needs_eoi_flag(unsigned irq)
 
 static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu)
 {
-       int irq = evtchn_to_irq[chn];
+       int irq = get_evtchn_to_irq(chn);
        struct irq_info *info = info_for_irq(irq);
 
        BUG_ON(irq == -1);
@@ -453,7 +513,9 @@ static unsigned int __startup_pirq(unsigned int irq)
 
        pirq_query_unmask(irq);
 
-       evtchn_to_irq[evtchn] = irq;
+       rc = set_evtchn_to_irq(evtchn, irq);
+       if (rc != 0)
+               return 0;
        bind_evtchn_to_cpu(evtchn, 0);
        info->evtchn = evtchn;
 
@@ -474,7 +536,7 @@ static void shutdown_pirq(struct irq_data *data)
        struct evtchn_close close;
        unsigned int irq = data->irq;
        struct irq_info *info = info_for_irq(irq);
-       int evtchn = evtchn_from_irq(irq);
+       int rc, evtchn = evtchn_from_irq(irq);
 
        BUG_ON(info->type != IRQT_PIRQ);
 
@@ -488,7 +550,7 @@ static void shutdown_pirq(struct irq_data *data)
                BUG();
 
        bind_evtchn_to_cpu(evtchn, 0);
-       evtchn_to_irq[evtchn] = -1;
+       rc = set_evtchn_to_irq(evtchn, -1);
        info->evtchn = 0;
 }
 
@@ -551,7 +613,7 @@ static void __unbind_from_irq(unsigned int irq)
                /* Closed ports are implicitly re-bound to VCPU0. */
                bind_evtchn_to_cpu(evtchn, 0);
 
-               evtchn_to_irq[evtchn] = -1;
+               set_evtchn_to_irq(evtchn, -1);
        }
 
        BUG_ON(info_for_irq(irq)->type == IRQT_UNBOUND);
@@ -755,9 +817,12 @@ int bind_evtchn_to_irq(unsigned int evtchn)
        int irq;
        int ret;
 
+       if (evtchn >= xen_evtchn_max_channels())
+               return -ENOMEM;
+
        mutex_lock(&irq_mapping_update_lock);
 
-       irq = evtchn_to_irq[evtchn];
+       irq = get_evtchn_to_irq(evtchn);
 
        if (irq == -1) {
                irq = xen_allocate_irq_dynamic();
@@ -847,7 +912,7 @@ static int find_virq(unsigned int virq, unsigned int cpu)
        int port, rc = -ENOENT;
 
        memset(&status, 0, sizeof(status));
-       for (port = 0; port <= NR_EVENT_CHANNELS; port++) {
+       for (port = 0; port < xen_evtchn_max_channels(); port++) {
                status.dom = DOMID_SELF;
                status.port = port;
                rc = HYPERVISOR_event_channel_op(EVTCHNOP_status, &status);
@@ -927,8 +992,9 @@ int bind_evtchn_to_irqhandler(unsigned int evtchn,
        int irq, retval;
 
        irq = bind_evtchn_to_irq(evtchn);
-       if (irq < 0)
+       if (irq < 0){
                return irq;
+       }
        retval = request_irq(irq, handler, irqflags, devname, dev_id);
        if (retval != 0) {
                unbind_from_irq(irq);
@@ -1017,7 +1083,7 @@ EXPORT_SYMBOL_GPL(unbind_from_irqhandler);
 
 int evtchn_make_refcounted(unsigned int evtchn)
 {
-       int irq = evtchn_to_irq[evtchn];
+       int irq = get_evtchn_to_irq(evtchn);
        struct irq_info *info;
 
        if (irq == -1)
@@ -1042,12 +1108,12 @@ int evtchn_get(unsigned int evtchn)
        struct irq_info *info;
        int err = -ENOENT;
 
-       if (evtchn >= NR_EVENT_CHANNELS)
+       if (evtchn >= xen_evtchn_max_channels())
                return -EINVAL;
 
        mutex_lock(&irq_mapping_update_lock);
 
-       irq = evtchn_to_irq[evtchn];
+       irq = get_evtchn_to_irq(evtchn);
        if (irq == -1)
                goto done;
 
@@ -1071,7 +1137,7 @@ EXPORT_SYMBOL_GPL(evtchn_get);
 
 void evtchn_put(unsigned int evtchn)
 {
-       int irq = evtchn_to_irq[evtchn];
+       int irq = get_evtchn_to_irq(evtchn);
        if (WARN_ON(irq == -1))
                return;
        unbind_from_irq(irq);
@@ -1158,7 +1224,7 @@ void rebind_evtchn_irq(int evtchn, int irq)
        mutex_lock(&irq_mapping_update_lock);
 
        /* After resume the irq<->evtchn mappings are all cleared out */
-       BUG_ON(evtchn_to_irq[evtchn] != -1);
+       BUG_ON(get_evtchn_to_irq(evtchn) != -1);
        /* Expect irq to have been bound before,
           so there should be a proper type */
        BUG_ON(info->type == IRQT_UNBOUND);
@@ -1443,15 +1509,14 @@ void xen_irq_resume(void)
        struct irq_info *info;
 
        /* New event-channel space is not 'live' yet. */
-       for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
+       for (evtchn = 0; evtchn < xen_evtchn_nr_channels(); evtchn++)
                mask_evtchn(evtchn);
 
        /* No IRQ <-> event-channel mappings. */
        list_for_each_entry(info, &xen_irq_list_head, list)
                info->evtchn = 0; /* zap event-channel binding */
 
-       for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
-               evtchn_to_irq[evtchn] = -1;
+       clear_evtchn_to_irq_all();
 
        for_each_possible_cpu(cpu) {
                restore_cpu_virqs(cpu);
@@ -1548,14 +1613,12 @@ void __init xen_init_IRQ(void)
 
        xen_evtchn_init_nlevel();
 
-       evtchn_to_irq = kcalloc(NR_EVENT_CHANNELS, sizeof(*evtchn_to_irq),
-                                   GFP_KERNEL);
+       evtchn_to_irq = kcalloc(EVTCHN_ROW(xen_evtchn_max_channels()),
+                               sizeof(*evtchn_to_irq), GFP_KERNEL);
        BUG_ON(!evtchn_to_irq);
-       for (i = 0; i < NR_EVENT_CHANNELS; i++)
-               evtchn_to_irq[i] = -1;
 
        /* No event channels are 'live' right now. */
-       for (i = 0; i < NR_EVENT_CHANNELS; i++)
+       for (i = 0; i < xen_evtchn_nr_channels(); i++)
                mask_evtchn(i);
 
        pirq_needs_eoi = pirq_needs_eoi_flag;
diff --git a/drivers/xen/events/events_internal.h 
b/drivers/xen/events/events_internal.h
index 32cb928..9d8b70c 100644
--- a/drivers/xen/events/events_internal.h
+++ b/drivers/xen/events/events_internal.h
@@ -35,7 +35,7 @@ struct irq_info {
        int refcnt;
        enum xen_irq_type type; /* type */
        unsigned irq;
-       unsigned short evtchn;  /* event channel */
+       unsigned int evtchn;    /* event channel */
        unsigned short cpu;     /* cpu bound */
 
        union {
@@ -55,6 +55,9 @@ struct irq_info {
 #define PIRQ_SHAREABLE (1 << 1)
 
 struct evtchn_ops {
+       unsigned (*max_channels)(void);
+       unsigned (*nr_channels)(void);
+
        int (*setup)(struct irq_info *info);
        void (*bind_to_cpu)(struct irq_info *info, int cpu);
 
@@ -70,12 +73,23 @@ struct evtchn_ops {
 
 extern const struct evtchn_ops *evtchn_ops;
 
-extern int *evtchn_to_irq;
+extern int **evtchn_to_irq;
+int get_evtchn_to_irq(unsigned int evtchn);
 
 struct irq_info *info_for_irq(unsigned irq);
 unsigned cpu_from_irq(unsigned irq);
 unsigned cpu_from_evtchn(unsigned int evtchn);
 
+static inline unsigned xen_evtchn_max_channels(void)
+{
+       return evtchn_ops->max_channels();
+}
+
+static inline unsigned xen_evtchn_nr_channels(void)
+{
+       return evtchn_ops->nr_channels();
+}
+
 static inline int xen_evtchn_port_setup(struct irq_info *info)
 {
        if (evtchn_ops->setup)
diff --git a/drivers/xen/events/n-level.c b/drivers/xen/events/n-level.c
index 6c0662e..da8d0aa 100644
--- a/drivers/xen/events/n-level.c
+++ b/drivers/xen/events/n-level.c
@@ -39,6 +39,11 @@
 static DEFINE_PER_CPU(xen_ulong_t [NR_EVENT_CHANNELS/BITS_PER_EVTCHN_WORD],
                      cpu_evtchn_mask);
 
+static unsigned nlevel_max_channels(void)
+{
+       return NR_EVENT_CHANNELS;
+}
+
 static void nlevel_bind_to_cpu(struct irq_info *info, int cpu)
 {
        clear_bit(info->evtchn, BM(per_cpu(cpu_evtchn_mask, info->cpu)));
@@ -212,7 +217,7 @@ static void nlevel_handle_events(int cpu)
 
                        /* Process port. */
                        port = (word_idx * BITS_PER_EVTCHN_WORD) + bit_idx;
-                       irq = evtchn_to_irq[port];
+                       irq = get_evtchn_to_irq(port);
 
                        if (irq != -1) {
                                desc = irq_to_desc(irq);
@@ -306,7 +311,7 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id)
                        int word_idx = i / BITS_PER_EVTCHN_WORD;
                        printk("  %d: event %d -> irq %d%s%s%s\n",
                               cpu_from_evtchn(i), i,
-                              evtchn_to_irq[i],
+                              get_evtchn_to_irq(i),
                               sync_test_bit(word_idx, 
BM(&v->evtchn_pending_sel))
                                             ? "" : " l2-clear",
                               !sync_test_bit(i, BM(sh->evtchn_mask))
@@ -322,6 +327,8 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id)
 }
 
 static const struct evtchn_ops evtchn_ops_nlevel = {
+       .max_channels      = nlevel_max_channels,
+       .nr_channels       = nlevel_max_channels,
        .bind_to_cpu       = nlevel_bind_to_cpu,
        .clear_pending     = nlevel_clear_pending,
        .set_pending       = nlevel_set_pending,
-- 
1.7.2.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.