[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |