From 25f98057076ce0474c2d37a238861476f974079b Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 5 Apr 2016 00:08:42 +0000 Subject: [PATCH 01/30] Don't wakeup the fdc worker thread once a second when idle. The fdc worker thread was using a one second timeout while waiting for a new bio to arrive or for the device to detach. However, the driver already does a wakeup when queueing a new bio or asking the thread to detach, so the timeout only served to waste CPU time waking up the thread once a second just so it could go right back to sleep. Use an infinite timeout instead. Discussed with: phk Sponsored by: Netflix --- sys/dev/fdc/fdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c index a8a607fd83f..a4abd196a57 100644 --- a/sys/dev/fdc/fdc.c +++ b/sys/dev/fdc/fdc.c @@ -968,7 +968,7 @@ fdc_worker(struct fdc_data *fdc) fdc->bp = bioq_takefirst(&fdc->head); if (fdc->bp == NULL) msleep(&fdc->head, &fdc->fdc_mtx, - PRIBIO, "-", hz); + PRIBIO, "-", 0); } while (fdc->bp == NULL && (fdc->flags & FDC_KTHREAD_EXIT) == 0); mtx_unlock(&fdc->fdc_mtx); From 2f9b9f9c7f137578b1038e80a2d149b6c4acb806 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 5 Apr 2016 00:09:19 +0000 Subject: [PATCH 02/30] Remove an unneeded check. CPUs with valid per-CPU data are not absent. Sponsored by: Netflix --- sys/net/netisr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sys/net/netisr.c b/sys/net/netisr.c index 64efbd772b6..6b89a854818 100644 --- a/sys/net/netisr.c +++ b/sys/net/netisr.c @@ -1167,9 +1167,6 @@ netisr_start(void *arg) STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) { if (nws_count >= netisr_maxthreads) break; - /* XXXRW: Is skipping absent CPUs still required here? */ - if (CPU_ABSENT(pc->pc_cpuid)) - continue; /* Worker will already be present for boot CPU. */ if (pc->pc_netisr != NULL) continue; From b406166f664417f55f3c170dd2436837bee7e091 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 5 Apr 2016 00:10:07 +0000 Subject: [PATCH 03/30] Remove a redundant check. cpu_suspend_map is always empty if smp_started is false. Sponsored by: Netflix --- sys/dev/xen/control/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/dev/xen/control/control.c b/sys/dev/xen/control/control.c index 6a351d831ec..c7bd8c27b29 100644 --- a/sys/dev/xen/control/control.c +++ b/sys/dev/xen/control/control.c @@ -260,7 +260,7 @@ xctrl_suspend() #ifdef SMP /* Send an IPI_BITMAP in case there are pending bitmap IPIs. */ lapic_ipi_vectored(IPI_BITMAP_VECTOR, APIC_IPI_DEST_ALL); - if (smp_started && !CPU_EMPTY(&cpu_suspend_map)) { + if (!CPU_EMPTY(&cpu_suspend_map)) { /* * Now that event channels have been initialized, * resume CPUs. From 281a5e676b7c498a0ce9dc105132ff041eeb6b07 Mon Sep 17 00:00:00 2001 From: Warren Block Date: Tue, 5 Apr 2016 01:12:56 +0000 Subject: [PATCH 04/30] Add another real-life example of setting a quirk for a USB gaming keyboard. From forum thread: https://forums.freebsd.org/threads/55717/ MFC after: 1 week --- share/man/man4/usb_quirk.4 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/share/man/man4/usb_quirk.4 b/share/man/man4/usb_quirk.4 index 0140ccb1eef..5ca603ab09d 100644 --- a/share/man/man4/usb_quirk.4 +++ b/share/man/man4/usb_quirk.4 @@ -16,7 +16,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 26, 2015 +.Dd April 4, 2016 .Dt USB_QUIRK 4 .Os .Sh NAME @@ -234,6 +234,12 @@ device which appears as a USB device on usbconfig -d ugen0.3 add_quirk UQ_MSC_EJECT_WAIT .Ed .Pp +Enable a Holtec/Keep Out F85 gaming keyboard on +.Pa ugen1.4 : +.Bd -literal -offset indent +usbconfig -d ugen1.4 add_quirk UQ_KBD_BOOTPROTO +.Ed +.Pp To install a quirk at boot time, place one or several lines like the following in .Xr loader.conf 5 : From 2db664d7b55f259a39e616637c41d84864ac260b Mon Sep 17 00:00:00 2001 From: Justin Hibbits Date: Tue, 5 Apr 2016 02:27:01 +0000 Subject: [PATCH 05/30] Make i2c device child auto-probe work for MPC85xx and QorIQ SoCs. OFW i2c probing requires a new method ofw_bus_get_node(), and the bus device is assumed iichb. With these changes, i2c devices attached in fdt are probed and attached automagically. --- sys/powerpc/mpc85xx/i2c.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sys/powerpc/mpc85xx/i2c.c b/sys/powerpc/mpc85xx/i2c.c index e02a08bdb55..89c91a288cb 100644 --- a/sys/powerpc/mpc85xx/i2c.c +++ b/sys/powerpc/mpc85xx/i2c.c @@ -97,6 +97,7 @@ static int i2c_stop(device_t dev); static int i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr); static int i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay); static int i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout); +static phandle_t i2c_get_node(device_t bus, device_t dev); static device_method_t i2c_methods[] = { DEVMETHOD(device_probe, i2c_probe), @@ -110,12 +111,13 @@ static device_method_t i2c_methods[] = { DEVMETHOD(iicbus_read, i2c_read), DEVMETHOD(iicbus_write, i2c_write), DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), + DEVMETHOD(ofw_bus_get_node, i2c_get_node), { 0, 0 } }; static driver_t i2c_driver = { - "i2c", + "iichb", i2c_methods, sizeof(struct i2c_softc), }; @@ -425,3 +427,11 @@ i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout) return (IIC_NOERR); } + +static phandle_t +i2c_get_node(device_t bus, device_t dev) +{ + + /* Share controller node with iibus device. */ + return (ofw_bus_get_node(bus)); +} From cc8a3448d9f88b9d22eae8f0f1bf504ec7b73589 Mon Sep 17 00:00:00 2001 From: Justin Hibbits Date: Tue, 5 Apr 2016 03:27:33 +0000 Subject: [PATCH 06/30] Add support for the Microchip mcp7941x. This is compatible with the ds1307, but comparing the mcp7941x datasheet vs the ds1307 code, appears there is one bit placement difference, so that is now accounted for. Relnotes: yes --- sys/dev/iicbus/ds1307.c | 41 ++++++++++++++++++++++++++++++-------- sys/dev/iicbus/ds1307reg.h | 1 + 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/sys/dev/iicbus/ds1307.c b/sys/dev/iicbus/ds1307.c index 9c7cd65171c..987db0d0e75 100644 --- a/sys/dev/iicbus/ds1307.c +++ b/sys/dev/iicbus/ds1307.c @@ -60,10 +60,20 @@ struct ds1307_softc { struct intr_config_hook enum_hook; uint16_t sc_addr; /* DS1307 slave address. */ uint8_t sc_ctrl; + int sc_mcp7941x; }; static void ds1307_start(void *); +#ifdef FDT +static const struct ofw_compat_data ds1307_compat_data[] = { + {"dallas,ds1307", (uintptr_t)"Maxim DS1307 RTC"}, + {"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"}, + {"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"}, + { NULL, 0 } +}; +#endif + static int ds1307_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len) { @@ -167,21 +177,25 @@ ds1307_set_24hrs_mode(struct ds1307_softc *sc) static int ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS) { - int sqwe, error, newv; + int sqwe, error, newv, sqwe_bit; struct ds1307_softc *sc; sc = (struct ds1307_softc *)arg1; error = ds1307_ctrl_read(sc); if (error != 0) return (error); - sqwe = newv = (sc->sc_ctrl & DS1307_CTRL_SQWE) ? 1 : 0; + if (sc->sc_mcp7941x) + sqwe_bit = MCP7941X_CTRL_SQWE; + else + sqwe_bit = DS1307_CTRL_SQWE; + sqwe = newv = (sc->sc_ctrl & sqwe_bit) ? 1 : 0; error = sysctl_handle_int(oidp, &newv, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (sqwe != newv) { - sc->sc_ctrl &= ~DS1307_CTRL_SQWE; + sc->sc_ctrl &= ~sqwe_bit; if (newv) - sc->sc_ctrl |= DS1307_CTRL_SQWE; + sc->sc_ctrl |= sqwe_bit; error = ds1307_ctrl_write(sc); if (error != 0) return (error); @@ -252,17 +266,25 @@ ds1307_sqw_out_sysctl(SYSCTL_HANDLER_ARGS) static int ds1307_probe(device_t dev) { - #ifdef FDT + const struct ofw_compat_data *compat; + if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "dallas,ds1307") && - !ofw_bus_is_compatible(dev, "maxim,ds1307")) + + compat = ofw_bus_search_compatible(dev, ds1307_compat_data); + + if (compat == NULL) return (ENXIO); -#endif + + device_set_desc(dev, (const char *)compat->ocd_data); + + return (BUS_PROBE_DEFAULT); +#else device_set_desc(dev, "Maxim DS1307 RTC"); return (BUS_PROBE_DEFAULT); +#endif } static int @@ -277,6 +299,9 @@ ds1307_attach(device_t dev) sc->enum_hook.ich_func = ds1307_start; sc->enum_hook.ich_arg = dev; + if (ofw_bus_is_compatible(dev, "microchip,mcp7941x")) + sc->sc_mcp7941x = 1; + /* * We have to wait until interrupts are enabled. Usually I2C read * and write only works when the interrupts are available. diff --git a/sys/dev/iicbus/ds1307reg.h b/sys/dev/iicbus/ds1307reg.h index 0c7c356c749..ddad40b2417 100644 --- a/sys/dev/iicbus/ds1307reg.h +++ b/sys/dev/iicbus/ds1307reg.h @@ -50,6 +50,7 @@ #define DS1307_YEAR_MASK 0xff #define DS1307_CONTROL 0x07 #define DS1307_CTRL_OUT (1 << 7) +#define MCP7941X_CTRL_SQWE (1 << 6) #define DS1307_CTRL_SQWE (1 << 4) #define DS1307_CTRL_RS1 (1 << 1) #define DS1307_CTRL_RS0 (1 << 0) From b7997839907347a1264b80e2788e6ddbaa222d04 Mon Sep 17 00:00:00 2001 From: Michal Meloun Date: Tue, 5 Apr 2016 09:20:52 +0000 Subject: [PATCH 07/30] TEGRA: Fix CPU frequency switching. The PLL_X, base CPU frequency source, doesn't have a bypass switch and thus we must use another frequency source for CPU while changing its frequency. PLL_P is ideal for this, it runs at 480MHz and CPU can be clocked at this frequency at any CPU voltage. --- sys/arm/nvidia/tegra124/tegra124_clk_pll.c | 20 +++++++------------- sys/arm/nvidia/tegra124/tegra124_clk_super.c | 4 ++-- sys/arm/nvidia/tegra124/tegra124_cpufreq.c | 17 ++++++++++++++++- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_pll.c b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c index 6adfd32ff2c..2c5c3a25b70 100644 --- a/sys/arm/nvidia/tegra124/tegra124_clk_pll.c +++ b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c @@ -823,12 +823,10 @@ pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) return (0); } - /* Set bypass. */ + /* PLLX doesn't have bypass, disable it first. */ RD4(sc, sc->base_reg, ®); - reg |= PLL_BASE_BYPASS; + reg &= ~PLL_BASE_ENABLE; WR4(sc, sc->base_reg, reg); - RD4(sc, sc->base_reg, ®); - DELAY(100); /* Set PLL. */ RD4(sc, sc->base_reg, ®); @@ -840,16 +838,16 @@ pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) RD4(sc, sc->base_reg, ®); DELAY(100); + /* Enable lock detection. */ + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + /* Enable PLL. */ RD4(sc, sc->base_reg, ®); reg |= PLL_BASE_ENABLE; WR4(sc, sc->base_reg, reg); - /* Enable lock detection */ - RD4(sc, sc->misc_reg, ®); - reg |= sc->lock_enable; - WR4(sc, sc->misc_reg, reg); - rv = wait_for_lock(sc); if (rv != 0) { /* Disable PLL */ @@ -860,10 +858,6 @@ pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) } RD4(sc, sc->misc_reg, ®); - /* Clear bypass. */ - RD4(sc, sc->base_reg, ®); - reg &= ~PLL_BASE_BYPASS; - WR4(sc, sc->base_reg, reg); *fout = ((fin / m) * n) / p; return (0); } diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_super.c b/sys/arm/nvidia/tegra124/tegra124_clk_super.c index 7f32ebaeb12..1ff81797ed6 100644 --- a/sys/arm/nvidia/tegra124/tegra124_clk_super.c +++ b/sys/arm/nvidia/tegra124/tegra124_clk_super.c @@ -205,8 +205,7 @@ super_mux_set_mux(struct clknode *clk, int idx) (state != SUPER_MUX_STATE_IDLE)) { panic("Unexpected super mux state: %u", state); } - - shift = state * SUPER_MUX_MUX_WIDTH; + shift = (state - 1) * SUPER_MUX_MUX_WIDTH; sc->mux = idx; if (sc->flags & SMF_HAVE_DIVIDER_2) { if (idx == sc->src_div2) { @@ -222,6 +221,7 @@ super_mux_set_mux(struct clknode *clk, int idx) } reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift); reg |= idx << shift; + WR4(sc, sc->base_reg, reg); RD4(sc, sc->base_reg, &dummy); DEVICE_UNLOCK(sc); diff --git a/sys/arm/nvidia/tegra124/tegra124_cpufreq.c b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c index 1227c813bcb..d4498092f02 100644 --- a/sys/arm/nvidia/tegra124/tegra124_cpufreq.c +++ b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c @@ -335,12 +335,27 @@ set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq) if (rv != 0) return (rv); } - rv = clk_set_freq(sc->clk_cpu_g, point->freq, CLK_SET_ROUND_DOWN); + + /* Switch supermux to PLLP first */ + rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p); + if (rv != 0) { + device_printf(sc->dev, "Can't set parent to PLLP\n"); + return (rv); + } + + /* Set PLLX frequency */ + rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN); if (rv != 0) { device_printf(sc->dev, "Can't set CPU clock frequency\n"); return (rv); } + rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x); + if (rv != 0) { + device_printf(sc->dev, "Can't set parent to PLLX\n"); + return (rv); + } + if (sc->act_speed_point->uvolt > point->uvolt) { /* set cpu voltage */ rv = regulator_set_voltage(sc->supply_vdd_cpu, From c77702de74d8ae5010e6aa8aeef4f95b1566a5fc Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Tue, 5 Apr 2016 10:36:40 +0000 Subject: [PATCH 08/30] x86 topo: add some comments, descriptions and references to documentation Plus a minor cosmetic change. MFC after: 1 month --- sys/kern/subr_smp.c | 35 +++++++++++++++++++-- sys/x86/x86/mp_x86.c | 75 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 105 insertions(+), 5 deletions(-) diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c index 98481367d64..b09bc50ba91 100644 --- a/sys/kern/subr_smp.c +++ b/sys/kern/subr_smp.c @@ -53,9 +53,7 @@ __FBSDID("$FreeBSD$"); #ifdef SMP MALLOC_DEFINE(M_TOPO, "toponodes", "SMP topology data"); -#endif -#ifdef SMP volatile cpuset_t stopped_cpus; volatile cpuset_t started_cpus; volatile cpuset_t suspended_cpus; @@ -895,6 +893,10 @@ topo_init_root(struct topo_node *root) root->type = TOPO_TYPE_SYSTEM; } +/* + * Add a child node with the given ID under the given parent. + * Do nothing if there is already a child with that ID. + */ struct topo_node * topo_add_node_by_hwid(struct topo_node *parent, int hwid, topo_node_type type, uintptr_t subtype) @@ -921,6 +923,9 @@ topo_add_node_by_hwid(struct topo_node *parent, int hwid, return (node); } +/* + * Find a child node with the given ID under the given parent. + */ struct topo_node * topo_find_node_by_hwid(struct topo_node *parent, int hwid, topo_node_type type, uintptr_t subtype) @@ -938,6 +943,12 @@ topo_find_node_by_hwid(struct topo_node *parent, int hwid, return (NULL); } +/* + * Given a node change the order of its parent's child nodes such + * that the node becomes the firt child while preserving the cyclic + * order of the children. In other words, the given node is promoted + * by rotation. + */ void topo_promote_child(struct topo_node *child) { @@ -959,6 +970,10 @@ topo_promote_child(struct topo_node *child) } } +/* + * Iterate to the next node in the depth-first search (traversal) of + * the topology tree. + */ struct topo_node * topo_next_node(struct topo_node *top, struct topo_node *node) { @@ -977,6 +992,10 @@ topo_next_node(struct topo_node *top, struct topo_node *node) return (NULL); } +/* + * Iterate to the next node in the depth-first search of the topology tree, + * but without descending below the current node. + */ struct topo_node * topo_next_nonchild_node(struct topo_node *top, struct topo_node *node) { @@ -992,6 +1011,10 @@ topo_next_nonchild_node(struct topo_node *top, struct topo_node *node) return (NULL); } +/* + * Assign the given ID to the given topology node that represents a logical + * processor. + */ void topo_set_pu_id(struct topo_node *node, cpuid_t id) { @@ -1013,6 +1036,14 @@ topo_set_pu_id(struct topo_node *node, cpuid_t id) } } +/* + * Check if the topology is uniform, that is, each package has the same number + * of cores in it and each core has the same number of threads (logical + * processors) in it. If so, calculate the number of package, the number of + * cores per package and the number of logical processors per core. + * 'all' parameter tells whether to include administratively disabled logical + * processors into the analysis. + */ int topo_analyze(struct topo_node *topo_root, int all, int *pkg_count, int *cores_per_pkg, int *thrs_per_core) diff --git a/sys/x86/x86/mp_x86.c b/sys/x86/x86/mp_x86.c index 90051c88333..7ad9b96f1b2 100644 --- a/sys/x86/x86/mp_x86.c +++ b/sys/x86/x86/mp_x86.c @@ -176,6 +176,9 @@ mask_width(u_int x) return (fls(x << (1 - powerof2(x))) - 1); } +/* + * Add a cache level to the cache topology description. + */ static int add_deterministic_cache(int type, int level, int share_count) { @@ -217,6 +220,16 @@ add_deterministic_cache(int type, int level, int share_count) return (1); } +/* + * Determine topology of processing units and caches for AMD CPUs. + * See: + * - AMD CPUID Specification (Publication # 25481) + * - BKDG For AMD Family 10h Processors (Publication # 31116), section 2.15 + * - BKDG for AMD NPT Family 0Fh Processors (Publication # 32559) + * XXX At the moment the code does not recognize grouping of AMD CMT threads, + * if supported, into cores, so each thread is treated as being in its own + * core. In other words, each logical CPU is considered to be a core. + */ static void topo_probe_amd(void) { @@ -277,6 +290,15 @@ topo_probe_amd(void) } } +/* + * Determine topology of processing units for Intel CPUs + * using CPUID Leaf 1 and Leaf 4, if supported. + * See: + * - Intel 64 Architecture Processor Topology Enumeration + * - Intel 64 and IA-32 ArchitecturesSoftware Developer’s Manual, + * Volume 3A: System Programming Guide, PROGRAMMING CONSIDERATIONS + * FOR HARDWARE MULTI-THREADING CAPABLE PROCESSORS + */ static void topo_probe_intel_0x4(void) { @@ -302,6 +324,15 @@ topo_probe_intel_0x4(void) pkg_id_shift = core_id_shift + mask_width(max_cores); } +/* + * Determine topology of processing units for Intel CPUs + * using CPUID Leaf 11, if supported. + * See: + * - Intel 64 Architecture Processor Topology Enumeration + * - Intel 64 and IA-32 ArchitecturesSoftware Developer’s Manual, + * Volume 3A: System Programming Guide, PROGRAMMING CONSIDERATIONS + * FOR HARDWARE MULTI-THREADING CAPABLE PROCESSORS + */ static void topo_probe_intel_0xb(void) { @@ -342,6 +373,14 @@ topo_probe_intel_0xb(void) } } +/* + * Determine topology of caches for Intel CPUs. + * See: + * - Intel 64 Architecture Processor Topology Enumeration + * - Intel 64 and IA-32 Architectures Software Developer’s Manual + * Volume 2A: Instruction Set Reference, A-M, + * CPUID instruction + */ static void topo_probe_intel_caches(void) { @@ -376,14 +415,16 @@ topo_probe_intel_caches(void) } } +/* + * Determine topology of processing units and caches for Intel CPUs. + * See: + * - Intel 64 Architecture Processor Topology Enumeration + */ static void topo_probe_intel(void) { /* - * See Intel(R) 64 Architecture Processor - * Topology Enumeration article for details. - * * Note that 0x1 <= cpu_high < 4 case should be * compatible with topo_probe_intel_0x4() logic when * CPUID.1:EBX[23:16] > 0 (cpu_cores will be 1) @@ -640,6 +681,12 @@ cpu_mp_announce(void) } } +/* + * Add a scheduling group, a group of logical processors sharing + * a particular cache (and, thus having an affinity), to the scheduling + * topology. + * This function recursively works on lower level caches. + */ static void x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) { @@ -657,6 +704,11 @@ x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) else cg_root->cg_level = root->subtype; + /* + * Check how many core nodes we have under the given root node. + * If we have multiple logical processors, but not multiple + * cores, then those processors must be hardware threads. + */ ncores = 0; node = root; while (node != NULL) { @@ -673,6 +725,13 @@ x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) root->cpu_count > 1 && ncores < 2) cg_root->cg_flags = CG_FLAG_SMT; + /* + * Find out how many cache nodes we have under the given root node. + * We ignore cache nodes that cover all the same processors as the + * root node. Also, we do not descend below found cache nodes. + * That is, we count top-level "non-redundant" caches under the root + * node. + */ nchildren = 0; node = root; while (node != NULL) { @@ -689,6 +748,10 @@ x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) cg_root->cg_child = smp_topo_alloc(nchildren); cg_root->cg_children = nchildren; + /* + * Now find again the same cache nodes as above and recursively + * build scheduling topologies for them. + */ node = root; i = 0; while (node != NULL) { @@ -705,6 +768,9 @@ x86topo_add_sched_group(struct topo_node *root, struct cpu_group *cg_root) } } +/* + * Build the MI scheduling topology from the discovered hardware topology. + */ struct cpu_group * cpu_topo(void) { @@ -719,6 +785,9 @@ cpu_topo(void) } +/* + * Add a logical CPU to the topology. + */ void cpu_add(u_int apic_id, char boot_cpu) { From 4c230cdafd698f20a23f42f6c66fe725fb1b6bbf Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Tue, 5 Apr 2016 11:30:52 +0000 Subject: [PATCH 09/30] Use proper locking macros in RACCT in RCTL. MFC after: 1 month Sponsored by: The FreeBSD Foundation --- sys/kern/kern_racct.c | 76 ++++++++++++++++++++------------------- sys/kern/kern_rctl.c | 82 +++++++++++++++++++++++-------------------- 2 files changed, 83 insertions(+), 75 deletions(-) diff --git a/sys/kern/kern_racct.c b/sys/kern/kern_racct.c index 419c69e904b..bbd50ca6e21 100644 --- a/sys/kern/kern_racct.c +++ b/sys/kern/kern_racct.c @@ -93,6 +93,10 @@ SYSCTL_UINT(_kern_racct, OID_AUTO, pcpu_threshold, CTLFLAG_RW, &pcpu_threshold, static struct mtx racct_lock; MTX_SYSINIT(racct_lock, &racct_lock, "racct lock", MTX_DEF); +#define RACCT_LOCK() mtx_lock(&racct_lock) +#define RACCT_UNLOCK() mtx_unlock(&racct_lock) +#define RACCT_LOCK_ASSERT() mtx_assert(&racct_lock, MA_OWNED) + static uma_zone_t racct_zone; static void racct_sub_racct(struct racct *dest, const struct racct *src); @@ -391,7 +395,7 @@ racct_add_racct(struct racct *dest, const struct racct *src) int i; ASSERT_RACCT_ENABLED(); - mtx_assert(&racct_lock, MA_OWNED); + RACCT_LOCK_ASSERT(); /* * Update resource usage in dest. @@ -413,7 +417,7 @@ racct_sub_racct(struct racct *dest, const struct racct *src) int i; ASSERT_RACCT_ENABLED(); - mtx_assert(&racct_lock, MA_OWNED); + RACCT_LOCK_ASSERT(); /* * Update resource usage in dest. @@ -466,7 +470,7 @@ racct_destroy_locked(struct racct **racctp) SDT_PROBE1(racct, , racct, destroy, racctp); - mtx_assert(&racct_lock, MA_OWNED); + RACCT_LOCK_ASSERT(); KASSERT(racctp != NULL, ("NULL racctp")); KASSERT(*racctp != NULL, ("NULL racct")); @@ -493,9 +497,9 @@ racct_destroy(struct racct **racct) if (!racct_enable) return; - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_destroy_locked(racct); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } /* @@ -509,7 +513,7 @@ racct_adjust_resource(struct racct *racct, int resource, { ASSERT_RACCT_ENABLED(); - mtx_assert(&racct_lock, MA_OWNED); + RACCT_LOCK_ASSERT(); KASSERT(racct != NULL, ("NULL racct")); racct->r_resources[resource] += amount; @@ -574,9 +578,9 @@ racct_add(struct proc *p, int resource, uint64_t amount) SDT_PROBE3(racct, , rusage, add, p, resource, amount); - mtx_lock(&racct_lock); + RACCT_LOCK(); error = racct_add_locked(p, resource, amount, 0); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); return (error); } @@ -593,9 +597,9 @@ racct_add_force(struct proc *p, int resource, uint64_t amount) SDT_PROBE3(racct, , rusage, add__force, p, resource, amount); - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_add_locked(p, resource, amount, 1); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } static void @@ -625,9 +629,9 @@ racct_add_cred(struct ucred *cred, int resource, uint64_t amount) if (!racct_enable) return; - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_add_cred_locked(cred, resource, amount); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } static int @@ -703,9 +707,9 @@ racct_set(struct proc *p, int resource, uint64_t amount) SDT_PROBE3(racct, , rusage, set__force, p, resource, amount); - mtx_lock(&racct_lock); + RACCT_LOCK(); error = racct_set_locked(p, resource, amount, 0); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); return (error); } @@ -718,9 +722,9 @@ racct_set_force(struct proc *p, int resource, uint64_t amount) SDT_PROBE3(racct, , rusage, set, p, resource, amount); - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_set_locked(p, resource, amount, 1); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } /* @@ -800,7 +804,7 @@ racct_sub(struct proc *p, int resource, uint64_t amount) KASSERT(RACCT_CAN_DROP(resource), ("%s: called for non-droppable resource %d", __func__, resource)); - mtx_lock(&racct_lock); + RACCT_LOCK(); KASSERT(amount <= p->p_racct->r_resources[resource], ("%s: freeing %ju of resource %d, which is more " "than allocated %jd for %s (pid %d)", __func__, amount, resource, @@ -808,7 +812,7 @@ racct_sub(struct proc *p, int resource, uint64_t amount) racct_adjust_resource(p->p_racct, resource, -amount); racct_sub_cred_locked(p->p_ucred, resource, amount); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } static void @@ -843,9 +847,9 @@ racct_sub_cred(struct ucred *cred, int resource, uint64_t amount) if (!racct_enable) return; - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_sub_cred_locked(cred, resource, amount); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } /* @@ -866,7 +870,7 @@ racct_proc_fork(struct proc *parent, struct proc *child) PROC_LOCK(parent); PROC_LOCK(child); - mtx_lock(&racct_lock); + RACCT_LOCK(); #ifdef RCTL error = rctl_proc_fork(parent, child); @@ -896,7 +900,7 @@ racct_proc_fork(struct proc *parent, struct proc *child) error += racct_add_locked(child, RACCT_NTHR, 1, 0); out: - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); PROC_UNLOCK(child); PROC_UNLOCK(parent); @@ -919,10 +923,10 @@ racct_proc_fork_done(struct proc *child) if (!racct_enable) return; - mtx_lock(&racct_lock); + RACCT_LOCK(); rctl_enforce(child, RACCT_NPROC, 0); rctl_enforce(child, RACCT_NTHR, 0); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); #endif } @@ -958,7 +962,7 @@ racct_proc_exit(struct proc *p) pct_estimate = 0; pct = racct_getpcpu(p, pct_estimate); - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_set_locked(p, RACCT_CPU, runtime, 0); racct_add_cred_locked(p->p_ucred, RACCT_PCTCPU, pct); @@ -970,7 +974,7 @@ racct_proc_exit(struct proc *p) racct_set_locked(p, i, 0, 0); } - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); PROC_UNLOCK(p); #ifdef RCTL @@ -1003,7 +1007,7 @@ racct_proc_ucred_changed(struct proc *p, struct ucred *oldcred, newpr = newcred->cr_prison; oldpr = oldcred->cr_prison; - mtx_lock(&racct_lock); + RACCT_LOCK(); if (newuip != olduip) { racct_sub_racct(olduip->ui_racct, p->p_racct); racct_add_racct(newuip->ui_racct, p->p_racct); @@ -1020,7 +1024,7 @@ racct_proc_ucred_changed(struct proc *p, struct ucred *oldcred, racct_add_racct(pr->pr_prison_racct->prr_racct, p->p_racct); } - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); #ifdef RCTL rctl_proc_ucred_changed(p, newcred); @@ -1033,12 +1037,10 @@ racct_move(struct racct *dest, struct racct *src) ASSERT_RACCT_ENABLED(); - mtx_lock(&racct_lock); - + RACCT_LOCK(); racct_add_racct(dest, src); racct_sub_racct(src, src); - - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } static void @@ -1112,7 +1114,7 @@ racct_decay_callback(struct racct *racct, void *dummy1, void *dummy2) int64_t r_old, r_new; ASSERT_RACCT_ENABLED(); - mtx_assert(&racct_lock, MA_OWNED); + RACCT_LOCK_ASSERT(); r_old = racct->r_resources[RACCT_PCTCPU]; @@ -1128,14 +1130,14 @@ static void racct_decay_pre(void) { - mtx_lock(&racct_lock); + RACCT_LOCK(); } static void racct_decay_post(void) { - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); } static void @@ -1203,13 +1205,13 @@ racctd(void) } else pct_estimate = 0; pct = racct_getpcpu(p, pct_estimate); - mtx_lock(&racct_lock); + RACCT_LOCK(); racct_set_locked(p, RACCT_PCTCPU, pct, 1); racct_set_locked(p, RACCT_CPU, runtime, 0); racct_set_locked(p, RACCT_WALLCLOCK, (uint64_t)wallclock.tv_sec * 1000000 + wallclock.tv_usec, 0); - mtx_unlock(&racct_lock); + RACCT_UNLOCK(); PROC_UNLOCK(p); } diff --git a/sys/kern/kern_rctl.c b/sys/kern/kern_rctl.c index 1970ed85f1d..7f6a7ad5075 100644 --- a/sys/kern/kern_rctl.c +++ b/sys/kern/kern_rctl.c @@ -181,6 +181,13 @@ static uma_zone_t rctl_rule_zone; static struct rwlock rctl_lock; RW_SYSINIT(rctl_lock, &rctl_lock, "RCTL lock"); +#define RCTL_RLOCK() rw_rlock(&rctl_lock) +#define RCTL_RUNLOCK() rw_runlock(&rctl_lock) +#define RCTL_WLOCK() rw_wlock(&rctl_lock) +#define RCTL_WUNLOCK() rw_wunlock(&rctl_lock) +#define RCTL_LOCK_ASSERT() rw_assert(&rctl_lock, RA_LOCKED) +#define RCTL_WLOCK_ASSERT() rw_assert(&rctl_lock, RA_WLOCKED) + static int rctl_rule_fully_specified(const struct rctl_rule *rule); static void rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule); @@ -231,7 +238,7 @@ rctl_proc_rule_to_racct(const struct proc *p, const struct rctl_rule *rule) struct ucred *cred = p->p_ucred; ASSERT_RACCT_ENABLED(); - rw_assert(&rctl_lock, RA_LOCKED); + RCTL_LOCK_ASSERT(); switch (rule->rr_per) { case RCTL_SUBJECT_TYPE_PROCESS: @@ -258,7 +265,7 @@ rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) const struct racct *racct; ASSERT_RACCT_ENABLED(); - rw_assert(&rctl_lock, RA_LOCKED); + RCTL_LOCK_ASSERT(); racct = rctl_proc_rule_to_racct(p, rule); available = rule->rr_amount - racct->r_resources[rule->rr_resource]; @@ -277,8 +284,7 @@ rctl_would_exceed(const struct proc *p, const struct rctl_rule *rule, int64_t available; ASSERT_RACCT_ENABLED(); - - rw_assert(&rctl_lock, RA_LOCKED); + RCTL_LOCK_ASSERT(); available = rctl_available_resource(p, rule); if (available >= amount) @@ -302,7 +308,7 @@ rctl_pcpu_available(const struct proc *p) { minavailable = INT64_MAX; limit = 0; - rw_rlock(&rctl_lock); + RCTL_RLOCK(); LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { rule = link->rrl_rule; @@ -317,7 +323,7 @@ rctl_pcpu_available(const struct proc *p) { } } - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); /* * Return slightly less than actual value of the available @@ -352,7 +358,7 @@ rctl_enforce(struct proc *p, int resource, uint64_t amount) ASSERT_RACCT_ENABLED(); - rw_rlock(&rctl_lock); + RCTL_RLOCK(); /* * There may be more than one matching rule; go through all of them. @@ -460,7 +466,7 @@ rctl_enforce(struct proc *p, int resource, uint64_t amount) } } - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); if (should_deny) { /* @@ -482,7 +488,7 @@ rctl_get_limit(struct proc *p, int resource) ASSERT_RACCT_ENABLED(); - rw_rlock(&rctl_lock); + RCTL_RLOCK(); /* * There may be more than one matching rule; go through all of them. @@ -498,7 +504,7 @@ rctl_get_limit(struct proc *p, int resource) amount = rule->rr_amount; } - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); return (amount); } @@ -514,7 +520,7 @@ rctl_get_available(struct proc *p, int resource) ASSERT_RACCT_ENABLED(); - rw_rlock(&rctl_lock); + RCTL_RLOCK(); /* * There may be more than one matching rule; go through all of them. @@ -531,7 +537,7 @@ rctl_get_available(struct proc *p, int resource) minavailable = available; } - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); /* * XXX: Think about this _hard_. @@ -675,9 +681,9 @@ rctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule) link->rrl_rule = rule; link->rrl_exceeded = 0; - rw_wlock(&rctl_lock); + RCTL_WLOCK(); LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); } static int @@ -687,7 +693,7 @@ rctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule) ASSERT_RACCT_ENABLED(); KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); - rw_assert(&rctl_lock, RA_WLOCKED); + RCTL_WLOCK_ASSERT(); link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT); if (link == NULL) @@ -713,7 +719,7 @@ rctl_racct_remove_rules(struct racct *racct, struct rctl_rule_link *link, *linktmp; ASSERT_RACCT_ENABLED(); - rw_assert(&rctl_lock, RA_WLOCKED); + RCTL_WLOCK_ASSERT(); LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) { if (!rctl_rule_matches(link->rrl_rule, filter)) @@ -1172,14 +1178,14 @@ static void rctl_rule_pre_callback(void) { - rw_wlock(&rctl_lock); + RCTL_WLOCK(); } static void rctl_rule_post_callback(void) { - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); } static void @@ -1189,7 +1195,7 @@ rctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3) int found = 0; ASSERT_RACCT_ENABLED(); - rw_assert(&rctl_lock, RA_WLOCKED); + RCTL_WLOCK_ASSERT(); found += rctl_racct_remove_rules(racct, filter); @@ -1210,9 +1216,9 @@ rctl_rule_remove(struct rctl_rule *filter) if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS && filter->rr_subject.rs_proc != NULL) { p = filter->rr_subject.rs_proc; - rw_wlock(&rctl_lock); + RCTL_WLOCK(); found = rctl_racct_remove_rules(p->p_racct, filter); - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); if (found) return (0); return (ESRCH); @@ -1229,11 +1235,11 @@ rctl_rule_remove(struct rctl_rule *filter) filter, (void *)&found); sx_assert(&allproc_lock, SA_LOCKED); - rw_wlock(&rctl_lock); + RCTL_WLOCK(); FOREACH_PROC_IN_SYSTEM(p) { found += rctl_racct_remove_rules(p->p_racct, filter); } - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); if (found) return (0); @@ -1460,7 +1466,7 @@ rctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3) struct sbuf *sb = (struct sbuf *)arg3; ASSERT_RACCT_ENABLED(); - rw_assert(&rctl_lock, RA_LOCKED); + RCTL_LOCK_ASSERT(); LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { if (!rctl_rule_matches(link->rrl_rule, filter)) @@ -1511,7 +1517,7 @@ sys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) KASSERT(sb != NULL, ("sbuf_new failed")); FOREACH_PROC_IN_SYSTEM(p) { - rw_rlock(&rctl_lock); + RCTL_RLOCK(); LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { /* * Non-process rules will be added to the buffer later. @@ -1525,7 +1531,7 @@ sys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) rctl_rule_to_sbuf(sb, link->rrl_rule); sbuf_printf(sb, ","); } - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); } loginclass_racct_foreach(rctl_get_rules_callback, @@ -1612,13 +1618,13 @@ sys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); KASSERT(sb != NULL, ("sbuf_new failed")); - rw_rlock(&rctl_lock); + RCTL_RLOCK(); LIST_FOREACH(link, &filter->rr_subject.rs_proc->p_racct->r_rule_links, rrl_next) { rctl_rule_to_sbuf(sb, link->rrl_rule); sbuf_printf(sb, ","); } - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); if (sbuf_error(sb) == ENOMEM) { error = ERANGE; goto out; @@ -1743,7 +1749,7 @@ again: * credentials. */ rulecnt = 0; - rw_rlock(&rctl_lock); + RCTL_RLOCK(); LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { if (link->rrl_rule->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS) @@ -1755,7 +1761,7 @@ again: rulecnt++; LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) rulecnt++; - rw_runlock(&rctl_lock); + RCTL_RUNLOCK(); /* * Create temporary list. We've dropped the rctl_lock in order @@ -1773,7 +1779,7 @@ again: /* * Assign rules to the newly allocated list entries. */ - rw_wlock(&rctl_lock); + RCTL_WLOCK(); LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { if (link->rrl_rule->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS) { @@ -1841,13 +1847,13 @@ again: newlink, rrl_next); } - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); return; } goaround: - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); /* * Rule list changed while we were not holding the rctl_lock. @@ -1879,7 +1885,7 @@ rctl_proc_fork(struct proc *parent, struct proc *child) ASSERT_RACCT_ENABLED(); KASSERT(parent->p_racct != NULL, ("process without racct; p = %p", parent)); - rw_wlock(&rctl_lock); + RCTL_WLOCK(); /* * Go through limits applicable to the parent and assign them @@ -1908,7 +1914,7 @@ rctl_proc_fork(struct proc *parent, struct proc *child) } } - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); return (0); fail: @@ -1918,7 +1924,7 @@ fail: rctl_rule_release(link->rrl_rule); uma_zfree(rctl_rule_link_zone, link); } - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); return (EAGAIN); } @@ -1932,14 +1938,14 @@ rctl_racct_release(struct racct *racct) ASSERT_RACCT_ENABLED(); - rw_wlock(&rctl_lock); + RCTL_WLOCK(); while (!LIST_EMPTY(&racct->r_rule_links)) { link = LIST_FIRST(&racct->r_rule_links); LIST_REMOVE(link, rrl_next); rctl_rule_release(link->rrl_rule); uma_zfree(rctl_rule_link_zone, link); } - rw_wunlock(&rctl_lock); + RCTL_WUNLOCK(); } static void From c520cb4f509af9d55b5e394503ab6953170380b2 Mon Sep 17 00:00:00 2001 From: Michal Meloun Date: Tue, 5 Apr 2016 12:13:53 +0000 Subject: [PATCH 10/30] ehci_interrupt is MPSAFE code. Most drivers in tree calls bus_setup_intr with MPSAFE, some are not. Fix those. Submitted by: Howard Su Differential Revision: https://reviews.freebsd.org/D5755 --- sys/arm/nvidia/tegra_ehci.c | 4 ++-- sys/dev/usb/controller/ehci_fsl.c | 2 +- sys/dev/usb/controller/ehci_imx.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sys/arm/nvidia/tegra_ehci.c b/sys/arm/nvidia/tegra_ehci.c index e7f7b228342..1834b33a1fd 100644 --- a/sys/arm/nvidia/tegra_ehci.c +++ b/sys/arm/nvidia/tegra_ehci.c @@ -253,8 +253,8 @@ tegra_ehci_attach(device_t dev) } /* Setup interrupt handler. */ - rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO, NULL, - (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); + rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); if (rv != 0) { device_printf(dev, "Could not setup IRQ\n"); goto out; diff --git a/sys/dev/usb/controller/ehci_fsl.c b/sys/dev/usb/controller/ehci_fsl.c index ed6c25937d9..7db642e780f 100644 --- a/sys/dev/usb/controller/ehci_fsl.c +++ b/sys/dev/usb/controller/ehci_fsl.c @@ -294,7 +294,7 @@ fsl_ehci_attach(device_t self) } /* Setup interrupt handler */ - err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO, + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); diff --git a/sys/dev/usb/controller/ehci_imx.c b/sys/dev/usb/controller/ehci_imx.c index 4473ebc4fd3..07f73105f9a 100644 --- a/sys/dev/usb/controller/ehci_imx.c +++ b/sys/dev/usb/controller/ehci_imx.c @@ -261,8 +261,8 @@ imx_ehci_attach(device_t dev) } /* Setup interrupt handler. */ - err = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO, NULL, - (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); + err = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); if (err != 0) { device_printf(dev, "Could not setup IRQ\n"); goto out; From 472f2cca82fe3e9cf0af93cc2637fb27e2043086 Mon Sep 17 00:00:00 2001 From: Svatopluk Kraus Date: Tue, 5 Apr 2016 13:37:03 +0000 Subject: [PATCH 11/30] Rework bcm283x interrupt controller for INTRNG and enable it on RPI-B by default. Reviewed by: gonzo Differential Revision: https://reviews.freebsd.org/D5809 --- sys/arm/broadcom/bcm2835/bcm2835_intr.c | 290 +++++++++++++++++++++++- sys/arm/conf/RPI-B | 2 + 2 files changed, 291 insertions(+), 1 deletion(-) diff --git a/sys/arm/broadcom/bcm2835/bcm2835_intr.c b/sys/arm/broadcom/bcm2835/bcm2835_intr.c index 941e9386717..936dc529deb 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_intr.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_intr.c @@ -30,12 +30,15 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include #include #include #include +#include #include #include #include @@ -49,6 +52,10 @@ __FBSDID("$FreeBSD$"); #include #endif +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + #define INTC_PENDING_BASIC 0x00 #define INTC_PENDING_BANK1 0x04 #define INTC_PENDING_BANK2 0x08 @@ -60,17 +67,55 @@ __FBSDID("$FreeBSD$"); #define INTC_DISABLE_BANK2 0x20 #define INTC_DISABLE_BASIC 0x24 +#define INTC_PENDING_BASIC_ARM 0x0000FF +#define INTC_PENDING_BASIC_GPU1_PEND 0x000100 +#define INTC_PENDING_BASIC_GPU2_PEND 0x000200 +#define INTC_PENDING_BASIC_GPU1_7 0x000400 +#define INTC_PENDING_BASIC_GPU1_9 0x000800 +#define INTC_PENDING_BASIC_GPU1_10 0x001000 +#define INTC_PENDING_BASIC_GPU1_18 0x002000 +#define INTC_PENDING_BASIC_GPU1_19 0x004000 +#define INTC_PENDING_BASIC_GPU2_21 0x008000 +#define INTC_PENDING_BASIC_GPU2_22 0x010000 +#define INTC_PENDING_BASIC_GPU2_23 0x020000 +#define INTC_PENDING_BASIC_GPU2_24 0x040000 +#define INTC_PENDING_BASIC_GPU2_25 0x080000 +#define INTC_PENDING_BASIC_GPU2_30 0x100000 +#define INTC_PENDING_BASIC_MASK 0x1FFFFF + +#define INTC_PENDING_BASIC_GPU1_MASK (INTC_PENDING_BASIC_GPU1_7 | \ + INTC_PENDING_BASIC_GPU1_9 | \ + INTC_PENDING_BASIC_GPU1_10 | \ + INTC_PENDING_BASIC_GPU1_18 | \ + INTC_PENDING_BASIC_GPU1_19) + +#define INTC_PENDING_BASIC_GPU2_MASK (INTC_PENDING_BASIC_GPU2_21 | \ + INTC_PENDING_BASIC_GPU2_22 | \ + INTC_PENDING_BASIC_GPU2_23 | \ + INTC_PENDING_BASIC_GPU2_24 | \ + INTC_PENDING_BASIC_GPU2_25 | \ + INTC_PENDING_BASIC_GPU2_30) + +#define INTC_PENDING_BANK1_MASK (~((1 << 7) | (1 << 9) | (1 << 10) | \ + (1 << 18) | (1 << 19))) +#define INTC_PENDING_BANK2_MASK (~((1 << 21) | (1 << 22) | (1 << 23) | \ + (1 << 24) | (1 << 25) | (1 << 30))) + #define BANK1_START 8 #define BANK1_END (BANK1_START + 32 - 1) #define BANK2_START (BANK1_START + 32) #define BANK2_END (BANK2_START + 32 - 1) +#ifndef ARM_INTRNG #define BANK3_START (BANK2_START + 32) #define BANK3_END (BANK3_START + 32 - 1) +#endif #define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START)) #define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) #define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) +#ifndef ARM_INTRNG #define ID_IRQ_BCM2836(n) (((n) >= BANK3_START) && ((n) <= BANK3_END)) +#endif #define IRQ_BANK1(n) ((n) - BANK1_START) #define IRQ_BANK2(n) ((n) - BANK2_START) @@ -80,11 +125,28 @@ __FBSDID("$FreeBSD$"); #define dprintf(fmt, args...) #endif +#ifdef ARM_INTRNG +#define BCM_INTC_NIRQS 72 /* 8 + 32 + 32 */ + +struct bcm_intc_irqsrc { + struct intr_irqsrc bii_isrc; + u_int bii_irq; + uint16_t bii_disable_reg; + uint16_t bii_enable_reg; + uint32_t bii_mask; +}; +#endif + struct bcm_intc_softc { device_t sc_dev; struct resource * intc_res; bus_space_tag_t intc_bst; bus_space_handle_t intc_bsh; +#ifdef ARM_INTRNG + struct resource * intc_irq_res; + void * intc_irq_hdl; + struct bcm_intc_irqsrc intc_isrcs[BCM_INTC_NIRQS]; +#endif }; static struct bcm_intc_softc *bcm_intc_sc = NULL; @@ -94,6 +156,192 @@ static struct bcm_intc_softc *bcm_intc_sc = NULL; #define intc_write_4(_sc, reg, val) \ bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val)) +#ifdef ARM_INTRNG +static inline void +bcm_intc_isrc_mask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii) +{ + + intc_write_4(sc, bii->bii_disable_reg, bii->bii_mask); +} + +static inline void +bcm_intc_isrc_unmask(struct bcm_intc_softc *sc, struct bcm_intc_irqsrc *bii) +{ + + intc_write_4(sc, bii->bii_enable_reg, bii->bii_mask); +} + +static inline int +bcm2835_intc_active_intr(struct bcm_intc_softc *sc) +{ + uint32_t pending, pending_gpu; + + pending = intc_read_4(sc, INTC_PENDING_BASIC) & INTC_PENDING_BASIC_MASK; + if (pending == 0) + return (-1); + if (pending & INTC_PENDING_BASIC_ARM) + return (ffs(pending) - 1); + if (pending & INTC_PENDING_BASIC_GPU1_MASK) { + if (pending & INTC_PENDING_BASIC_GPU1_7) + return (BANK1_START + 7); + if (pending & INTC_PENDING_BASIC_GPU1_9) + return (BANK1_START + 9); + if (pending & INTC_PENDING_BASIC_GPU1_10) + return (BANK1_START + 10); + if (pending & INTC_PENDING_BASIC_GPU1_18) + return (BANK1_START + 18); + if (pending & INTC_PENDING_BASIC_GPU1_19) + return (BANK1_START + 19); + } + if (pending & INTC_PENDING_BASIC_GPU2_MASK) { + if (pending & INTC_PENDING_BASIC_GPU2_21) + return (BANK2_START + 21); + if (pending & INTC_PENDING_BASIC_GPU2_22) + return (BANK2_START + 22); + if (pending & INTC_PENDING_BASIC_GPU2_23) + return (BANK2_START + 23); + if (pending & INTC_PENDING_BASIC_GPU2_24) + return (BANK2_START + 24); + if (pending & INTC_PENDING_BASIC_GPU2_25) + return (BANK2_START + 25); + if (pending & INTC_PENDING_BASIC_GPU2_30) + return (BANK2_START + 30); + } + if (pending & INTC_PENDING_BASIC_GPU1_PEND) { + pending_gpu = intc_read_4(sc, INTC_PENDING_BANK1); + pending_gpu &= INTC_PENDING_BANK1_MASK; + if (pending_gpu != 0) + return (BANK1_START + ffs(pending_gpu) - 1); + } + if (pending & INTC_PENDING_BASIC_GPU2_PEND) { + pending_gpu = intc_read_4(sc, INTC_PENDING_BANK2); + pending_gpu &= INTC_PENDING_BANK2_MASK; + if (pending_gpu != 0) + return (BANK2_START + ffs(pending_gpu) - 1); + } + return (-1); /* It shouldn't end here, but it's hardware. */ +} + +static int +bcm2835_intc_intr(void *arg) +{ + int irq, num; + struct bcm_intc_softc *sc = arg; + + for (num = 0; ; num++) { + irq = bcm2835_intc_active_intr(sc); + if (irq == -1) + break; + if (intr_isrc_dispatch(&sc->intc_isrcs[irq].bii_isrc, + curthread->td_intr_frame) != 0) { + bcm_intc_isrc_mask(sc, &sc->intc_isrcs[irq]); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", + irq); + } + arm_irq_memory_barrier(0); /* XXX */ + } + if (num == 0) + device_printf(sc->sc_dev, "Spurious interrupt detected\n"); + + return (FILTER_HANDLED); +} + +static void +bcm_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_intc_irqsrc *bii = (struct bcm_intc_irqsrc *)isrc; + + arm_irq_memory_barrier(bii->bii_irq); + bcm_intc_isrc_unmask(device_get_softc(dev), bii); +} + +static void +bcm_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_intc_isrc_mask(device_get_softc(dev), + (struct bcm_intc_irqsrc *)isrc); +} + +static int +bcm_intc_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + u_int irq; + struct bcm_intc_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + if (data->fdt.ncells == 1) + irq = data->fdt.cells[0]; + else if (data->fdt.ncells == 2) + irq = data->fdt.cells[0] * 32 + data->fdt.cells[1]; + else + return (EINVAL); + + if (irq >= BCM_INTC_NIRQS) + return (EINVAL); + + sc = device_get_softc(dev); + *isrcp = &sc->intc_isrcs[irq].bii_isrc; + return (0); +} + +static void +bcm_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_intc_disable_intr(dev, isrc); +} + +static void +bcm_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_intc_enable_intr(dev, isrc); +} + +static void +bcm_intc_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static int +bcm_intc_pic_register(struct bcm_intc_softc *sc, intptr_t xref) +{ + struct bcm_intc_irqsrc *bii; + int error; + uint32_t irq; + const char *name; + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < BCM_INTC_NIRQS; irq++) { + bii = &sc->intc_isrcs[irq]; + bii->bii_irq = irq; + if (IS_IRQ_BASIC(irq)) { + bii->bii_disable_reg = INTC_DISABLE_BASIC; + bii->bii_enable_reg = INTC_ENABLE_BASIC; + bii->bii_mask = 1 << irq; + } else if (IS_IRQ_BANK1(irq)) { + bii->bii_disable_reg = INTC_DISABLE_BANK1; + bii->bii_enable_reg = INTC_ENABLE_BANK1; + bii->bii_mask = 1 << IRQ_BANK1(irq); + } else if (IS_IRQ_BANK2(irq)) { + bii->bii_disable_reg = INTC_DISABLE_BANK2; + bii->bii_enable_reg = INTC_ENABLE_BANK2; + bii->bii_mask = 1 << IRQ_BANK2(irq); + } else + return (ENXIO); + + error = intr_isrc_register(&bii->bii_isrc, sc->sc_dev, 0, + "%s,%u", name, irq); + if (error != 0) + return (error); + } + return (intr_pic_register(sc->sc_dev, xref)); +} +#endif + static int bcm_intc_probe(device_t dev) { @@ -112,7 +360,9 @@ bcm_intc_attach(device_t dev) { struct bcm_intc_softc *sc = device_get_softc(dev); int rid = 0; - +#ifdef ARM_INTRNG + intptr_t xref; +#endif sc->sc_dev = dev; if (bcm_intc_sc) @@ -124,6 +374,32 @@ bcm_intc_attach(device_t dev) return (ENXIO); } +#ifdef ARM_INTRNG + xref = OF_xref_from_node(ofw_bus_get_node(dev)); + if (bcm_intc_pic_register(sc, xref) != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->intc_res); + device_printf(dev, "could not register PIC\n"); + return (ENXIO); + } + + rid = 0; + sc->intc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->intc_irq_res == NULL) { + if (intr_pic_claim_root(dev, xref, bcm2835_intc_intr, sc, 0) != 0) { + /* XXX clean up */ + device_printf(dev, "could not set PIC as a root\n"); + return (ENXIO); + } + } else { + if (bus_setup_intr(dev, sc->intc_irq_res, INTR_TYPE_CLK, + bcm2835_intc_intr, NULL, sc, &sc->intc_irq_hdl)) { + /* XXX clean up */ + device_printf(dev, "could not setup irq handler\n"); + return (ENXIO); + } + } +#endif sc->intc_bst = rman_get_bustag(sc->intc_res); sc->intc_bsh = rman_get_bushandle(sc->intc_res); @@ -135,6 +411,16 @@ bcm_intc_attach(device_t dev) static device_method_t bcm_intc_methods[] = { DEVMETHOD(device_probe, bcm_intc_probe), DEVMETHOD(device_attach, bcm_intc_attach), + +#ifdef ARM_INTRNG + DEVMETHOD(pic_disable_intr, bcm_intc_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_intc_enable_intr), + DEVMETHOD(pic_map_intr, bcm_intc_map_intr), + DEVMETHOD(pic_post_filter, bcm_intc_post_filter), + DEVMETHOD(pic_post_ithread, bcm_intc_post_ithread), + DEVMETHOD(pic_pre_ithread, bcm_intc_pre_ithread), +#endif + { 0, 0 } }; @@ -148,6 +434,7 @@ static devclass_t bcm_intc_devclass; DRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, 0, 0); +#ifndef ARM_INTRNG int arm_get_next_irq(int last_irq) { @@ -247,3 +534,4 @@ intr_pic_init_secondary(void) { } #endif +#endif diff --git a/sys/arm/conf/RPI-B b/sys/arm/conf/RPI-B index db5f7678055..9bc2636e091 100644 --- a/sys/arm/conf/RPI-B +++ b/sys/arm/conf/RPI-B @@ -24,6 +24,8 @@ include "std.armv6" include "../broadcom/bcm2835/std.rpi" include "../broadcom/bcm2835/std.bcm2835" +options ARM_INTRNG + options HZ=100 options SCHED_4BSD # 4BSD scheduler options PLATFORM From 120b6fc9b2c98550c520adf4d70230f81fb0f7cb Mon Sep 17 00:00:00 2001 From: Svatopluk Kraus Date: Tue, 5 Apr 2016 13:41:51 +0000 Subject: [PATCH 12/30] Implement bcm2836 interrupt controller for INTRNG and enable it on RPI2 by default. Differential Revision: https://reviews.freebsd.org/D5822 --- sys/arm/broadcom/bcm2835/bcm2836.c | 712 ++++++++++++++++++++++++++ sys/arm/broadcom/bcm2835/bcm2836.h | 3 +- sys/arm/broadcom/bcm2835/bcm2836_mp.c | 2 + sys/arm/conf/RPI2 | 2 + 4 files changed, 718 insertions(+), 1 deletion(-) diff --git a/sys/arm/broadcom/bcm2835/bcm2836.c b/sys/arm/broadcom/bcm2835/bcm2836.c index c6f8cb0954c..05e9cc68c07 100644 --- a/sys/arm/broadcom/bcm2835/bcm2836.c +++ b/sys/arm/broadcom/bcm2835/bcm2836.c @@ -1,5 +1,6 @@ /* * Copyright 2015 Andrew Turner. + * Copyright 2016 Svatopluk Kraus * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,19 +29,33 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include +#include #include #include +#include #include +#ifdef SMP +#include +#endif #include +#include #include +#ifdef SMP +#include +#endif #include #include +#ifdef ARM_INTRNG +#include "pic_if.h" +#else #include #define ARM_LOCAL_BASE 0x40000000 @@ -55,7 +70,703 @@ __FBSDID("$FreeBSD$"); #define INT_PENDING_MASK 0x011f #define MAILBOX0_IRQ 4 #define MAILBOX0_IRQEN (1 << 0) +#endif +#ifdef ARM_INTRNG +#define BCM_LINTC_CONTROL_REG 0x00 +#define BCM_LINTC_PRESCALER_REG 0x08 +#define BCM_LINTC_GPU_ROUTING_REG 0x0c +#define BCM_LINTC_PMU_ROUTING_SET_REG 0x10 +#define BCM_LINTC_PMU_ROUTING_CLR_REG 0x14 +#define BCM_LINTC_TIMER_CFG_REG(n) (0x40 + (n) * 4) +#define BCM_LINTC_MBOX_CFG_REG(n) (0x50 + (n) * 4) +#define BCM_LINTC_PENDING_REG(n) (0x60 + (n) * 4) +#define BCM_LINTC_MBOX0_SET_REG(n) (0x80 + (n) * 16) +#define BCM_LINTC_MBOX1_SET_REG(n) (0x84 + (n) * 16) +#define BCM_LINTC_MBOX2_SET_REG(n) (0x88 + (n) * 16) +#define BCM_LINTC_MBOX3_SET_REG(n) (0x8C + (n) * 16) +#define BCM_LINTC_MBOX0_CLR_REG(n) (0xC0 + (n) * 16) +#define BCM_LINTC_MBOX1_CLR_REG(n) (0xC4 + (n) * 16) +#define BCM_LINTC_MBOX2_CLR_REG(n) (0xC8 + (n) * 16) +#define BCM_LINTC_MBOX3_CLR_REG(n) (0xCC + (n) * 16) + +/* Prescaler Register */ +#define BCM_LINTC_PSR_19_2 0x80000000 /* 19.2 MHz */ + +/* GPU Interrupt Routing Register */ +#define BCM_LINTC_GIRR_IRQ_CORE(n) (n) +#define BCM_LINTC_GIRR_FIQ_CORE(n) ((n) << 2) + +/* PMU Interrupt Routing Register */ +#define BCM_LINTC_PIRR_IRQ_EN_CORE(n) (1 << (n)) +#define BCM_LINTC_PIRR_FIQ_EN_CORE(n) (1 << ((n) + 4)) + +/* Timer Config Register */ +#define BCM_LINTC_TCR_IRQ_EN_TIMER(n) (1 << (n)) +#define BCM_LINTC_TCR_FIQ_EN_TIMER(n) (1 << ((n) + 4)) + +/* MBOX Config Register */ +#define BCM_LINTC_MCR_IRQ_EN_MBOX(n) (1 << (n)) +#define BCM_LINTC_MCR_FIQ_EN_MBOX(n) (1 << ((n) + 4)) + +#define BCM_LINTC_CNTPSIRQ_IRQ 0 +#define BCM_LINTC_CNTPNSIRQ_IRQ 1 +#define BCM_LINTC_CNTHPIRQ_IRQ 2 +#define BCM_LINTC_CNTVIRQ_IRQ 3 +#define BCM_LINTC_MBOX0_IRQ 4 +#define BCM_LINTC_MBOX1_IRQ 5 +#define BCM_LINTC_MBOX2_IRQ 6 +#define BCM_LINTC_MBOX3_IRQ 7 +#define BCM_LINTC_GPU_IRQ 8 +#define BCM_LINTC_PMU_IRQ 9 +#define BCM_LINTC_AXI_IRQ 10 +#define BCM_LINTC_LTIMER_IRQ 11 + +#define BCM_LINTC_NIRQS 12 + +#define BCM_LINTC_TIMER0_IRQ BCM_LINTC_CNTPSIRQ_IRQ +#define BCM_LINTC_TIMER1_IRQ BCM_LINTC_CNTPNSIRQ_IRQ +#define BCM_LINTC_TIMER2_IRQ BCM_LINTC_CNTHPIRQ_IRQ +#define BCM_LINTC_TIMER3_IRQ BCM_LINTC_CNTVIRQ_IRQ + +#define BCM_LINTC_TIMER0_IRQ_MASK (1 << BCM_LINTC_TIMER0_IRQ) +#define BCM_LINTC_TIMER1_IRQ_MASK (1 << BCM_LINTC_TIMER1_IRQ) +#define BCM_LINTC_TIMER2_IRQ_MASK (1 << BCM_LINTC_TIMER2_IRQ) +#define BCM_LINTC_TIMER3_IRQ_MASK (1 << BCM_LINTC_TIMER3_IRQ) +#define BCM_LINTC_MBOX0_IRQ_MASK (1 << BCM_LINTC_MBOX0_IRQ) +#define BCM_LINTC_GPU_IRQ_MASK (1 << BCM_LINTC_GPU_IRQ) +#define BCM_LINTC_PMU_IRQ_MASK (1 << BCM_LINTC_PMU_IRQ) + +#define BCM_LINTC_UP_PENDING_MASK \ + (BCM_LINTC_TIMER0_IRQ_MASK | \ + BCM_LINTC_TIMER1_IRQ_MASK | \ + BCM_LINTC_TIMER2_IRQ_MASK | \ + BCM_LINTC_TIMER3_IRQ_MASK | \ + BCM_LINTC_GPU_IRQ_MASK | \ + BCM_LINTC_PMU_IRQ_MASK) + +#define BCM_LINTC_SMP_PENDING_MASK \ + (BCM_LINTC_UP_PENDING_MASK | \ + BCM_LINTC_MBOX0_IRQ_MASK) + +#ifdef SMP +#define BCM_LINTC_PENDING_MASK BCM_LINTC_SMP_PENDING_MASK +#else +#define BCM_LINTC_PENDING_MASK BCM_LINTC_UP_PENDING_MASK +#endif + +struct bcm_lintc_irqsrc { + struct intr_irqsrc bli_isrc; + u_int bli_irq; + union { + u_int bli_mask; /* for timers */ + u_int bli_value; /* for GPU */ + }; +}; + +struct bcm_lintc_softc { + device_t bls_dev; + struct mtx bls_mtx; + struct resource * bls_mem; + bus_space_tag_t bls_bst; + bus_space_handle_t bls_bsh; + struct bcm_lintc_irqsrc bls_isrcs[BCM_LINTC_NIRQS]; +}; + +static struct bcm_lintc_softc *bcm_lintc_sc; + +#ifdef SMP +#define BCM_LINTC_NIPIS 32 /* only mailbox 0 is used for IPI */ +CTASSERT(INTR_IPI_COUNT <= BCM_LINTC_NIPIS); +#endif + +#define BCM_LINTC_LOCK(sc) mtx_lock_spin(&(sc)->bls_mtx) +#define BCM_LINTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->bls_mtx) +#define BCM_LINTC_LOCK_INIT(sc) mtx_init(&(sc)->bls_mtx, \ + device_get_nameunit((sc)->bls_dev), "bmc_local_intc", MTX_SPIN) +#define BCM_LINTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->bls_mtx) + +#define bcm_lintc_read_4(sc, reg) \ + bus_space_read_4((sc)->bls_bst, (sc)->bls_bsh, (reg)) +#define bcm_lintc_write_4(sc, reg, val) \ + bus_space_write_4((sc)->bls_bst, (sc)->bls_bsh, (reg), (val)) + +static inline void +bcm_lintc_rwreg_clr(struct bcm_lintc_softc *sc, uint32_t reg, + uint32_t mask) +{ + + bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) & ~mask); +} + +static inline void +bcm_lintc_rwreg_set(struct bcm_lintc_softc *sc, uint32_t reg, + uint32_t mask) +{ + + bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) | mask); +} + +static void +bcm_lintc_timer_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + cpuset_t *cpus; + uint32_t cpu; + + cpus = &bli->bli_isrc.isrc_cpu; + + BCM_LINTC_LOCK(sc); + for (cpu = 0; cpu < 4; cpu++) + if (CPU_ISSET(cpu, cpus)) + bcm_lintc_rwreg_clr(sc, BCM_LINTC_TIMER_CFG_REG(cpu), + bli->bli_mask); + BCM_LINTC_UNLOCK(sc); +} + +static void +bcm_lintc_timer_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + cpuset_t *cpus; + uint32_t cpu; + + cpus = &bli->bli_isrc.isrc_cpu; + + BCM_LINTC_LOCK(sc); + for (cpu = 0; cpu < 4; cpu++) + if (CPU_ISSET(cpu, cpus)) + bcm_lintc_rwreg_set(sc, BCM_LINTC_TIMER_CFG_REG(cpu), + bli->bli_mask); + BCM_LINTC_UNLOCK(sc); +} + +static inline void +bcm_lintc_gpu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + + /* It's accessed just and only by one core. */ + bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, 0); +} + +static inline void +bcm_lintc_gpu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + + /* It's accessed just and only by one core. */ + bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, bli->bli_value); +} + +static inline void +bcm_lintc_pmu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + cpuset_t *cpus; + uint32_t cpu, mask; + + mask = 0; + cpus = &bli->bli_isrc.isrc_cpu; + + BCM_LINTC_LOCK(sc); + for (cpu = 0; cpu < 4; cpu++) + if (CPU_ISSET(cpu, cpus)) + mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu); + /* Write-clear register. */ + bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_CLR_REG, mask); + BCM_LINTC_UNLOCK(sc); +} + +static inline void +bcm_lintc_pmu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + cpuset_t *cpus; + uint32_t cpu, mask; + + mask = 0; + cpus = &bli->bli_isrc.isrc_cpu; + + BCM_LINTC_LOCK(sc); + for (cpu = 0; cpu < 4; cpu++) + if (CPU_ISSET(cpu, cpus)) + mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu); + /* Write-set register. */ + bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, mask); + BCM_LINTC_UNLOCK(sc); +} + +static void +bcm_lintc_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + + switch (bli->bli_irq) { + case BCM_LINTC_TIMER0_IRQ: + case BCM_LINTC_TIMER1_IRQ: + case BCM_LINTC_TIMER2_IRQ: + case BCM_LINTC_TIMER3_IRQ: + bcm_lintc_timer_mask(sc, bli); + return; + case BCM_LINTC_MBOX0_IRQ: + case BCM_LINTC_MBOX1_IRQ: + case BCM_LINTC_MBOX2_IRQ: + case BCM_LINTC_MBOX3_IRQ: + return; + case BCM_LINTC_GPU_IRQ: + bcm_lintc_gpu_mask(sc, bli); + return; + case BCM_LINTC_PMU_IRQ: + bcm_lintc_pmu_mask(sc, bli); + return; + default: + panic("%s: not implemented for irq %u", __func__, bli->bli_irq); + } +} + +static void +bcm_lintc_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli) +{ + + switch (bli->bli_irq) { + case BCM_LINTC_TIMER0_IRQ: + case BCM_LINTC_TIMER1_IRQ: + case BCM_LINTC_TIMER2_IRQ: + case BCM_LINTC_TIMER3_IRQ: + bcm_lintc_timer_unmask(sc, bli); + return; + case BCM_LINTC_MBOX0_IRQ: + case BCM_LINTC_MBOX1_IRQ: + case BCM_LINTC_MBOX2_IRQ: + case BCM_LINTC_MBOX3_IRQ: + return; + case BCM_LINTC_GPU_IRQ: + bcm_lintc_gpu_unmask(sc, bli); + return; + case BCM_LINTC_PMU_IRQ: + bcm_lintc_pmu_unmask(sc, bli); + return; + default: + panic("%s: not implemented for irq %u", __func__, bli->bli_irq); + } +} + +#ifdef SMP +static inline void +bcm_lintc_ipi_write(struct bcm_lintc_softc *sc, cpuset_t cpus, u_int ipi) +{ + u_int cpu; + uint32_t mask; + + mask = 1 << ipi; + for (cpu = 0; cpu < mp_ncpus; cpu++) + if (CPU_ISSET(cpu, &cpus)) + bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_SET_REG(cpu), + mask); +} + +static inline void +bcm_lintc_ipi_dispatch(struct bcm_lintc_softc *sc, u_int cpu, + struct trapframe *tf) +{ + u_int ipi; + uint32_t mask; + + mask = bcm_lintc_read_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu)); + if (mask == 0) { + device_printf(sc->bls_dev, "Spurious ipi detected\n"); + return; + } + + for (ipi = 0; mask != 0; mask >>= 1, ipi++) { + if ((mask & 0x01) == 0) + continue; + /* + * Clear an IPI before dispatching to not miss anyone + * and make sure that it's observed by everybody. + */ + bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu), 1 << ipi); + dsb(); + intr_ipi_dispatch(ipi, tf); + } +} +#endif + +static inline void +bcm_lintc_irq_dispatch(struct bcm_lintc_softc *sc, u_int irq, + struct trapframe *tf) +{ + struct bcm_lintc_irqsrc *bli; + + bli = &sc->bls_isrcs[irq]; + if (intr_isrc_dispatch(&bli->bli_isrc, tf) != 0) + device_printf(sc->bls_dev, "Stray irq %u detected\n", irq); +} + +static int +bcm_lintc_intr(void *arg) +{ + struct bcm_lintc_softc *sc; + u_int cpu; + uint32_t num, reg; + struct trapframe *tf; + + sc = arg; + cpu = PCPU_GET(cpuid); + tf = curthread->td_intr_frame; + + for (num = 0; ; num++) { + reg = bcm_lintc_read_4(sc, BCM_LINTC_PENDING_REG(cpu)); + if ((reg & BCM_LINTC_PENDING_MASK) == 0) + break; +#ifdef SMP + if (reg & BCM_LINTC_MBOX0_IRQ_MASK) + bcm_lintc_ipi_dispatch(sc, cpu, tf); +#endif + if (reg & BCM_LINTC_TIMER0_IRQ_MASK) + bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER0_IRQ, tf); + if (reg & BCM_LINTC_TIMER1_IRQ_MASK) + bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER1_IRQ, tf); + if (reg & BCM_LINTC_TIMER2_IRQ_MASK) + bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER2_IRQ, tf); + if (reg & BCM_LINTC_TIMER3_IRQ_MASK) + bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER3_IRQ, tf); + if (reg & BCM_LINTC_GPU_IRQ_MASK) + bcm_lintc_irq_dispatch(sc, BCM_LINTC_GPU_IRQ, tf); + if (reg & BCM_LINTC_PMU_IRQ_MASK) + bcm_lintc_irq_dispatch(sc, BCM_LINTC_PMU_IRQ, tf); + + arm_irq_memory_barrier(0); /* XXX */ + } + reg &= ~BCM_LINTC_PENDING_MASK; + if (reg != 0) + device_printf(sc->bls_dev, "Unknown interrupt(s) %x\n", reg); + else if (num == 0) + device_printf(sc->bls_dev, "Spurious interrupt detected\n"); + + return (FILTER_HANDLED); +} + +static void +bcm_lintc_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_lintc_mask(device_get_softc(dev), (struct bcm_lintc_irqsrc *)isrc); +} + +static void +bcm_lintc_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; + + arm_irq_memory_barrier(bli->bli_irq); + bcm_lintc_unmask(device_get_softc(dev), bli); +} + +static int +bcm_lintc_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct bcm_lintc_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + if (data->fdt.ncells != 1 || data->fdt.cells[0] >= BCM_LINTC_NIRQS) + return (EINVAL); + + sc = device_get_softc(dev); + *isrcp = &sc->bls_isrcs[data->fdt.cells[0]].bli_isrc; + return (0); +} + +static void +bcm_lintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; + + if (bli->bli_irq == BCM_LINTC_GPU_IRQ) + bcm_lintc_gpu_mask(device_get_softc(dev), bli); + else { + /* + * Handler for PPI interrupt does not make sense much unless + * there is one bound ithread for each core for it. Thus the + * interrupt can be masked on current core only while ithread + * bounded to this core ensures unmasking on the same core. + */ + panic ("%s: handlers are not supported", __func__); + } +} + +static void +bcm_lintc_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc; + + if (bli->bli_irq == BCM_LINTC_GPU_IRQ) + bcm_lintc_gpu_unmask(device_get_softc(dev), bli); + else { + /* See comment in bcm_lintc_pre_ithread(). */ + panic ("%s: handlers are not supported", __func__); + } +} + +static void +bcm_lintc_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static int +bcm_lintc_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct bcm_lintc_softc *sc; + + if (isrc->isrc_handlers == 0 && isrc->isrc_flags & INTR_ISRCF_PPI) { + sc = device_get_softc(dev); + BCM_LINTC_LOCK(sc); + CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + BCM_LINTC_UNLOCK(sc); + } + return (0); +} + +#ifdef SMP +static bool +bcm_lint_init_on_ap(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli, + u_int cpu) +{ + struct intr_irqsrc *isrc; + + isrc = &bli->bli_isrc; + + KASSERT(isrc->isrc_flags & INTR_ISRCF_PPI, + ("%s: irq %d is not PPI", __func__, bli->bli_irq)); + + if (isrc->isrc_handlers == 0) + return (false); + if (isrc->isrc_flags & INTR_ISRCF_BOUND) + return (CPU_ISSET(cpu, &isrc->isrc_cpu)); + + CPU_SET(cpu, &isrc->isrc_cpu); + return (true); +} + +static void +bcm_lintc_init_rwreg_on_ap(struct bcm_lintc_softc *sc, u_int cpu, u_int irq, + uint32_t reg, uint32_t mask) +{ + + if (bcm_lint_init_on_ap(sc, &sc->bls_isrcs[irq], cpu)) + bcm_lintc_rwreg_set(sc, reg, mask); +} + +static void +bcm_lintc_init_pmu_on_ap(struct bcm_lintc_softc *sc, u_int cpu) +{ + + if (bcm_lint_init_on_ap(sc, &sc->bls_isrcs[BCM_LINTC_PMU_IRQ], cpu)) { + /* Write-set register. */ + bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, + BCM_LINTC_PIRR_IRQ_EN_CORE(cpu)); + } +} + +static void +bcm_lintc_init_secondary(device_t dev) +{ + u_int cpu; + struct bcm_lintc_softc *sc; + + cpu = PCPU_GET(cpuid); + sc = device_get_softc(dev); + + BCM_LINTC_LOCK(sc); + bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER0_IRQ, + BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(0)); + bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER1_IRQ, + BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(1)); + bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER2_IRQ, + BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(2)); + bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER3_IRQ, + BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(3)); + bcm_lintc_init_pmu_on_ap(sc, cpu); + BCM_LINTC_UNLOCK(sc); +} + +static void +bcm_lintc_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus, + u_int ipi) +{ + struct bcm_lintc_softc *sc = device_get_softc(dev); + + KASSERT(isrc == &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc, + ("%s: bad ISRC %p argument", __func__, isrc)); + bcm_lintc_ipi_write(sc, cpus, ipi); +} + +static int +bcm_lintc_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) +{ + struct bcm_lintc_softc *sc = device_get_softc(dev); + + KASSERT(ipi < BCM_LINTC_NIPIS, ("%s: too high ipi %u", __func__, ipi)); + + *isrcp = &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc; + return (0); +} +#endif + +static int +bcm_lintc_pic_attach(struct bcm_lintc_softc *sc) +{ + struct bcm_lintc_irqsrc *bisrcs; + int error; + u_int flags; + uint32_t irq; + const char *name; + intptr_t xref; + + bisrcs = sc->bls_isrcs; + name = device_get_nameunit(sc->bls_dev); + for (irq = 0; irq < BCM_LINTC_NIRQS; irq++) { + bisrcs[irq].bli_irq = irq; + switch (irq) { + case BCM_LINTC_TIMER0_IRQ: + bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(0); + flags = INTR_ISRCF_PPI; + break; + case BCM_LINTC_TIMER1_IRQ: + bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(1); + flags = INTR_ISRCF_PPI; + break; + case BCM_LINTC_TIMER2_IRQ: + bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(2); + flags = INTR_ISRCF_PPI; + break; + case BCM_LINTC_TIMER3_IRQ: + bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(3); + flags = INTR_ISRCF_PPI; + break; + case BCM_LINTC_MBOX0_IRQ: + case BCM_LINTC_MBOX1_IRQ: + case BCM_LINTC_MBOX2_IRQ: + case BCM_LINTC_MBOX3_IRQ: + bisrcs[irq].bli_value = 0; /* not used */ + flags = INTR_ISRCF_IPI; + break; + case BCM_LINTC_GPU_IRQ: + bisrcs[irq].bli_value = BCM_LINTC_GIRR_IRQ_CORE(0); + flags = 0; + break; + case BCM_LINTC_PMU_IRQ: + bisrcs[irq].bli_value = 0; /* not used */ + flags = INTR_ISRCF_PPI; + break; + default: + bisrcs[irq].bli_value = 0; /* not used */ + flags = 0; + break; + } + + error = intr_isrc_register(&bisrcs[irq].bli_isrc, sc->bls_dev, + flags, "%s,%u", name, irq); + if (error != 0) + return (error); + } + + xref = OF_xref_from_node(ofw_bus_get_node(sc->bls_dev)); + error = intr_pic_register(sc->bls_dev, xref); + if (error != 0) + return (error); + + return (intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc, 0)); +} + +static int +bcm_lintc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "brcm,bcm2836-l1-intc")) + return (ENXIO); + device_set_desc(dev, "BCM2836 Interrupt Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +bcm_lintc_attach(device_t dev) +{ + struct bcm_lintc_softc *sc; + int cpu, rid; + + sc = device_get_softc(dev); + + sc->bls_dev = dev; + if (bcm_lintc_sc != NULL) + return (ENXIO); + + rid = 0; + sc->bls_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->bls_mem == NULL) { + device_printf(dev, "could not allocate memory resource\n"); + return (ENXIO); + } + + sc->bls_bst = rman_get_bustag(sc->bls_mem); + sc->bls_bsh = rman_get_bushandle(sc->bls_mem); + + bcm_lintc_write_4(sc, BCM_LINTC_CONTROL_REG, 0); + bcm_lintc_write_4(sc, BCM_LINTC_PRESCALER_REG, BCM_LINTC_PSR_19_2); + + /* Disable all timers on all cores. */ + for (cpu = 0; cpu < 4; cpu++) + bcm_lintc_write_4(sc, BCM_LINTC_TIMER_CFG_REG(cpu), 0); + +#ifdef SMP + /* Enable mailbox 0 on all cores used for IPI. */ + for (cpu = 0; cpu < 4; cpu++) + bcm_lintc_write_4(sc, BCM_LINTC_MBOX_CFG_REG(cpu), + BCM_LINTC_MCR_IRQ_EN_MBOX(0)); +#endif + + if (bcm_lintc_pic_attach(sc) != 0) { + device_printf(dev, "could not attach PIC\n"); + return (ENXIO); + } + + BCM_LINTC_LOCK_INIT(sc); + bcm_lintc_sc = sc; + return (0); +} + +static device_method_t bcm_lintc_methods[] = { + DEVMETHOD(device_probe, bcm_lintc_probe), + DEVMETHOD(device_attach, bcm_lintc_attach), + + DEVMETHOD(pic_disable_intr, bcm_lintc_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_lintc_enable_intr), + DEVMETHOD(pic_map_intr, bcm_lintc_map_intr), + DEVMETHOD(pic_post_filter, bcm_lintc_post_filter), + DEVMETHOD(pic_post_ithread, bcm_lintc_post_ithread), + DEVMETHOD(pic_pre_ithread, bcm_lintc_pre_ithread), + DEVMETHOD(pic_setup_intr, bcm_lintc_setup_intr), +#ifdef SMP + DEVMETHOD(pic_init_secondary, bcm_lintc_init_secondary), + DEVMETHOD(pic_ipi_send, bcm_lintc_ipi_send), + DEVMETHOD(pic_ipi_setup, bcm_lintc_ipi_setup), +#endif + + DEVMETHOD_END +}; + +static driver_t bcm_lintc_driver = { + "local_intc", + bcm_lintc_methods, + sizeof(struct bcm_lintc_softc), +}; + +static devclass_t bcm_lintc_devclass; + +EARLY_DRIVER_MODULE(local_intc, simplebus, bcm_lintc_driver, bcm_lintc_devclass, + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +#else /* * A driver for features of the bcm2836. */ @@ -214,3 +925,4 @@ static driver_t bcm2836_driver = { EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +#endif diff --git a/sys/arm/broadcom/bcm2835/bcm2836.h b/sys/arm/broadcom/bcm2835/bcm2836.h index 7acd41be157..14abfe2e49b 100644 --- a/sys/arm/broadcom/bcm2835/bcm2836.h +++ b/sys/arm/broadcom/bcm2835/bcm2836.h @@ -30,10 +30,11 @@ #ifndef _BCM2815_BCM2836_H #define _BCM2815_BCM2836_H +#ifndef ARM_INTRNG #define BCM2836_GPU_IRQ 8 int bcm2836_get_next_irq(int); void bcm2836_mask_irq(uintptr_t); void bcm2836_unmask_irq(uintptr_t); - +#endif #endif diff --git a/sys/arm/broadcom/bcm2835/bcm2836_mp.c b/sys/arm/broadcom/bcm2835/bcm2836_mp.c index 6a3a8578e50..a361319a115 100644 --- a/sys/arm/broadcom/bcm2835/bcm2836_mp.c +++ b/sys/arm/broadcom/bcm2835/bcm2836_mp.c @@ -139,6 +139,7 @@ platform_mp_start_ap(void) } } +#ifndef ARM_INTRNG void pic_ipi_send(cpuset_t cpus, u_int ipi) { @@ -176,3 +177,4 @@ void pic_ipi_clear(int ipi) { } +#endif diff --git a/sys/arm/conf/RPI2 b/sys/arm/conf/RPI2 index cfbc14eca6a..3cee50d3f1f 100644 --- a/sys/arm/conf/RPI2 +++ b/sys/arm/conf/RPI2 @@ -24,6 +24,8 @@ include "std.armv6" include "../broadcom/bcm2835/std.rpi" include "../broadcom/bcm2835/std.bcm2836" +options ARM_INTRNG + options HZ=100 options SCHED_ULE # ULE scheduler options SMP # Enable multiple cores From 89de2fb6d4af4956b1855f4e43d7c7f88cbca7f9 Mon Sep 17 00:00:00 2001 From: Svatopluk Kraus Date: Tue, 5 Apr 2016 13:45:23 +0000 Subject: [PATCH 13/30] Rework BCM283x gpio interrupt controller for INTRNG. It's used on RPI-B and RPI2 where INTRNG is already enabled by default. Differential Revision: https://reviews.freebsd.org/D5810 --- sys/arm/broadcom/bcm2835/bcm2835_gpio.c | 383 +++++++++++++++++++++++- sys/boot/fdt/dts/arm/bcm2835.dtsi | 2 +- sys/boot/fdt/dts/arm/bcm2836.dtsi | 2 +- 3 files changed, 380 insertions(+), 7 deletions(-) diff --git a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c index 6fff6d80c20..d448fc5cd4e 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c @@ -28,6 +28,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include @@ -37,10 +39,12 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include +#include #include #include @@ -49,6 +53,10 @@ __FBSDID("$FreeBSD$"); #include "gpio_if.h" +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) @@ -64,10 +72,10 @@ __FBSDID("$FreeBSD$"); static struct resource_spec bcm_gpio_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { SYS_RES_IRQ, 0, RF_ACTIVE }, - { SYS_RES_IRQ, 1, RF_ACTIVE }, - { SYS_RES_IRQ, 2, RF_ACTIVE }, - { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ + { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ + { SYS_RES_IRQ, 2, RF_ACTIVE }, /* bank 1 interrupt (mirrored) */ + { SYS_RES_IRQ, 3, RF_ACTIVE }, /* bank 0-1 interrupt (united) */ { -1, 0, 0 } }; @@ -76,6 +84,15 @@ struct bcm_gpio_sysctl { uint32_t pin; }; +#ifdef ARM_INTRNG +struct bcm_gpio_irqsrc { + struct intr_irqsrc bgi_isrc; + uint32_t bgi_irq; + uint32_t bgi_reg; + uint32_t bgi_mask; +}; +#endif + struct bcm_gpio_softc { device_t sc_dev; device_t sc_busdev; @@ -88,10 +105,16 @@ struct bcm_gpio_softc { int sc_ro_npins; int sc_ro_pins[BCM_GPIO_PINS]; struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; +#ifndef ARM_INTRNG struct intr_event * sc_events[BCM_GPIO_PINS]; +#endif struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; +#ifdef ARM_INTRNG + struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; +#else enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS]; enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS]; +#endif }; enum bcm_gpio_pud { @@ -130,6 +153,13 @@ enum bcm_gpio_pud { static struct bcm_gpio_softc *bcm_gpio_sc = NULL; +#ifdef ARM_INTRNG +static int bcm_gpio_intr_bank0(void *arg); +static int bcm_gpio_intr_bank1(void *arg); +static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); +static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); +#endif + static int bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) { @@ -661,6 +691,7 @@ bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) return (0); } +#ifndef ARM_INTRNG static int bcm_gpio_intr(void *arg) { @@ -694,6 +725,7 @@ bcm_gpio_intr(void *arg) return (FILTER_HANDLED); } +#endif static int bcm_gpio_probe(device_t dev) @@ -709,6 +741,49 @@ bcm_gpio_probe(device_t dev) return (BUS_PROBE_DEFAULT); } +#ifdef ARM_INTRNG +static int +bcm_gpio_intr_attach(device_t dev) +{ + struct bcm_gpio_softc *sc; + + /* + * Only first two interrupt lines are used. Third line is + * mirrored second line and forth line is common for all banks. + */ + sc = device_get_softc(dev); + if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) + return (-1); + + if (bcm_gpio_pic_attach(sc) != 0) { + device_printf(dev, "unable to attach PIC\n"); + return (-1); + } + if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, + bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) + return (-1); + if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, + bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) + return (-1); + + return (0); +} + +static void +bcm_gpio_intr_detach(device_t dev) +{ + struct bcm_gpio_softc *sc; + + sc = device_get_softc(dev); + if (sc->sc_intrhand[0] != NULL) + bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); + if (sc->sc_intrhand[1] != NULL) + bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); + + bcm_gpio_pic_detach(sc); +} + +#else static int bcm_gpio_intr_attach(device_t dev) { @@ -741,6 +816,7 @@ bcm_gpio_intr_detach(device_t dev) } } } +#endif static int bcm_gpio_attach(device_t dev) @@ -786,9 +862,11 @@ bcm_gpio_attach(device_t dev) sc->sc_gpio_pins[i].gp_pin = j; sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); +#ifndef ARM_INTRNG /* The default is active-low interrupts. */ sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; +#endif i++; } sc->sc_gpio_npins = i; @@ -814,6 +892,289 @@ bcm_gpio_detach(device_t dev) return (EBUSY); } +#ifdef ARM_INTRNG +static inline void +bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) +{ + uint32_t bank; + + /* Write 1 to clear. */ + bank = BCM_GPIO_BANK(bgi->bgi_irq); + BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); +} + +static inline bool +bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) +{ + uint32_t bank; + + bank = BCM_GPIO_BANK(bgi->bgi_irq); + return (bgi->bgi_reg == BCM_GPIO_GPHEN(bank) || + bgi->bgi_reg == BCM_GPIO_GPLEN(bank)); +} + +static inline void +bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) +{ + + BCM_GPIO_LOCK(sc); + BCM_GPIO_CLEAR_BITS(sc, bgi->bgi_reg, bgi->bgi_mask); + BCM_GPIO_UNLOCK(bcm_gpio_sc); +} + +static inline void +bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) +{ + + BCM_GPIO_LOCK(sc); + BCM_GPIO_SET_BITS(sc, bgi->bgi_reg, bgi->bgi_mask); + BCM_GPIO_UNLOCK(sc); +} + +static int +bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) +{ + u_int irq; + struct bcm_gpio_irqsrc *bgi; + uint32_t reg; + + /* Do not care of spurious interrupt on GPIO. */ + reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); + while (reg != 0) { + irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; + bgi = sc->sc_isrcs + irq; + if (!bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); + if (intr_isrc_dispatch(&bgi->bgi_isrc, + curthread->td_intr_frame) != 0) { + bcm_gpio_isrc_mask(sc, bgi); + if (bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", + irq); + } + reg &= ~bgi->bgi_mask; + } + return (FILTER_HANDLED); +} + +static int +bcm_gpio_intr_bank0(void *arg) +{ + + return (bcm_gpio_intr_internal(arg, 0)); +} + +static int +bcm_gpio_intr_bank1(void *arg) +{ + + return (bcm_gpio_intr_internal(arg, 1)); +} + +static int +bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + name = device_get_nameunit(sc->sc_dev); + for (irq = 0; irq < BCM_GPIO_PINS; irq++) { + sc->sc_isrcs[irq].bgi_irq = irq; + sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); + sc->sc_isrcs[irq].bgi_reg = 0; + + error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, + sc->sc_dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); /* XXX deregister ISRCs */ + } + return (intr_pic_register(sc->sc_dev, + OF_xref_from_node(ofw_bus_get_node(sc->sc_dev)))); +} + +static int +bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + +static void +bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + bcm_gpio_isrc_mask(sc, bgi); +} + +static void +bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + arm_irq_memory_barrier(bgi->bgi_irq); + bcm_gpio_isrc_unmask(sc, bgi); +} + +static int +bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, u_int ncells, pcell_t *cells, + u_int *irqp, uint32_t *regp) +{ + u_int irq; + uint32_t reg, bank; + + /* + * The first cell is the interrupt number. + * The second cell is used to specify flags: + * bits[3:0] trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + */ + if (ncells != 2) + return (EINVAL); + + irq = cells[0]; + if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) + return (EINVAL); + + /* + * All interrupt types could be set for an interrupt at one moment. + * At least, the combination of 'low-to-high' and 'high-to-low' edge + * triggered interrupt types can make a sense. However, no combo is + * supported now. + */ + bank = BCM_GPIO_BANK(irq); + if (cells[1] == 1) + reg = BCM_GPIO_GPREN(bank); + else if (cells[1] == 2) + reg = BCM_GPIO_GPFEN(bank); + else if (cells[1] == 4) + reg = BCM_GPIO_GPHEN(bank); + else if (cells[1] == 8) + reg = BCM_GPIO_GPLEN(bank); + else + return (EINVAL); + + *irqp = irq; + if (regp != NULL) + *regp = reg; + return (0); +} + +static int +bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + int error; + u_int irq; + struct bcm_gpio_softc *sc; + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + error = bcm_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, + &irq, NULL); + if (error == 0) + *isrcp = &sc->sc_isrcs[irq].bgi_isrc; + return (error); +} + +static void +bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + if (bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); +} + +static void +bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + bcm_gpio_pic_enable_intr(dev, isrc); +} + +static void +bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + bcm_gpio_isrc_mask(sc, bgi); + if (bcm_gpio_isrc_is_level(bgi)) + bcm_gpio_isrc_eoi(sc, bgi); +} + +static int +bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + u_int irq; + uint32_t bank, reg; + struct bcm_gpio_softc *sc; + struct bcm_gpio_irqsrc *bgi; + + if (data == NULL || data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + sc = device_get_softc(dev); + bgi = (struct bcm_gpio_irqsrc *)isrc; + + /* Get and check config for an interrupt. */ + if (bcm_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq, + ®) != 0 || bgi->bgi_irq != irq) + return (EINVAL); + + /* + * If this is a setup for another handler, + * only check that its configuration match. + */ + if (isrc->isrc_handlers != 0) + return (bgi->bgi_reg == reg ? 0 : EINVAL); + + bank = BCM_GPIO_BANK(irq); + BCM_GPIO_LOCK(sc); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); + BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); + bgi->bgi_reg = reg; + BCM_GPIO_SET_BITS(sc, reg, bgi->bgi_mask); + BCM_GPIO_UNLOCK(sc); + return (0); +} + +static int +bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct bcm_gpio_softc *sc = device_get_softc(dev); + struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) { + BCM_GPIO_LOCK(sc); + BCM_GPIO_CLEAR_BITS(sc, bgi->bgi_reg, bgi->bgi_mask); + bgi->bgi_reg = 0; + BCM_GPIO_UNLOCK(sc); + } + return (0); +} + +#else static uint32_t bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank) { @@ -984,6 +1345,7 @@ bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, return (err); } +#endif static phandle_t bcm_gpio_get_node(device_t bus, device_t dev) @@ -1010,13 +1372,24 @@ static device_method_t bcm_gpio_methods[] = { DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), + DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), + DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), +#else /* Bus interface */ DEVMETHOD(bus_activate_resource, bcm_gpio_activate_resource), DEVMETHOD(bus_deactivate_resource, bcm_gpio_deactivate_resource), DEVMETHOD(bus_config_intr, bcm_gpio_config_intr), DEVMETHOD(bus_setup_intr, bcm_gpio_setup_intr), DEVMETHOD(bus_teardown_intr, bcm_gpio_teardown_intr), - +#endif /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), diff --git a/sys/boot/fdt/dts/arm/bcm2835.dtsi b/sys/boot/fdt/dts/arm/bcm2835.dtsi index 0b73401e4a0..a1c955d2173 100644 --- a/sys/boot/fdt/dts/arm/bcm2835.dtsi +++ b/sys/boot/fdt/dts/arm/bcm2835.dtsi @@ -144,7 +144,7 @@ #gpio-cells = <2>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; pinctrl-names = "default"; pinctrl-0 = <&pins_reserved>; diff --git a/sys/boot/fdt/dts/arm/bcm2836.dtsi b/sys/boot/fdt/dts/arm/bcm2836.dtsi index 5229d6a54b5..5cb80af48b8 100644 --- a/sys/boot/fdt/dts/arm/bcm2836.dtsi +++ b/sys/boot/fdt/dts/arm/bcm2836.dtsi @@ -137,7 +137,7 @@ #gpio-cells = <2>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; pinctrl-names = "default"; pinctrl-0 = <&pins_reserved>; From dc57f06939041070a0ace8f9a8c0fead6bac8fde Mon Sep 17 00:00:00 2001 From: Ian Lepore Date: Tue, 5 Apr 2016 13:47:06 +0000 Subject: [PATCH 14/30] Add more DPRINTF() to the ftdi driver. Now everything that can change the chip's state has a DPRINTF, with things that happen repeatedly at debug=2 level and things that happen frequently (like per-transfer IO) at debug=3. --- sys/dev/usb/serial/uftdi.c | 43 +++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c index 136cd5511b7..8b7e620aaa0 100644 --- a/sys/dev/usb/serial/uftdi.c +++ b/sys/dev/usb/serial/uftdi.c @@ -1178,7 +1178,7 @@ uftdi_cfg_open(struct ucom_softc *ucom) * DPRINTF() so that you can see the point at which open gets called * when debugging is enabled. */ - DPRINTF(""); + DPRINTF("\n"); } static void @@ -1190,7 +1190,7 @@ uftdi_cfg_close(struct ucom_softc *ucom) * DPRINTF() so that you can see the point at which close gets called * when debugging is enabled. */ - DPRINTF(""); + DPRINTF("\n"); } static void @@ -1202,6 +1202,8 @@ uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error) uint32_t buflen; uint8_t buf[1]; + DPRINTFN(3, "\n"); + switch (USB_GET_STATE(xfer)) { default: /* Error */ if (error != USB_ERR_CANCELLED) { @@ -1262,6 +1264,8 @@ uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error) int pktmax; int offset; + DPRINTFN(3, "\n"); + usbd_xfer_status(xfer, &buflen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { @@ -1343,6 +1347,8 @@ uftdi_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) uint16_t wValue; struct usb_device_request req; + DPRINTFN(2, "DTR=%u\n", onoff); + wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -1362,6 +1368,8 @@ uftdi_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) uint16_t wValue; struct usb_device_request req; + DPRINTFN(2, "RTS=%u\n", onoff); + wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -1381,6 +1389,8 @@ uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) uint16_t wValue; struct usb_device_request req; + DPRINTFN(2, "BREAK=%u\n", onoff); + if (onoff) { sc->sc_last_lcr |= FTDI_SIO_SET_BREAK; } else { @@ -1618,14 +1628,14 @@ uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t) struct uftdi_param_config cfg; struct usb_device_request req; + DPRINTF("\n"); + if (uftdi_set_parm_soft(ucom, t, &cfg)) { /* should not happen */ return; } sc->sc_last_lcr = cfg.lcr; - DPRINTF("\n"); - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_BAUD_RATE; USETW(req.wValue, cfg.baud_lobits); @@ -1656,8 +1666,7 @@ uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uftdi_softc *sc = ucom->sc_parent; - DPRINTF("msr=0x%02x lsr=0x%02x\n", - sc->sc_msr, sc->sc_lsr); + DPRINTFN(3, "msr=0x%02x lsr=0x%02x\n", sc->sc_msr, sc->sc_lsr); *msr = sc->sc_msr; *lsr = sc->sc_lsr; @@ -1669,6 +1678,8 @@ uftdi_reset(struct ucom_softc *ucom, int reset_type) struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; + DPRINTFN(2, "\n"); + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_RESET; @@ -1686,6 +1697,8 @@ uftdi_set_bitmode(struct ucom_softc *ucom, uint8_t bitmode, uint8_t iomask) usb_device_request_t req; int rv; + DPRINTFN(2, "\n"); + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_BITMODE; @@ -1710,6 +1723,8 @@ uftdi_get_bitmode(struct ucom_softc *ucom, uint8_t *bitmode, uint8_t *iomask) struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; + DPRINTFN(2, "\n"); + req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = FTDI_SIO_GET_BITMODE; @@ -1727,6 +1742,8 @@ uftdi_set_latency(struct ucom_softc *ucom, int latency) struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; + DPRINTFN(2, "\n"); + if (latency < 0 || latency > 255) return (USB_ERR_INVAL); @@ -1748,6 +1765,8 @@ uftdi_get_latency(struct ucom_softc *ucom, int *latency) usb_error_t err; uint8_t buf; + DPRINTFN(2, "\n"); + req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = FTDI_SIO_GET_LATENCY; @@ -1768,6 +1787,8 @@ uftdi_set_event_char(struct ucom_softc *ucom, int echar) usb_device_request_t req; uint8_t enable; + DPRINTFN(2, "\n"); + enable = (echar == -1) ? 0 : 1; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -1787,6 +1808,8 @@ uftdi_set_error_char(struct ucom_softc *ucom, int echar) usb_device_request_t req; uint8_t enable; + DPRINTFN(2, "\n"); + enable = (echar == -1) ? 0 : 1; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -1807,6 +1830,8 @@ uftdi_read_eeprom(struct ucom_softc *ucom, struct uftdi_eeio *eeio) usb_error_t err; uint16_t widx, wlength, woffset; + DPRINTFN(3, "\n"); + /* Offset and length must both be evenly divisible by two. */ if ((eeio->offset | eeio->length) & 0x01) return (EINVAL); @@ -1835,6 +1860,8 @@ uftdi_write_eeprom(struct ucom_softc *ucom, struct uftdi_eeio *eeio) usb_error_t err; uint16_t widx, wlength, woffset; + DPRINTFN(3, "\n"); + /* Offset and length must both be evenly divisible by two. */ if ((eeio->offset | eeio->length) & 0x01) return (EINVAL); @@ -1861,6 +1888,8 @@ uftdi_erase_eeprom(struct ucom_softc *ucom, int confirmation) usb_device_request_t req; usb_error_t err; + DPRINTFN(2, "\n"); + /* Small effort to prevent accidental erasure. */ if (confirmation != UFTDI_CONFIRM_ERASE) return (EINVAL); @@ -1883,8 +1912,6 @@ uftdi_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int err; struct uftdi_bitmode * mode; - DPRINTF("portno: %d cmd: %#x\n", ucom->sc_portno, cmd); - switch (cmd) { case UFTDIIOC_RESET_IO: case UFTDIIOC_RESET_RX: From 3d6bafd11a10be520f9296471f5c5e58444b708e Mon Sep 17 00:00:00 2001 From: Svatopluk Kraus Date: Tue, 5 Apr 2016 13:56:43 +0000 Subject: [PATCH 15/30] Fix typo. No functional change. --- sys/arm/ti/aintc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/arm/ti/aintc.c b/sys/arm/ti/aintc.c index 9a0a313b5a5..8a544d00edb 100644 --- a/sys/arm/ti/aintc.c +++ b/sys/arm/ti/aintc.c @@ -64,7 +64,7 @@ __FBSDID("$FreeBSD$"); #define INTC_ISR_CLEAR(x) (0x94 + ((x) * 0x20)) #define INTC_SIR_SPURIOUS_MASK 0xffffff80 -#define INTS_SIR_ACTIVE_MASK 0x7f +#define INTC_SIR_ACTIVE_MASK 0x7f #define INTC_NIRQS 128 @@ -143,7 +143,7 @@ ti_aintc_intr(void *arg) } /* Only level-sensitive interrupts detection is supported. */ - irq &= INTS_SIR_ACTIVE_MASK; + irq &= INTC_SIR_ACTIVE_MASK; if (intr_isrc_dispatch(&sc->aintc_isrcs[irq].tai_isrc, curthread->td_intr_frame) != 0) { ti_aintc_irq_mask(sc, irq); From e2bb79f70fe429d2b1d774a955ed1608d4abd3d0 Mon Sep 17 00:00:00 2001 From: Oleksandr Tymoshenko Date: Tue, 5 Apr 2016 18:07:13 +0000 Subject: [PATCH 16/30] Remove misleading comment. musb supports host mode for more than two years now Spotted by: jmcneill --- sys/dev/usb/controller/musb_otg.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c index eba8c657129..c7bc8b3fdfd 100644 --- a/sys/dev/usb/controller/musb_otg.c +++ b/sys/dev/usb/controller/musb_otg.c @@ -33,7 +33,6 @@ * This file contains the driver for the Mentor Graphics Inventra USB * 2.0 High Speed Dual-Role controller. * - * NOTE: The current implementation only supports Device Side Mode! */ #ifdef USB_GLOBAL_INCLUDE_FILE From 91e34616df412f7cb67709b6fed3dbbf4402df24 Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Tue, 5 Apr 2016 19:30:19 +0000 Subject: [PATCH 17/30] bhyve: Remove unneeded variable ncq. ncq was not being inititialized properly but it was not actually necessary either, so make the code smaller by removing it. CID: 1248842 Reviewed by: grehan --- usr.sbin/bhyve/pci_ahci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/usr.sbin/bhyve/pci_ahci.c b/usr.sbin/bhyve/pci_ahci.c index d3122cb09aa..2ef60b7c389 100644 --- a/usr.sbin/bhyve/pci_ahci.c +++ b/usr.sbin/bhyve/pci_ahci.c @@ -427,7 +427,6 @@ ahci_port_stop(struct ahci_port *p) struct ahci_ioreq *aior; uint8_t *cfis; int slot; - int ncq; int error; assert(pthread_mutex_isowned_np(&p->pr_sc->mtx)); @@ -445,10 +444,7 @@ ahci_port_stop(struct ahci_port *p) if (cfis[2] == ATA_WRITE_FPDMA_QUEUED || cfis[2] == ATA_READ_FPDMA_QUEUED || cfis[2] == ATA_SEND_FPDMA_QUEUED) - ncq = 1; - - if (ncq) - p->sact &= ~(1 << slot); + p->sact &= ~(1 << slot); /* NCQ */ else p->ci &= ~(1 << slot); From 91da76981b8b424b040d483f433813cd95ac46ff Mon Sep 17 00:00:00 2001 From: Bryan Drewery Date: Tue, 5 Apr 2016 21:12:03 +0000 Subject: [PATCH 18/30] Remove leftover _LDSCRIPTROOT missed in r297270. Sponsored by: EMC / Isilon Storage Division --- Makefile.inc1 | 2 +- Makefile.libcompat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.inc1 b/Makefile.inc1 index 7c027a82860..01566508cac 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -450,7 +450,7 @@ LIBCOMPAT= SOFT WMAKE= ${WMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 DESTDIR=${WORLDTMP} -IMAKEENV= ${CROSSENV:N_LDSCRIPTROOT=*} +IMAKEENV= ${CROSSENV} IMAKE= ${IMAKEENV} ${MAKE} -f Makefile.inc1 \ ${IMAKE_INSTALL} ${IMAKE_MTREE} .if empty(.MAKEFLAGS:M-n) diff --git a/Makefile.libcompat b/Makefile.libcompat index 3ab962f0d84..2c15a9bbd3c 100644 --- a/Makefile.libcompat +++ b/Makefile.libcompat @@ -97,7 +97,7 @@ LIBCOMPATWMAKEFLAGS+= CC="${XCC} ${LIBCOMPATCFLAGS}" \ MK_TESTS=no LIBCOMPATWMAKE+= ${LIBCOMPATWMAKEENV} ${MAKE} ${LIBCOMPATWMAKEFLAGS} \ MK_MAN=no MK_HTML=no -LIBCOMPATIMAKE+= ${LIBCOMPATWMAKE:NINSTALL=*:NDESTDIR=*:N_LDSCRIPTROOT=*} \ +LIBCOMPATIMAKE+= ${LIBCOMPATWMAKE:NINSTALL=*:NDESTDIR=*} \ MK_TOOLCHAIN=no ${IMAKE_INSTALL} \ -DLIBRARIES_ONLY From 1d47c76c8c6798770bcf11752b89d5712b252c7a Mon Sep 17 00:00:00 2001 From: Andriy Voskoboinyk Date: Tue, 5 Apr 2016 21:29:11 +0000 Subject: [PATCH 19/30] net80211: copy MAC address into iv_myaddr[] instead of aliasing it. Since IF_LLADDR() returns a non-constant pointer to the MAC address preserve a copy of it in iv_myaddr. PR: 208505 --- sys/net80211/ieee80211.c | 4 ++-- sys/net80211/ieee80211_var.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index d64667ec300..981689052e4 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -483,7 +483,7 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, vap->iv_htextcaps = ic->ic_htextcaps; vap->iv_opmode = opmode; vap->iv_caps |= ieee80211_opcap[opmode]; - vap->iv_myaddr = ic->ic_macaddr; + IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr); switch (opmode) { case IEEE80211_M_WDS: /* @@ -603,7 +603,7 @@ ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, ifp->if_baudrate = IF_Mbps(maxrate); ether_ifattach(ifp, macaddr); - vap->iv_myaddr = IF_LLADDR(ifp); + IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp)); /* hook output method setup by ether_ifattach */ vap->iv_output = ifp->if_output; ifp->if_output = ieee80211_output; diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 393c7c3bfe9..0066854c14f 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -362,7 +362,8 @@ struct ieee80211vap { TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ struct ieee80211com *iv_ic; /* back ptr to common state */ - const uint8_t *iv_myaddr; /* MAC address: ifp or ic */ + /* MAC address: ifp or ic */ + uint8_t iv_myaddr[IEEE80211_ADDR_LEN]; uint32_t iv_debug; /* debug msg flags */ struct ieee80211_stats iv_stats; /* statistics */ From 1b866afdc6b5d41062d9c0dab7d5b75b89ebfee5 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 5 Apr 2016 21:54:07 +0000 Subject: [PATCH 20/30] [net80211] Add a new capability flag to indicate that the stack should do software A-MSDU encapsulation. Right now there's AMSDU TX/RX capability bits and they're mostly unused, however I'd like to maintain those as the general configuration, not also "please software encap AMSDU." For platforms that can do A-MSDU in firmware (iwn, iwm, etc) then their init paths can read this flag to configure A-MSDU. --- sys/net80211/ieee80211_var.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 0066854c14f..fbaa7828fc4 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -648,6 +648,7 @@ MALLOC_DECLARE(M_80211_VAP); #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ #define IEEE80211_C_SWSLEEP 0x00080000 /* CAPABILITY: do sleep here */ +#define IEEE80211_C_SWAMSDUTX 0x00100000 /* CAPABILITY: software A-MSDU TX */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ From 22e6904e8fbbcfa0228983590544803232518efb Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 5 Apr 2016 21:54:42 +0000 Subject: [PATCH 21/30] [net80211] note that M_FF will soon mean "fast-frames" or "A-MSDU." --- sys/net80211/ieee80211_freebsd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 62f3335e58d..d27b2ca7163 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -263,7 +263,7 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); #define M_EAPOL M_PROTO3 /* PAE/EAPOL frame */ #define M_PWR_SAV M_PROTO4 /* bypass PS handling */ #define M_MORE_DATA M_PROTO5 /* more data frames to follow */ -#define M_FF M_PROTO6 /* fast frame */ +#define M_FF M_PROTO6 /* fast frame / A-MSDU */ #define M_TXCB M_PROTO7 /* do tx complete callback */ #define M_AMPDU_MPDU M_PROTO8 /* ok for A-MPDU aggregation */ #define M_FRAG M_PROTO9 /* frame fragmentation */ From 57f78a351ed1be718fff8accec46f3c7e30b6d46 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 5 Apr 2016 22:01:56 +0000 Subject: [PATCH 22/30] [net80211] rename 11n rate macros into a useful spot * begin moving the 11n macros out of ieee80211_phy.c and into a header so they can be used elsewhere. * rename some of them into the IEEE80211_* namespace. * convert HT_RC_2_MCS() to work with three-stream rates. --- sys/net80211/ieee80211_phy.c | 2 +- sys/net80211/ieee80211_phy.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c index 4242ac0ec5d..16bbb2aec41 100644 --- a/sys/net80211/ieee80211_phy.c +++ b/sys/net80211/ieee80211_phy.c @@ -590,7 +590,7 @@ static const uint16_t ht40_bps[32] = { #define HT_STF 4 #define HT_LTF(n) ((n) * 4) -#define HT_RC_2_MCS(_rc) ((_rc) & 0xf) +#define HT_RC_2_MCS(_rc) ((_rc) & 0x1f) #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) #define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h index cb6b358d2b7..7970388b3e4 100644 --- a/sys/net80211/ieee80211_phy.h +++ b/sys/net80211/ieee80211_phy.h @@ -194,6 +194,14 @@ uint8_t ieee80211_plcp2rate(uint8_t, enum ieee80211_phytype); */ uint8_t ieee80211_rate2plcp(int, enum ieee80211_phytype); +/* + * 802.11n rate manipulation. + */ + +#define IEEE80211_HT_RC_2_MCS(_rc) ((_rc) & 0x1f) +#define IEEE80211_HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define IEEE80211_IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) + uint32_t ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate, int streams, int isht40, int isShortGI); From f7125f180601542f99986001188e32335607f695 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Tue, 5 Apr 2016 22:14:21 +0000 Subject: [PATCH 23/30] [urtwn] first cut of getting the fast-frames / amsdu support in shape. The urtwn hardware transmits FF/A-MSDU just fine - it takes an 802.11 frame and will dutifully send the thing. So: * bump RX queue up from 1. Why's it 1? That's really silly. * Add the "software A-MSDU" encap capability bit. * bump the TX buffer size up so we can at least send A-MSDU frames. * track active frames submitted to the NIC - we can't make assumptions about how many are in flight in the NIC though. For 88E parts we could use per-packet TX indication, but for R92 parts we can't. So, just fake it somewhat. * Kick the transmit queue when we finish reception; try to avoid stalls. * Kick the FF queue a little more regularly. A-MSDU TX won't happen until the net80211 side is done, but atheros fast-frames support should now work. Tested: * urtwn0: MAC/BB RTL8188EU, RF 6052 1T1R ; A-MSDU transmit. --- sys/dev/urtwn/if_urtwn.c | 77 ++++++++++++++++++++++++++++++++++++- sys/dev/urtwn/if_urtwnvar.h | 9 +++-- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/sys/dev/urtwn/if_urtwn.c b/sys/dev/urtwn/if_urtwn.c index 95de7ce096c..ff8e5167bc2 100644 --- a/sys/dev/urtwn/if_urtwn.c +++ b/sys/dev/urtwn/if_urtwn.c @@ -70,6 +70,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif #include #include @@ -577,6 +580,8 @@ urtwn_attach(device_t self) #endif | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_SWAMSDUTX /* Do software A-MSDU TX */ + | IEEE80211_C_FF /* Atheros fast-frames */ ; ic->ic_cryptocaps = @@ -894,6 +899,15 @@ urtwn_report_intr(struct usb_xfer *xfer, struct urtwn_data *data) buf = data->buf; stat = (struct r92c_rx_stat *)buf; + /* + * For 88E chips we can tie the FF flushing here; + * this is where we do know exactly how deep the + * transmit queue is. + * + * But it won't work for R92 chips, so we can't + * take the easy way out. + */ + if (sc->chip & URTWN_CHIP_88E) { int report_sel = MS(le32toh(stat->rxdw3), R88E_RXDW3_RPT); @@ -1101,7 +1115,7 @@ tr_setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); - return; + goto finish; } STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); @@ -1131,7 +1145,6 @@ tr_setup: (void)ieee80211_input_all(ic, m, rssi - nf, nf); } - URTWN_LOCK(sc); m = next; } @@ -1150,6 +1163,20 @@ tr_setup: } break; } +finish: + /* Finished receive; age anything left on the FF queue by a little bump */ + /* + * XXX TODO: just make this a callout timer schedule so we can + * flush the FF staging queue if we're approaching idle. + */ +#ifdef IEEE80211_SUPPORT_SUPERG + URTWN_UNLOCK(sc); + ieee80211_ff_age_all(ic, 1); + URTWN_LOCK(sc); +#endif + + /* Kick-start more transmit in case we stalled */ + urtwn_start(sc); } static void @@ -1161,6 +1188,9 @@ urtwn_txeof(struct urtwn_softc *sc, struct urtwn_data *data, int status) if (data->ni != NULL) /* not a beacon frame */ ieee80211_tx_complete(data->ni, data->m, status); + if (sc->sc_tx_n_active > 0) + sc->sc_tx_n_active--; + data->ni = NULL; data->m = NULL; @@ -1269,6 +1299,9 @@ static void urtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtwn_softc *sc = usbd_xfer_softc(xfer); +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = &sc->sc_ic; +#endif struct urtwn_data *data; URTWN_ASSERT_LOCKED(sc); @@ -1287,12 +1320,14 @@ tr_setup: if (data == NULL) { URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: empty pending queue\n", __func__); + sc->sc_tx_n_active = 0; goto finish; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); + sc->sc_tx_n_active++; break; default: data = STAILQ_FIRST(&sc->sc_tx_active); @@ -1307,6 +1342,35 @@ tr_setup: break; } finish: +#ifdef IEEE80211_SUPPORT_SUPERG + /* + * If the TX active queue drops below a certain + * threshold, ensure we age fast-frames out so they're + * transmitted. + */ + if (sc->sc_tx_n_active <= 1) { + /* XXX ew - net80211 should defer this for us! */ + + /* + * Note: this sc_tx_n_active currently tracks + * the number of pending transmit submissions + * and not the actual depth of the TX frames + * pending to the hardware. That means that + * we're going to end up with some sub-optimal + * aggregation behaviour. + */ + /* + * XXX TODO: just make this a callout timer schedule so we can + * flush the FF staging queue if we're approaching idle. + */ + URTWN_UNLOCK(sc); + ieee80211_ff_flush(ic, WME_AC_VO); + ieee80211_ff_flush(ic, WME_AC_VI); + ieee80211_ff_flush(ic, WME_AC_BE); + ieee80211_ff_flush(ic, WME_AC_BK); + URTWN_LOCK(sc); + } +#endif /* Kick-start more transmit */ urtwn_start(sc); } @@ -3153,6 +3217,11 @@ urtwn_start(struct urtwn_softc *sc) } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; + + URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: called; m=%p\n", + __func__, + m); + if (urtwn_tx_data(sc, ni, m, bf) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); @@ -5326,6 +5395,10 @@ urtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, struct urtwn_data *bf; int error; + URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: called; m=%p\n", + __func__, + m); + /* prevent management frames from being sent if we're not ready */ URTWN_LOCK(sc); if (!(sc->sc_flags & URTWN_RUNNING)) { diff --git a/sys/dev/urtwn/if_urtwnvar.h b/sys/dev/urtwn/if_urtwnvar.h index ba8ca813034..dd6d78b91a9 100644 --- a/sys/dev/urtwn/if_urtwnvar.h +++ b/sys/dev/urtwn/if_urtwnvar.h @@ -17,12 +17,14 @@ * $FreeBSD$ */ -#define URTWN_RX_LIST_COUNT 1 +#define URTWN_RX_LIST_COUNT 64 #define URTWN_TX_LIST_COUNT 8 #define URTWN_HOST_CMD_RING_COUNT 32 -#define URTWN_RXBUFSZ (16 * 1024) -#define URTWN_TXBUFSZ (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN) +#define URTWN_RXBUFSZ (8 * 1024) +//#define URTWN_TXBUFSZ (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN) +/* Leave enough space for an A-MSDU frame */ +#define URTWN_TXBUFSZ (16 * 1024) #define URTWN_RX_DESC_SIZE (sizeof(struct r92c_rx_stat)) #define URTWN_TX_DESC_SIZE (sizeof(struct r92c_tx_desc)) @@ -195,6 +197,7 @@ struct urtwn_softc { urtwn_datahead sc_rx_inactive; struct urtwn_data sc_tx[URTWN_TX_LIST_COUNT]; urtwn_datahead sc_tx_active; + int sc_tx_n_active; urtwn_datahead sc_tx_inactive; urtwn_datahead sc_tx_pending; From b6348be7b96c43bc6c3b7918d946a0382c489024 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Tue, 5 Apr 2016 22:36:48 +0000 Subject: [PATCH 24/30] Add kern.features flags for linux and linux64 modules kern.features.linux: 1 meaning linux 32 bits binaries are supported kern.features.linux64: 1 meaning linux 64 bits binaries are supported The goal here is to help 3rd party applications (including ports) to determine if the host do support linux emulation Reviewed by: dchagin MFC after: 1 week Relnotes: yes Differential Revision: D5830 --- sys/amd64/linux/linux_sysvec.c | 1 + sys/amd64/linux32/linux32_sysvec.c | 1 + sys/i386/linux/linux_sysvec.c | 1 + 3 files changed, 3 insertions(+) diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c index 6089a9097ea..d8f56c0cbed 100644 --- a/sys/amd64/linux/linux_sysvec.c +++ b/sys/amd64/linux/linux_sysvec.c @@ -992,3 +992,4 @@ static moduledata_t linux64_elf_mod = { DECLARE_MODULE_TIED(linux64elf, linux64_elf_mod, SI_SUB_EXEC, SI_ORDER_ANY); MODULE_DEPEND(linux64elf, linux_common, 1, 1, 1); +FEATURE(linux64, "Linux 64bit support"); diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c index 673ea4f5bcd..4c4b5a19140 100644 --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -1205,3 +1205,4 @@ static moduledata_t linux_elf_mod = { DECLARE_MODULE_TIED(linuxelf, linux_elf_mod, SI_SUB_EXEC, SI_ORDER_ANY); MODULE_DEPEND(linuxelf, linux_common, 1, 1, 1); +FEATURE(linux, "Linux 32bit support"); diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index 211ec13cc07..2a4a49e0df3 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -1197,3 +1197,4 @@ static moduledata_t linux_elf_mod = { }; DECLARE_MODULE_TIED(linuxelf, linux_elf_mod, SI_SUB_EXEC, SI_ORDER_ANY); +FEATURE(linux, "Linux 32bit support"); From 8f61276d403fde3c3d8f1a5cbd53130df5842c1e Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Wed, 6 Apr 2016 00:01:03 +0000 Subject: [PATCH 25/30] bhyveload: fix from loading undefined size. We were setting an incorrect/undefined size and as it came out the st struct was not really being used at all. This was actually a bug but by sheer luck it had no visual effect. CID: 1194320 Reviewed by: grehan --- usr.sbin/bhyveload/bhyveload.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/usr.sbin/bhyveload/bhyveload.c b/usr.sbin/bhyveload/bhyveload.c index 8417f2a7efa..9df680f5712 100644 --- a/usr.sbin/bhyveload/bhyveload.c +++ b/usr.sbin/bhyveload/bhyveload.c @@ -152,7 +152,6 @@ struct cb_file { static int cb_open(void *arg, const char *filename, void **hp) { - struct stat st; struct cb_file *cf; char path[PATH_MAX]; @@ -169,7 +168,7 @@ cb_open(void *arg, const char *filename, void **hp) return (errno); } - cf->cf_size = st.st_size; + cf->cf_size = cf->cf_stat.st_size; if (S_ISDIR(cf->cf_stat.st_mode)) { cf->cf_isdir = 1; cf->cf_u.dir = opendir(path); From b60322c9b48c97d867aa6529f9bda7bf8adc3a84 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Wed, 6 Apr 2016 00:41:06 +0000 Subject: [PATCH 26/30] [rsu] We don't do A-MPDU transmit right now, so don't bother registering for it. --- sys/dev/usb/wlan/if_rsu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys/dev/usb/wlan/if_rsu.c b/sys/dev/usb/wlan/if_rsu.c index 5aa460ba82c..6ee1c24de82 100644 --- a/sys/dev/usb/wlan/if_rsu.c +++ b/sys/dev/usb/wlan/if_rsu.c @@ -521,7 +521,9 @@ rsu_attach(device_t self) /* Enable basic HT */ ic->ic_htcaps = IEEE80211_HTC_HT | +#if 0 IEEE80211_HTC_AMPDU | +#endif IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; From c383e5c8ac5dd20b1c688cf3ef4d43a8be5483c9 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Wed, 6 Apr 2016 00:52:30 +0000 Subject: [PATCH 27/30] [urtwn] disable doing AMPDU TX. We don't do AMPDU TX for now, so don't bother setting this. --- sys/dev/urtwn/if_urtwn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys/dev/urtwn/if_urtwn.c b/sys/dev/urtwn/if_urtwn.c index ff8e5167bc2..53ff9657992 100644 --- a/sys/dev/urtwn/if_urtwn.c +++ b/sys/dev/urtwn/if_urtwn.c @@ -593,7 +593,9 @@ urtwn_attach(device_t self) if (urtwn_enable_11n) { device_printf(self, "enabling 11n\n"); ic->ic_htcaps = IEEE80211_HTC_HT | +#if 0 IEEE80211_HTC_AMPDU | +#endif IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; From 678fec509dde47fe561de515df7e600274c38380 Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Wed, 6 Apr 2016 00:55:39 +0000 Subject: [PATCH 28/30] Fix sed functions 'i' and 'a' from discarding leading white space. This appears to be implementation dependent but convenient and makes our sed behave more like GNU sed. Given that it is not the historic behavior, bump FreeBSD_version should userland/ports somehow depend on it. Obtained from: NetBSD (bin/49872) Reviewed by: bdrewery PR: 208554 Merge after: NEVER --- sys/sys/param.h | 2 +- usr.bin/sed/compile.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sys/sys/param.h b/sys/sys/param.h index 987453c418a..2f6eb7af1f7 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1100104 /* Master, propagated to newvers */ +#define __FreeBSD_version 1100105 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/usr.bin/sed/compile.c b/usr.bin/sed/compile.c index 68cbbc54f22..2d7f8d2f6e7 100644 --- a/usr.bin/sed/compile.c +++ b/usr.bin/sed/compile.c @@ -730,7 +730,7 @@ compile_tr(char *p, struct s_tr **py) } /* - * Compile the text following an a or i command. + * Compile the text following an a, c, or i command. */ static char * compile_text(void) @@ -746,7 +746,6 @@ compile_text(void) while (cu_fgets(lbuf, sizeof(lbuf), NULL)) { op = s = text + size; p = lbuf; - EATSPACE(); for (esc_nl = 0; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0' && *++p == '\n') esc_nl = 1; From 1c7b0c84562311856233b4b8cab7760c73a3b771 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Wed, 6 Apr 2016 01:21:51 +0000 Subject: [PATCH 29/30] [net80211] Initial A-MSDU support for testing / evaluation A-MSDU is another 11n aggregation mechanism where multiple ethernet frames get LLC encapsulated (so they have a length field), padded, and put in a single MPDU (802.11 MAC frame.) This means it gets sent out as a single frame, with a single seqno, it's acked as one frame, etc. It turns out that, hah, atheros fast frames is almost but not quite like this, so I'm reusing all of the current superg/fast-frames stuff in order to actually transmit A-MSDU. Yes, this means that A-MSDU frames are also only aggregated two at a time, so it's not necessarily a huge win, but it's better than nothing. This doesn't do anything by default - the driver needs to say it does A-MSDU as well as set the AMSDU software TX capability so this code path gets exercised. For now, the only driver that enables this is urtwn. I'll enable it for rsu at some point soon. Tested: * Add an amsdu encap path to aggregate two frames, same as the fast-frames path. * Always do the superg init/teardown and node init/teardown stuff, regardless of whether the nodes are doing fast-frames (the ATH capability stuff.) That way we can reuse it for amsdu. * Don't do AMSDU for multicast/broadcast and EAPOL frames. * If we're doing A-MPDU, then don't bother doing FF/A-MSDU. We can likely do both together, but I don't want to change behaviour. * Teach the fast frames approx txtime logic to support the 11n rates. But, since we don't currently have a full "current rate" support, assume it's HT20, long-gi, etc. That way we overshoot on the TX time estimation, so we're always inside the requirements. (And we only aggregate two frames for now, so we're not really going to exceed that.) * Drop the maximum FF age default down to 2ms, otherwise we end up with some very annoyingly large latencies. TODO: * We only aggregate two ethernet frames, so I'm not checking the max A-MSDU size. But when it comes time to support >2 frames, we should obey that. Tested: * urtwn(4) --- sys/net80211/ieee80211_ht.h | 4 + sys/net80211/ieee80211_node.c | 4 +- sys/net80211/ieee80211_output.c | 127 +++++++++++------ sys/net80211/ieee80211_sta.c | 6 +- sys/net80211/ieee80211_superg.c | 240 ++++++++++++++++++++++++++------ sys/net80211/ieee80211_superg.h | 23 +++ 6 files changed, 309 insertions(+), 95 deletions(-) diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h index e2b331854df..c0464119aa7 100644 --- a/sys/net80211/ieee80211_ht.h +++ b/sys/net80211/ieee80211_ht.h @@ -65,6 +65,10 @@ struct ieee80211_tx_ampdu { #define IEEE80211_AMPDU_RUNNING(tap) \ (((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0) +/* return non-zero if AMPDU tx for the TID was NACKed */ +#define IEEE80211_AMPDU_NACKED(tap)\ + (!! ((tap)->txa_flags & IEEE80211_AGGR_NAK)) + /* return non-zero if AMPDU tx for the TID is running or started */ #define IEEE80211_AMPDU_REQUESTED(tap) \ (((tap)->txa_flags & \ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 520687b0a4e..66afb740106 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1014,8 +1014,8 @@ node_cleanup(struct ieee80211_node *ni) if (ni->ni_flags & IEEE80211_NODE_HT) ieee80211_ht_node_cleanup(ni); #ifdef IEEE80211_SUPPORT_SUPERG - else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) - ieee80211_ff_node_cleanup(ni); + /* Always do FF node cleanup; for A-MSDU */ + ieee80211_ff_node_cleanup(ni); #endif #ifdef IEEE80211_SUPPORT_MESH /* diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index b2580d1a5c8..1882a263440 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -191,51 +191,68 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, * otherwise unable to establish a BA stream. */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && - (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && - (m->m_flags & M_EAPOL) == 0) { - int tid = WME_AC_TO_TID(M_WME_GETAC(m)); - struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; + (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) { + if ((m->m_flags & M_EAPOL) == 0) { + int tid = WME_AC_TO_TID(M_WME_GETAC(m)); + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; - ieee80211_txampdu_count_packet(tap); - if (IEEE80211_AMPDU_RUNNING(tap)) { - /* - * Operational, mark frame for aggregation. - * - * XXX do tx aggregation here - */ - m->m_flags |= M_AMPDU_MPDU; - } else if (!IEEE80211_AMPDU_REQUESTED(tap) && - ic->ic_ampdu_enable(ni, tap)) { - /* - * Not negotiated yet, request service. - */ - ieee80211_ampdu_request(ni, tap); - /* XXX hold frame for reply? */ + ieee80211_txampdu_count_packet(tap); + if (IEEE80211_AMPDU_RUNNING(tap)) { + /* + * Operational, mark frame for aggregation. + * + * XXX do tx aggregation here + */ + m->m_flags |= M_AMPDU_MPDU; + } else if (!IEEE80211_AMPDU_REQUESTED(tap) && + ic->ic_ampdu_enable(ni, tap)) { + /* + * Not negotiated yet, request service. + */ + ieee80211_ampdu_request(ni, tap); + /* XXX hold frame for reply? */ + } } } - /* - * XXX If we aren't doing AMPDU TX then we /could/ do - * fast-frames encapsulation, however right now this - * output logic doesn't handle that case. - * - * So we'll be limited to "fast-frames" xmit for non-11n STA - * and "no fast frames" xmit for 11n STAs. - * It'd be nice to eventually test fast-frames out by - * gracefully falling from failing A-MPDU transmission - * (driver says no, fail to negotiate it with peer) to - * using fast-frames. - * - * Note: we can actually put A-MSDU's inside an A-MPDU, - * so hopefully we can figure out how to make that particular - * combination work right. - */ #ifdef IEEE80211_SUPPORT_SUPERG - else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { - m = ieee80211_ff_check(ni, m); - if (m == NULL) { - /* NB: any ni ref held on stageq */ - return (0); + /* + * Check for AMSDU/FF; queue for aggregation + * + * Note: we don't bother trying to do fast frames or + * A-MSDU encapsulation for 802.3 drivers. Now, we + * likely could do it for FF (because it's a magic + * atheros tunnel LLC type) but I don't think we're going + * to really need to. For A-MSDU we'd have to set the + * A-MSDU QoS bit in the wifi header, so we just plain + * can't do it. + * + * Strictly speaking, we could actually /do/ A-MSDU / FF + * with A-MPDU together which for certain circumstances + * is beneficial (eg A-MSDU of TCK ACKs.) However, + * I'll ignore that for now so existing behaviour is maintained. + * Later on it would be good to make "amsdu + ampdu" configurable. + */ + else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) { + m = ieee80211_amsdu_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: amsdu_check queued frame\n", + __func__); + return (0); + } + } else if ((! mcast) && IEEE80211_ATH_CAP(vap, ni, + IEEE80211_NODE_FF)) { + m = ieee80211_ff_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: ff_check queued frame\n", + __func__); + return (0); + } } } #endif /* IEEE80211_SUPPORT_SUPERG */ @@ -1229,6 +1246,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, ieee80211_seq seqno; int meshhdrsize, meshae; uint8_t *qos; + int is_amsdu = 0; IEEE80211_TX_LOCK_ASSERT(ic); @@ -1383,9 +1401,19 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, } else { #ifdef IEEE80211_SUPPORT_SUPERG /* - * Aggregated frame. + * Aggregated frame. Check if it's for AMSDU or FF. + * + * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented + * anywhere for some reason. But, since 11n requires + * AMSDU RX, we can just assume "11n" == "AMSDU". */ - m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__); + if (ieee80211_amsdu_tx_ok(ni)) { + m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key); + is_amsdu = 1; + } else { + m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + } if (m == NULL) #endif goto bad; @@ -1521,6 +1549,13 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, qos[1] = 0; wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + /* + * If this is an A-MSDU then ensure we set the + * relevant field. + */ + if (is_amsdu) + qos[0] |= IEEE80211_QOS_AMSDU; + if ((m->m_flags & M_AMPDU_MPDU) == 0) { /* * NB: don't assign a sequence # to potential @@ -1544,6 +1579,14 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); + + /* + * XXX TODO: we shouldn't allow EAPOL, etc that would + * be forced to be non-QoS traffic to be A-MSDU encapsulated. + */ + if (is_amsdu) + printf("%s: XXX ERROR: is_amsdu set; not QoS!\n", + __func__); } diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 16d801cf0fc..9c1707b90a8 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -1703,12 +1703,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, ieee80211_setup_basic_htrates(ni, htinfo); ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); - } else { -#ifdef IEEE80211_SUPPORT_SUPERG - if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH)) - ieee80211_ff_node_init(ni); -#endif } + /* * Configure state now that we are associated. * diff --git a/sys/net80211/ieee80211_superg.c b/sys/net80211/ieee80211_superg.c index 8be098a0fca..1879d6204c2 100644 --- a/sys/net80211/ieee80211_superg.c +++ b/sys/net80211/ieee80211_superg.c @@ -99,18 +99,22 @@ ieee80211_superg_attach(struct ieee80211com *ic) { struct ieee80211_superg *sg; - if (ic->ic_caps & IEEE80211_C_FF) { - sg = (struct ieee80211_superg *) IEEE80211_MALLOC( - sizeof(struct ieee80211_superg), M_80211_VAP, - IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); - if (sg == NULL) { - printf("%s: cannot allocate SuperG state block\n", - __func__); - return; - } - ic->ic_superg = sg; + sg = (struct ieee80211_superg *) IEEE80211_MALLOC( + sizeof(struct ieee80211_superg), M_80211_VAP, + IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (sg == NULL) { + printf("%s: cannot allocate SuperG state block\n", + __func__); + return; } - ieee80211_ffagemax = msecs_to_ticks(150); + ic->ic_superg = sg; + + /* + * Default to not being so aggressive for FF/AMSDU + * aging, otherwise we may hold a frame around + * for way too long before we expire it out. + */ + ieee80211_ffagemax = msecs_to_ticks(2); } void @@ -353,13 +357,145 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, goto bad; } m1->m_nextpkt = NULL; + /* - * Include fast frame headers in adjusting header layout. + * Adjust to include 802.11 header requirement. + */ + KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!")); + ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t)); + m1 = ieee80211_mbuf_adjust(vap, hdrspace, key, m1); + if (m1 == NULL) { + printf("%s: failed initial mbuf_adjust\n", __func__); + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + m_freem(m2); + goto bad; + } + + /* + * Copy second frame's Ethernet header out of line + * and adjust for possible padding in case there isn't room + * at the end of first frame. + */ + KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); + ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t)); + m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2); + if (m2 == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + printf("%s: failed second \n", __func__); + goto bad; + } + + /* + * Now do tunnel encapsulation. First, each + * frame gets a standard encapsulation. + */ + m1 = ieee80211_ff_encap1(vap, m1, &eh1); + if (m1 == NULL) + goto bad; + m2 = ieee80211_ff_encap1(vap, m2, &eh2); + if (m2 == NULL) + goto bad; + + /* + * Pad leading frame to a 4-byte boundary. If there + * is space at the end of the first frame, put it + * there; otherwise prepend to the front of the second + * frame. We know doing the second will always work + * because we reserve space above. We prefer appending + * as this typically has better DMA alignment properties. + */ + for (m = m1; m->m_next != NULL; m = m->m_next) + ; + pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len; + if (pad) { + if (M_TRAILINGSPACE(m) < pad) { /* prepend to second */ + m2->m_data -= pad; + m2->m_len += pad; + m2->m_pkthdr.len += pad; + } else { /* append to first */ + m->m_len += pad; + m1->m_pkthdr.len += pad; + } + } + + /* + * A-MSDU's are just appended; the "I'm A-MSDU!" bit is in the + * QoS header. + * + * XXX optimize by prepending together + */ + m->m_next = m2; /* NB: last mbuf from above */ + m1->m_pkthdr.len += m2->m_pkthdr.len; + M_PREPEND(m1, sizeof(uint32_t)+2, M_NOWAIT); + if (m1 == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: no space for tunnel header\n", __func__); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + memset(mtod(m1, void *), 0, sizeof(uint32_t)+2); + + M_PREPEND(m1, sizeof(struct llc), M_NOWAIT); + if (m1 == NULL) { /* XXX cannot happen */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: no space for llc header\n", __func__); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + llc = mtod(m1, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0; + llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1; + llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2; + llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE); + + vap->iv_stats.is_ff_encap++; + + return m1; +bad: + vap->iv_stats.is_ff_encapfail++; + if (m1 != NULL) + m_freem(m1); + if (m2 != NULL) + m_freem(m2); + return NULL; +} + +/* + * A-MSDU encapsulation. + * + * This assumes just two frames for now, since we're borrowing the + * same queuing code and infrastructure as fast-frames. + * + * There must be two packets chained with m_nextpkt. + * We do header adjustment for each, and then concatenate the mbuf chains + * to form a single frame for transmission. + */ +struct mbuf * +ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, + struct ieee80211_key *key) +{ + struct mbuf *m2; + struct ether_header eh1, eh2; + struct mbuf *m; + int pad; + + m2 = m1->m_nextpkt; + if (m2 == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: only one frame\n", __func__); + goto bad; + } + m1->m_nextpkt = NULL; + + /* + * Include A-MSDU header in adjusting header layout. */ KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!")); ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t)); m1 = ieee80211_mbuf_adjust(vap, - hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 + + hdrspace + sizeof(struct llc) + sizeof(uint32_t) + sizeof(struct ether_header), key, m1); if (m1 == NULL) { @@ -376,9 +512,7 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, */ KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t)); - m2 = ieee80211_mbuf_adjust(vap, - ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header), - NULL, m2); + m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2); if (m2 == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; @@ -418,42 +552,16 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, } /* - * Now, stick 'em together and prepend the tunnel headers; - * first the Atheros tunnel header (all zero for now) and - * then a special fast frame LLC. - * - * XXX optimize by prepending together + * Now, stick 'em together. */ m->m_next = m2; /* NB: last mbuf from above */ m1->m_pkthdr.len += m2->m_pkthdr.len; - M_PREPEND(m1, sizeof(uint32_t)+2, M_NOWAIT); - if (m1 == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, - "%s: no space for tunnel header\n", __func__); - vap->iv_stats.is_tx_nobuf++; - return NULL; - } - memset(mtod(m1, void *), 0, sizeof(uint32_t)+2); - M_PREPEND(m1, sizeof(struct llc), M_NOWAIT); - if (m1 == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, - "%s: no space for llc header\n", __func__); - vap->iv_stats.is_tx_nobuf++; - return NULL; - } - llc = mtod(m1, struct llc *); - llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; - llc->llc_control = LLC_UI; - llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0; - llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1; - llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2; - llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE); - - vap->iv_stats.is_ff_encap++; + vap->iv_stats.is_amsdu_encap++; return m1; bad: + vap->iv_stats.is_amsdu_encapfail++; if (m1 != NULL) m_freem(m1); if (m2 != NULL) @@ -461,6 +569,7 @@ bad: return NULL; } + static void ff_transmit(struct ieee80211_node *ni, struct mbuf *m) { @@ -605,6 +714,7 @@ ff_approx_txtime(struct ieee80211_node *ni, struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; uint32_t framelen; + uint32_t frame_time; /* * Approximate the frame length to be transmitted. A swag to add @@ -621,7 +731,21 @@ ff_approx_txtime(struct ieee80211_node *ni, framelen += 24; if (m2 != NULL) framelen += m2->m_pkthdr.len; - return ieee80211_compute_duration(ic->ic_rt, framelen, ni->ni_txrate, 0); + + /* + * For now, we assume non-shortgi, 20MHz, just because I want to + * at least test 802.11n. + */ + if (ni->ni_txrate & IEEE80211_RATE_MCS) + frame_time = ieee80211_compute_duration_ht(framelen, + ni->ni_txrate, + IEEE80211_HT_RC_2_STREAMS(ni->ni_txrate), + 0, /* isht40 */ + 0); /* isshortgi */ + else + frame_time = ieee80211_compute_duration(ic->ic_rt, framelen, + ni->ni_txrate, 0); + return (frame_time); } /* @@ -753,6 +877,30 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m) return mstaged; } +struct mbuf * +ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m) +{ + /* + * XXX TODO: actually enforce the node support + * and HTCAP requirements for the maximum A-MSDU + * size. + */ + + /* First: software A-MSDU transmit? */ + if (! ieee80211_amsdu_tx_ok(ni)) + return (m); + + /* Next - EAPOL? Nope, don't aggregate; we don't QoS encap them */ + if (m->m_flags & (M_EAPOL | M_MCAST | M_BCAST)) + return (m); + + /* Next - needs to be a data frame, non-broadcast, etc */ + if (ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) + return (m); + + return (ieee80211_ff_check(ni, m)); +} + void ieee80211_ff_node_init(struct ieee80211_node *ni) { diff --git a/sys/net80211/ieee80211_superg.h b/sys/net80211/ieee80211_superg.h index 4df138632fc..ced3c9b6824 100644 --- a/sys/net80211/ieee80211_superg.h +++ b/sys/net80211/ieee80211_superg.h @@ -82,6 +82,27 @@ int ieee80211_parse_athparams(struct ieee80211_node *, uint8_t *, void ieee80211_ff_node_init(struct ieee80211_node *); void ieee80211_ff_node_cleanup(struct ieee80211_node *); +static inline int +ieee80211_amsdu_tx_ok(struct ieee80211_node *ni) +{ + + /* First: software A-MSDU transmit? */ + if ((ni->ni_ic->ic_caps & IEEE80211_C_SWAMSDUTX) == 0) + return (0); + + /* Next: does the VAP have AMSDU TX enabled? */ + if ((ni->ni_vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX) == 0) + return (0); + + /* Next: 11n node? (assumed that A-MSDU TX to HT nodes is ok */ + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) + return (0); + + /* ok, we can at least /do/ AMSDU to this node */ + return (1); +} + +struct mbuf * ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m); struct mbuf *ieee80211_ff_check(struct ieee80211_node *, struct mbuf *); void ieee80211_ff_age(struct ieee80211com *, struct ieee80211_stageq *, int quanta); @@ -122,6 +143,8 @@ ieee80211_ff_age_all(struct ieee80211com *ic, int quanta) struct mbuf *ieee80211_ff_encap(struct ieee80211vap *, struct mbuf *, int, struct ieee80211_key *); +struct mbuf * ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1, + int hdrspace, struct ieee80211_key *key); struct mbuf *ieee80211_ff_decap(struct ieee80211_node *, struct mbuf *); From 0e6cbef2d40dd10e67d88555a29c044978d68d0b Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Wed, 6 Apr 2016 01:22:20 +0000 Subject: [PATCH 30/30] [net80211] missed commit from last one - always cleanup superg state. --- sys/net80211/ieee80211_hostap.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 1e356dc7ec5..4227dfbc734 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -2078,8 +2078,8 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } else if (ni->ni_flags & IEEE80211_NODE_HT) ieee80211_ht_node_cleanup(ni); #ifdef IEEE80211_SUPPORT_SUPERG - else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) - ieee80211_ff_node_cleanup(ni); + /* Always do ff node cleanup; for A-MSDU */ + ieee80211_ff_node_cleanup(ni); #endif /* * Allow AMPDU operation only with unencrypted traffic @@ -2097,6 +2097,10 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, "capinfo 0x%x ucastcipher %d", capinfo, rsnparms.rsn_ucastcipher); ieee80211_ht_node_cleanup(ni); +#ifdef IEEE80211_SUPPORT_SUPERG + /* Always do ff node cleanup; for A-MSDU */ + ieee80211_ff_node_cleanup(ni); +#endif vap->iv_stats.is_ht_assoc_downgrade++; } /*