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

Re: [Xen-devel] [RFC PATCH 03/19] xen/arm: its: Port ITS driver to xen



Hello Vijay,

On 02/03/2015 12:30, vijay.kilari@xxxxxxxxx wrote:
@@ -228,10 +242,10 @@ static struct its_collection *its_build_mapd_cmd(struct 
its_cmd_block *cmd,
                                                   struct its_cmd_desc *desc)
  {
      unsigned long itt_addr;
-    u8 size = max(order_base_2(desc->its_mapd_cmd.dev->nr_ites), 1);
+    u8 size = max(fls(desc->its_mapd_cmd.dev->nr_ites) - 1, 1);

You may want to give a look to get_count_order.


-    itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
-    itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN);
+    itt_addr = __pa(desc->its_mapd_cmd.dev->itt);
+    itt_addr = ((itt_addr) + (ITS_ITT_ALIGN - 1)) & ~(ITS_ITT_ALIGN - 1);

You can use ROUNDUP.

[..]

@@ -343,17 +357,23 @@ static int its_queue_full(struct its_node *its)
  static struct its_cmd_block *its_allocate_entry(struct its_node *its)
  {
      struct its_cmd_block *cmd;
-    u32 count = 1000000;    /* 1s! */
+    bool_t timeout = 0;
+    s_time_t deadline = NOW() + MILLISECS(1000);

      while (its_queue_full(its)) {
-        count--;
-        if (!count) {
-            pr_err_ratelimited("ITS queue not draining\n");
-            return NULL;
+       if ( NOW() > deadline )
+        {
+            timeout = 1;
+            break;
          }
          cpu_relax();
          udelay(1);
      }
+    if ( timeout )
+    {
+        its_err("ITS queue not draining\n");
+        return NULL;
+    }

Why do you need to modify the loop? It looks like to me it will work Xen too.

      cmd = its->cmd_write++;

@@ -380,7 +400,7 @@ static void its_flush_cmd(struct its_node *its, struct 
its_cmd_block *cmd)
       * the ITS.
       */
      if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING)
-        __flush_dcache_area(cmd, sizeof(*cmd));
+        clean_dcache_va_range(cmd, sizeof(*cmd));

__flush_dcache_area perform a clean & invalidate while clean_dcache_va_range only clean.

You may want to use clean_and_invalidate_va_range

[..]

@@ -390,7 +410,8 @@ static void its_wait_for_range_completion(struct its_node 
*its,
                                            struct its_cmd_block *to)
  {
      u64 rd_idx, from_idx, to_idx;
-    u32 count = 1000000;    /* 1s! */
+    bool_t timeout = 0;
+    s_time_t deadline = NOW() + MILLISECS(1000);

      from_idx = its_cmd_ptr_to_offset(its, from);
      to_idx = its_cmd_ptr_to_offset(its, to);
@@ -400,14 +421,16 @@ static void its_wait_for_range_completion(struct its_node 
*its,
          if (rd_idx >= to_idx || rd_idx < from_idx)
              break;

-        count--;
-        if (!count) {
-            pr_err_ratelimited("ITS queue timeout\n");
-            return;
+        if ( NOW() > deadline )
+        {
+            timeout = 1;
+            break;
          }
          cpu_relax();
          udelay(1);
      }
+    if ( timeout )
+        printk("ITS queue timeout\n");
  }

Same question for the loop.

[..]

  /*
- * irqchip functions - assumes MSI, mostly.
- */
-
-static void lpi_set_config(struct its_device *its_dev, u32 hwirq,
-               u32 id, int enable)
-{
-    u8 *cfg = page_address(gic_rdists->prop_page) + hwirq - 8192;
-
-    if (enable)
-        *cfg |= LPI_PROP_ENABLED;
-    else
-        *cfg &= ~LPI_PROP_ENABLED;
-
-    /*
-     * Make the above write visible to the redistributors.
-     * And yes, we're flushing exactly: One. Single. Byte.
-     * Humpf...
-     */
-    if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING)
-        __flush_dcache_area(cfg, sizeof(*cfg));
-    else
-        dsb(ishst);
-    its_send_inv(its_dev, id);
-}
-
-static inline u16 its_msi_get_entry_nr(struct msi_desc *desc)
-{
-    return desc->msi_attrib.entry_nr;
-}
-
-static void its_mask_irq(struct irq_data *d)
-{
-    struct its_device *its_dev = irq_data_get_irq_handler_data(d);
-    u32 id;
-
-    /* If MSI, propagate the mask to the RC */
-    if (IS_ENABLED(CONFIG_PCI_MSI) && d->msi_desc) {
-        id = its_msi_get_entry_nr(d->msi_desc);
-        mask_msi_irq(d);
-    } else {
-        id = d->hwirq;
-    }
-
-    lpi_set_config(its_dev, d->hwirq, id, 0);
-}
-
-static void its_unmask_irq(struct irq_data *d)
-{
-    struct its_device *its_dev = irq_data_get_irq_handler_data(d);
-    u32 id;
-
-    /* If MSI, propagate the unmask to the RC */
-    if (IS_ENABLED(CONFIG_PCI_MSI) && d->msi_desc) {
-        id = its_msi_get_entry_nr(d->msi_desc);
-        unmask_msi_irq(d);
-    } else {
-        id = d->hwirq;
-    }
-
-    lpi_set_config(its_dev, d->hwirq, id, 1);
-}
-
-static void its_eoi_irq(struct irq_data *d)
-{
-    gic_write_eoir(d->hwirq);
-}
-
-static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
-                            bool force)
-{
-    unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask);
-    struct its_device *its_dev = irq_data_get_irq_handler_data(d);
-    struct its_collection *target_col;
-    u32 id;
-
-    if (cpu >= nr_cpu_ids)
-        return -EINVAL;
-
-    target_col = &its_dev->its->collections[cpu];
-    if (IS_ENABLED(CONFIG_PCI_MSI) && d->msi_desc)
-        id = its_msi_get_entry_nr(d->msi_desc);
-    else
-        id = d->hwirq;
-    its_send_movi(its_dev, target_col, id);
-    its_dev->collection = target_col;
-
-    return IRQ_SET_MASK_OK;
-}
-
-static struct irq_chip its_irq_chip = {
-    .name            = "ITS",
-    .irq_mask        = its_mask_irq,
-    .irq_unmask        = its_unmask_irq,
-    .irq_eoi        = its_eoi_irq,
-    .irq_set_affinity    = its_set_affinity,
-};
-

Most of those callbacks seems useful to me. Why did you drop them?

[..]

-static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids)
+static unsigned long *its_lpi_alloc_chunks(int nr_irq, int *base, int *nr_ids)
  {
      unsigned long *bitmap = NULL;
      int chunk_id;
      int nr_chunks;
      int i;

-    nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK);
+    nr_chunks = DIV_ROUND_UP(nr_irq, IRQS_PER_CHUNK);

      spin_lock(&lpi_lock);

      do {
-        chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks,
-                                              0, nr_chunks, 0);
+        chunk_id = find_next_zero_bit(lpi_bitmap, lpi_chunks, 0);

This is not the same. bitmap_find_next_zero_area looks for a contiguous region of nr_chunks while find_next_zero_bit looks for the first 0 bit.

[..]

  /*
@@ -732,31 +654,28 @@ static void its_lpi_free(unsigned long *bitmap, int base, 
int nr_ids)
  /*
   * This is how many bits of ID we need, including the useless ones.
   */
-#define LPI_NRBITS        ilog2(LPI_PROPBASE_SZ + SZ_8K)
+#define LPI_NRBITS        (fls(LPI_PROPBASE_SZ + SZ_8K) - 1)

-#define LPI_PROP_DEFAULT_PRIO    0xa0
+#define LPI_PROP_DEFAULT_PRIO    0xa2

It's more than a compilation fix...

  static int __init its_alloc_lpi_tables(void)
  {
-    phys_addr_t paddr;
+    gic_rdists->prop_page = 
alloc_xenheap_pages(get_order_from_bytes(LPI_PROPBASE_SZ), 0);

-    gic_rdists->prop_page = alloc_pages(GFP_NOWAIT,
-                       get_order(LPI_PROPBASE_SZ));
      if (!gic_rdists->prop_page) {
-        pr_err("Failed to allocate PROPBASE\n");
+        its_err("Failed to allocate PROPBASE\n");
          return -ENOMEM;
      }

-    paddr = page_to_phys(gic_rdists->prop_page);
-    pr_info("GIC: using LPI property table @%pa\n", &paddr);
+    its_info("GIC: using LPI property table @%pa\n", gic_rdists->prop_page);

      /* Priority 0xa0, Group-1, disabled */
-    memset(page_address(gic_rdists->prop_page),
+    memset(gic_rdists->prop_page,
             LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1,
             LPI_PROPBASE_SZ);

      /* Make sure the GIC will observe the written configuration */
-    __flush_dcache_area(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ);
+    clean_dcache_va_range(gic_rdists->prop_page, LPI_PROPBASE_SZ);

Ditto for __flush_dcache_area

[..]

@@ -806,17 +725,18 @@ static int its_alloc_tables(struct its_node *its)
          if (type == GITS_BASER_TYPE_NONE)
              continue;

-        base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 
get_order(max_ittsize));
+        base = alloc_xenheap_pages(get_order_from_bytes(max_ittsize), 0);
          if (!base) {
              err = -ENOMEM;
              goto out_free;
          }
-
+        memset(base, 0, max_ittsize);
+        clear_page(base);

Why do you do both? memset should be enough. Although, you may need to align max_ittsize.

[..]

@@ -909,32 +827,30 @@ static int its_alloc_collections(struct its_node *its)
  static void its_cpu_init_lpis(void)
  {
      void __iomem *rbase = gic_data_rdist_rd_base();
-    struct page *pend_page;
+    void *pend_page;
      u64 val, tmp;

      /* If we didn't allocate the pending table yet, do it now */
-    pend_page = gic_data_rdist()->pend_page;
+    pend_page = gic_data_rdist().pend_page;

It would make sense to have gic_data_rdist returning a pointer. That would avoid a such change.

[..]

+        memset(pend_page, 0, max(LPI_PENDBASE_SZ, SZ_64K));
          /* Make sure the GIC will observe the zero-ed page */
-        __flush_dcache_area(page_address(pend_page), LPI_PENDBASE_SZ);
+        clean_dcache_va_range(pend_page, LPI_PENDBASE_SZ);

Ditto for clean_dcache_va_range

[..]


-static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
-                                            int nvecs)
+/* TODO: Removed static for compilation */

It's a bit strange to add a TODO but not on the same other changes. See its_lpi_free for instance.

[..]

-static int its_alloc_device_irq(struct its_device *dev, u32 id,
-                                int *hwirq, unsigned int *irq)
+/* TODO: Removed static for compilation */
+int its_alloc_device_irq(struct its_device *dev, u32 id,
+                         int *hwirq, unsigned int *irq)
  {
      int idx;

@@ -1101,9 +1019,6 @@ static int its_alloc_device_irq(struct its_device *dev, 
u32 id,
          return -ENOSPC;

      *hwirq = dev->lpi_base + idx;
-    *irq = irq_create_mapping(lpi_domain, *hwirq);
-    if (!*irq)
-        return -ENOSPC;    /* Don't kill the device, though */

With this change *irq is not set at all. Do you plan to fix it later?

      set_bit(idx, dev->lpi_map);

@@ -1113,134 +1028,52 @@ static int its_alloc_device_irq(struct its_device 
*dev, u32 id,
      return 0;
  }

-
-
-static int its_msi_get_vec_count(struct pci_dev *pdev, struct msi_desc *desc)
-{
-#ifdef CONFIG_PCI_MSI
-    if (desc->msi_attrib.is_msix)
-        return pci_msix_vec_count(pdev);
-    else
-        return pci_msi_vec_count(pdev);
-#else
-    return -EINVAL;
-#endif
-}
-
-int pci_requester_id(struct pci_dev *dev);
-static int its_msi_setup_irq(struct msi_chip *chip,
-                             struct pci_dev *pdev,
-                             struct msi_desc *desc)
-{
-    struct its_node *its = container_of(chip, struct its_node, msi_chip);
-    struct its_device *its_dev;
-    struct msi_msg msg;
-    unsigned int irq;
-    u64 addr;
-    int hwirq;
-    int err;
-    u32 dev_id = pci_requester_id(pdev);
-    u32 vec_nr;
-
-    its_dev = its_find_device(its, dev_id);
-    if (!its_dev) {
-        int nvec = its_msi_get_vec_count(pdev, desc);
-        if (WARN_ON(nvec <= 0))
-            return nvec;
-        its_dev = its_create_device(its, dev_id, nvec);
-    }
-    if (!its_dev)
-        return -ENOMEM;
-    vec_nr = its_msi_get_entry_nr(desc);
-    err = its_alloc_device_irq(its_dev, vec_nr, &hwirq, &irq);
-    if (err)
-        return err;
-
-    irq_set_msi_desc(irq, desc);
-    irq_set_handler_data(irq, its_dev);
-
-    addr = its->phys_base + GITS_TRANSLATER;
-
-    msg.address_lo        = (u32)addr;
-    msg.address_hi        = (u32)(addr >> 32);
-    msg.data        = vec_nr;
-
-    write_msi_msg(irq, &msg);
-    return 0;
-}
-
-static void its_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
-{
-    struct irq_data *d = irq_get_irq_data(irq);
-    struct its_device *its_dev = irq_data_get_irq_handler_data(d);
-
-    BUG_ON(d->hwirq < its_dev->lpi_base ||        /* OMG! */
-           d->hwirq > (its_dev->lpi_base + its_dev->nr_lpis));
-
-    /* Stop the delivery of interrupts */
-    its_send_discard(its_dev, its_msi_get_entry_nr(d->msi_desc));
-
-    /* Mark interrupt index as unused, and clear the mapping */
-    clear_bit(d->hwirq - its_dev->lpi_base, its_dev->lpi_map);
-    irq_dispose_mapping(irq);
-
-    /* If all interrupts have been freed, start mopping the floor */
-    if (bitmap_empty(its_dev->lpi_map, its_dev->nr_lpis)) {
-        its_lpi_free(its_dev->lpi_map,
-                     its_dev->lpi_base,
-                     its_dev->nr_lpis);
-
-        /* Unmap device/itt */
-        its_send_mapd(its_dev, 0);
-        its_free_device(its_dev);
-    }
-}
-

Those 2 functions seems useful for me. Why did you drop them?

-static int its_probe(struct device_node *node)
+static int its_probe(struct dt_device_node *node)
  {
-    struct resource res;
+    paddr_t its_addr, its_size;
      struct its_node *its;
      void __iomem *its_base;
      u32 val;
      u64 baser, tmp;
      int err;

-    err = of_address_to_resource(node, 0, &res);
-    if (err) {
-        pr_warn("%s: no regs?\n", node->full_name);
+    err = dt_device_get_address(node, 0, &its_addr, &its_size);
+    if ( err || !its_addr )

Why the second check (!its_addr)?

+    {
+        its_warn("%s: cannot find GIC-ITS\n", node->full_name);
          return -ENXIO;
      }

-    its_base = ioremap(res.start, resource_size(&res));
-    if (!its_base) {
-        pr_warn("%s: unable to map registers\n", node->full_name);
+    its_base = ioremap_nocache(its_addr, its_size);
+    if ( !its_base)
+    {

Please keep the Linux coding style.

[..]

@@ -1255,19 +1088,19 @@ static int its_probe(struct device_node *node)

[..]

      writeq_relaxed(baser, its->base + GITS_CBASER);
      tmp = readq_relaxed(its->base + GITS_CBASER);
-/*    writeq_relaxed(0, its->base + GITS_CWRITER); */
+    writeq_relaxed(0, its->base + GITS_CWRITER);

Care to explain why it was commented on the previous patch and you uncomment here?

[..]

  out_free_tables:
      its_free_tables(its);
  out_free_cmd:
-    kfree(its->cmd_base);
+    xfree(its->cmd_base);
  out_free_its:
-    kfree(its);
+    xfree(its);
  out_unmap:
-    iounmap(its_base);
-    pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
+    //TODO: no call for iounmap in xen?

The function exists since 2012 in xen/vmap.h ...

[..]

-static struct of_device_id its_device_id[] = {
-    {    .compatible    = "arm,gic-v3-its",    },
-    {},
-};
-
-struct irq_chip *its_init(struct device_node *node, struct rdists *rdists,
-                          struct irq_domain *domain)
+int its_init(struct dt_device_node *node, struct rdist_prop *rdists)
  {
-    struct device_node *np;
+    static const struct dt_device_match its_device_ids[] __initconst =
+    {
+        DT_MATCH_COMPATIBLE("arm,gic-v3-its"),
+        { /* sentinel */ },
+    };

Just modifing the type of the its_device_id would have been work too.

s/of_device_id/dt_device_match/

+    struct dt_device_node *np = NULL;

-    for (np = of_find_matching_node(node, its_device_id); np;
-         np = of_find_matching_node(np, its_device_id)) {
-        its_probe(np);
+    while ( (np = dt_find_matching_node(np, its_device_ids)) )
+    {
+        if ( !dt_find_property(np, "msi-controller", NULL) )
+            continue;
+
+        if ( dt_get_parent(np) )
+            break;

Linux doesn't do those check, why do you need them?

[..]

diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 47452ca..5c35ac5 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -63,6 +63,7 @@ static struct gic_info gicv3_info;

  /* per-cpu re-distributor base */
  static DEFINE_PER_CPU(void __iomem*, rbase);
+DEFINE_PER_CPU(struct rdist, rdist);

It would have been better to create a separate patch before in order to implment rdist correctly.

Regards,

--
Julien Grall

_______________________________________________
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®.