[RFC PATCH] ARM: OMAP4: ID: Improve features detection and check
Replaces several flags bearing the same meaning. There is no need to set flags due to different omap types here, it can be checked in appropriate places as well. Cc: Tony Lindgren Cc: Russell King Cc: linux-o...@vger.kernel.org Cc: linux-arm-ker...@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/id.c | 25 +++-- arch/arm/mach-omap2/soc.h |8 ++-- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c index cf2362c..3c47a19 100644 --- a/arch/arm/mach-omap2/id.c +++ b/arch/arm/mach-omap2/id.c @@ -28,6 +28,9 @@ #include "soc.h" #include "control.h" +#define OMAP4_SILICON_TYPE_STANDARD0x01 +#define OMAP4_SILICON_TYPE_PERFORMANCE 0x02 + static unsigned int omap_revision; static const char *cpu_rev; u32 omap_features; @@ -273,25 +276,11 @@ void __init omap4xxx_check_features(void) { u32 si_type; - if (cpu_is_omap443x()) - omap_features |= OMAP4_HAS_MPU_1GHZ; - + si_type = + (read_tap_reg(OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1) >> 16) & 0x03; - if (cpu_is_omap446x()) { - si_type = - read_tap_reg(OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1); - switch ((si_type & (3 << 16)) >> 16) { - case 2: - /* High performance device */ - omap_features |= OMAP4_HAS_MPU_1_5GHZ; - break; - case 1: - default: - /* Standard device */ - omap_features |= OMAP4_HAS_MPU_1_2GHZ; - break; - } - } + if (si_type == OMAP4_SILICON_TYPE_PERFORMANCE) + omap_features = OMAP4_HAS_PERF_SILICON; } void __init ti81xx_check_features(void) diff --git a/arch/arm/mach-omap2/soc.h b/arch/arm/mach-omap2/soc.h index 0700964..f31d907 100644 --- a/arch/arm/mach-omap2/soc.h +++ b/arch/arm/mach-omap2/soc.h @@ -435,9 +435,7 @@ extern u32 omap_features; #define OMAP3_HAS_IO_WAKEUPBIT(6) #define OMAP3_HAS_SDRC BIT(7) #define OMAP3_HAS_IO_CHAIN_CTRLBIT(8) -#define OMAP4_HAS_MPU_1GHZ BIT(9) -#define OMAP4_HAS_MPU_1_2GHZ BIT(10) -#define OMAP4_HAS_MPU_1_5GHZ BIT(11) +#define OMAP4_HAS_PERF_SILICON BIT(9) #define OMAP3_HAS_FEATURE(feat,flag) \ @@ -465,9 +463,7 @@ static inline unsigned int omap4_has_ ##feat(void) \ return omap_features & OMAP4_HAS_ ##flag; \ } \ -OMAP4_HAS_FEATURE(mpu_1ghz, MPU_1GHZ) -OMAP4_HAS_FEATURE(mpu_1_2ghz, MPU_1_2GHZ) -OMAP4_HAS_FEATURE(mpu_1_5ghz, MPU_1_5GHZ) +OMAP4_HAS_FEATURE(perf_silicon, PERF_SILICON) #endif /* __ASSEMBLY__ */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: ID: Improve features detection and check
Replaces several flags bearing the same meaning. There is no need to set flags due to different omap types here, it can be checked in appropriate places as well. Cc: Tony Lindgren Cc: Russell King Cc: linux-o...@vger.kernel.org Cc: linux-arm-ker...@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Nishanth Menon Acked-by: Santosh Shilimkar Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/id.c | 25 +++-- arch/arm/mach-omap2/soc.h |8 ++-- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c index cf2362c..3c47a19 100644 --- a/arch/arm/mach-omap2/id.c +++ b/arch/arm/mach-omap2/id.c @@ -28,6 +28,9 @@ #include "soc.h" #include "control.h" +#define OMAP4_SILICON_TYPE_STANDARD0x01 +#define OMAP4_SILICON_TYPE_PERFORMANCE 0x02 + static unsigned int omap_revision; static const char *cpu_rev; u32 omap_features; @@ -273,25 +276,11 @@ void __init omap4xxx_check_features(void) { u32 si_type; - if (cpu_is_omap443x()) - omap_features |= OMAP4_HAS_MPU_1GHZ; - + si_type = + (read_tap_reg(OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1) >> 16) & 0x03; - if (cpu_is_omap446x()) { - si_type = - read_tap_reg(OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1); - switch ((si_type & (3 << 16)) >> 16) { - case 2: - /* High performance device */ - omap_features |= OMAP4_HAS_MPU_1_5GHZ; - break; - case 1: - default: - /* Standard device */ - omap_features |= OMAP4_HAS_MPU_1_2GHZ; - break; - } - } + if (si_type == OMAP4_SILICON_TYPE_PERFORMANCE) + omap_features = OMAP4_HAS_PERF_SILICON; } void __init ti81xx_check_features(void) diff --git a/arch/arm/mach-omap2/soc.h b/arch/arm/mach-omap2/soc.h index 0700964..f31d907 100644 --- a/arch/arm/mach-omap2/soc.h +++ b/arch/arm/mach-omap2/soc.h @@ -435,9 +435,7 @@ extern u32 omap_features; #define OMAP3_HAS_IO_WAKEUPBIT(6) #define OMAP3_HAS_SDRC BIT(7) #define OMAP3_HAS_IO_CHAIN_CTRLBIT(8) -#define OMAP4_HAS_MPU_1GHZ BIT(9) -#define OMAP4_HAS_MPU_1_2GHZ BIT(10) -#define OMAP4_HAS_MPU_1_5GHZ BIT(11) +#define OMAP4_HAS_PERF_SILICON BIT(9) #define OMAP3_HAS_FEATURE(feat,flag) \ @@ -465,9 +463,7 @@ static inline unsigned int omap4_has_ ##feat(void) \ return omap_features & OMAP4_HAS_ ##flag; \ } \ -OMAP4_HAS_FEATURE(mpu_1ghz, MPU_1GHZ) -OMAP4_HAS_FEATURE(mpu_1_2ghz, MPU_1_2GHZ) -OMAP4_HAS_FEATURE(mpu_1_5ghz, MPU_1_5GHZ) +OMAP4_HAS_FEATURE(perf_silicon, PERF_SILICON) #endif /* __ASSEMBLY__ */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: Correct reset source map
In the map for reset sources register we use defines intended for using with PRM_RSTCTRL register. So fix it. Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/prm44xx.c |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index 7498bc7..e335216 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -56,9 +56,9 @@ static struct omap_prcm_irq_setup omap4_prcm_irq_setup = { * enumeration) */ static struct prm_reset_src_map omap44xx_prm_reset_src_map[] = { - { OMAP4430_RST_GLOBAL_WARM_SW_SHIFT, + { OMAP4430_GLOBAL_WARM_SW_RST_SHIFT, OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, - { OMAP4430_RST_GLOBAL_COLD_SW_SHIFT, + { OMAP4430_GLOBAL_COLD_RST_SHIFT, OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, { OMAP4430_MPU_SECURITY_VIOL_RST_SHIFT, OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: Correct wrong instance usage for reading reset sources
To read reset sources registers we have to use PRM_DEVICE_INST Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/prm44xx.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index 7498bc7..0b61b8d 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -333,7 +333,7 @@ static u32 omap44xx_prm_read_reset_sources(void) u32 r = 0; u32 v; - v = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, OMAP4_RM_RSTST); p = omap44xx_prm_reset_src_map; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: fix RSTTIME and RSTST offsets
From: Nishanth Menon RSTTIME is offset 0x8 and RSTST is offset 0x04 for OMAP4430 and OMAP4460. Signed-off-by: Nishanth Menon [ivan.khoronz...@ti.com: ported from k3.4] Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/prm44xx.h |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h index 22b0979..8ee1fbd 100644 --- a/arch/arm/mach-omap2/prm44xx.h +++ b/arch/arm/mach-omap2/prm44xx.h @@ -62,8 +62,8 @@ /* OMAP4 specific register offsets */ #define OMAP4_RM_RSTCTRL 0x -#define OMAP4_RM_RSTTIME 0x0004 -#define OMAP4_RM_RSTST 0x0008 +#define OMAP4_RM_RSTST 0x0004 +#define OMAP4_RM_RSTTIME 0x0008 #define OMAP4_PM_PWSTCTRL 0x #define OMAP4_PM_PWSTST0x0004 -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[RFC PATCH] Input: gpio_keys: Fix suspend/resume press event lost
Rebased on linux_omap/master. During suspend/resume the key press can be lost if time of resume sequence is significant. If press event cannot be remembered then the driver can read the current button state only in time of interrupt handling. But in some cases when time between IRQ and IRQ handler is significant we can read incorrect state. As a particular case, when device is in suspend we press wakupable key and up it back in a jiffy, the interrupt handler read the state of up but the interrupt source is press indeed. As a result, in a OS like android, we resume then suspend right away because the key state is not changed. This patch add to gpio_keys framework opportunity to recover lost of press key event at resuming. The variable "key_pressed" from gpio_button_data structure is not used for gpio keys, it is only used for gpio irq keys, so it is logically used to remember press lost while resuming. Signed-off-by: Ivan Khoronzhuk --- drivers/input/keyboard/gpio_keys.c | 31 ++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index b29ca65..33ac8c5 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -45,6 +45,7 @@ struct gpio_button_data { struct gpio_keys_drvdata { const struct gpio_keys_platform_data *pdata; struct input_dev *input; + int suspended; struct mutex disable_lock; struct gpio_button_data data[0]; }; @@ -326,14 +327,40 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; + struct gpio_keys_drvdata *ddata = input_get_drvdata(input); unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; + /* +* Don't generate input event while resuming, +* it will be generated at gpio_keys_resume function + */ + if (ddata->suspended) { + /* +* missed press event while resuming so set +* key_pressed flag to generate press and up events +* while gpio_keys_resume function. +*/ + if (button->wakeup && state == 0) + bdata->key_pressed = 1; + return; + } + if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); } else { - input_event(input, type, button->code, !!state); + /* +* missed press key, so generate press event then up event +*/ + if (bdata->key_pressed) { + input_event(bdata->input, EV_KEY, button->code, 1); + input_sync(bdata->input); + input_event(bdata->input, EV_KEY, button->code, 0); + bdata->key_pressed = 0; + } else { + input_event(input, type, button->code, !!state); + } } input_sync(input); } @@ -822,6 +849,7 @@ static int gpio_keys_suspend(struct device *dev) mutex_unlock(&input->mutex); } + ddata->suspended = 1; return 0; } @@ -832,6 +860,7 @@ static int gpio_keys_resume(struct device *dev) int error = 0; int i; + ddata->suspended = 0; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 1/2] ARM: OMAP3: PRCM: Fix incorrect read of reset sources
The flag mask are incorrect, so fix it. Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/prcm.c |5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c index 0f51e03..dc45156 100644 --- a/arch/arm/mach-omap2/prcm.c +++ b/arch/arm/mach-omap2/prcm.c @@ -48,9 +48,10 @@ void __iomem *prcm_mpu_base; u32 omap_prcm_get_reset_sources(void) { - /* XXX This presumably needs modification for 34XX */ - if (cpu_is_omap24xx() || cpu_is_omap34xx()) + if (cpu_is_omap24xx()) return omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST) & 0x7f; + if (cpu_is_omap34xx()) + return omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST) & 0x7ff; if (cpu_is_omap44xx()) return omap2_prm_read_mod_reg(WKUP_MOD, OMAP4_RM_RSTST) & 0x7f; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 2/2] ARM: OMAP4: PRCM: Fix incorrect read of reset sources
The address of PRM_RSTST register and flag mask are incorrect, so fix it. Signed-off-by: Ivan Khoronzhuk --- arch/arm/mach-omap2/prcm.c |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c index dc45156..02f27f2 100644 --- a/arch/arm/mach-omap2/prcm.c +++ b/arch/arm/mach-omap2/prcm.c @@ -53,8 +53,8 @@ u32 omap_prcm_get_reset_sources(void) if (cpu_is_omap34xx()) return omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST) & 0x7ff; if (cpu_is_omap44xx()) - return omap2_prm_read_mod_reg(WKUP_MOD, OMAP4_RM_RSTST) & 0x7f; - + return omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, + OMAP4_PRM_RSTST_OFFSET) & 0x7ff; return 0; } EXPORT_SYMBOL(omap_prcm_get_reset_sources); -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: Correct PRM_RSTST and PRM_RSTTIME registers shifts
According to TRMs the assigned shifts are wrong, so correct them. --- arch/arm/mach-omap2/prm44xx.h |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h index 22b0979..8ee1fbd 100644 --- a/arch/arm/mach-omap2/prm44xx.h +++ b/arch/arm/mach-omap2/prm44xx.h @@ -62,8 +62,8 @@ /* OMAP4 specific register offsets */ #define OMAP4_RM_RSTCTRL 0x -#define OMAP4_RM_RSTTIME 0x0004 -#define OMAP4_RM_RSTST 0x0008 +#define OMAP4_RM_RSTST 0x0004 +#define OMAP4_RM_RSTTIME 0x0008 #define OMAP4_PM_PWSTCTRL 0x #define OMAP4_PM_PWSTST0x0004 -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: Correct PRM_RSTST and PRM_RSTTIME registers shifts
According to TRMs the assigned shifts are wrong, so correct them. --- arch/arm/mach-omap2/prm44xx.h |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h index 22b0979..8ee1fbd 100644 --- a/arch/arm/mach-omap2/prm44xx.h +++ b/arch/arm/mach-omap2/prm44xx.h @@ -62,8 +62,8 @@ /* OMAP4 specific register offsets */ #define OMAP4_RM_RSTCTRL 0x -#define OMAP4_RM_RSTTIME 0x0004 -#define OMAP4_RM_RSTST 0x0008 +#define OMAP4_RM_RSTST 0x0004 +#define OMAP4_RM_RSTTIME 0x0008 #define OMAP4_PM_PWSTCTRL 0x #define OMAP4_PM_PWSTST0x0004 -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: Correct wrong instance usage for reading reset sources
To read reset sources registers we have to use PRM_DEVICE_INST --- arch/arm/mach-omap2/prm44xx.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index 7498bc7..0b61b8d 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -333,7 +333,7 @@ static u32 omap44xx_prm_read_reset_sources(void) u32 r = 0; u32 v; - v = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, OMAP4_RM_RSTST); p = omap44xx_prm_reset_src_map; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ARM: OMAP4: PRM: Correct reset source map
In the map for reset sources register we use defines intended for using with PRM_RSTCTRL register. So fix it. --- arch/arm/mach-omap2/prm44xx.c |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index 7498bc7..e335216 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -56,9 +56,9 @@ static struct omap_prcm_irq_setup omap4_prcm_irq_setup = { * enumeration) */ static struct prm_reset_src_map omap44xx_prm_reset_src_map[] = { - { OMAP4430_RST_GLOBAL_WARM_SW_SHIFT, + { OMAP4430_GLOBAL_WARM_SW_RST_SHIFT, OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, - { OMAP4430_RST_GLOBAL_COLD_SW_SHIFT, + { OMAP4430_GLOBAL_COLD_RST_SHIFT, OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, { OMAP4430_MPU_SECURITY_VIOL_RST_SHIFT, OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[RFC PATCH] Input: gpio_keys: Fix suspend/resume press event lost
During suspend/resume the key press can be lost if time of resume sequence is significant. If press event cannot be remembered then the driver can read the current button state only in time of interrupt handling. But in some cases when time between IRQ and IRQ handler is significant we can read incorrect state. As a particular case, when device is in suspend we press wakupable key and up it back in a jiffy, the interrupt handler read the state of up but the interrupt source is press indeed. As a result, in a OS like android, we resume then suspend right away because the key state is not changed. This patch add to gpio_keys framework opportunity to recover lost of press key event at resuming. The variable "key_pressed" from gpio_button_data structure is not used for gpio keys, it is only used for gpio irq keys, so it is logically used to remember press lost while resuming. Signed-off-by: Ivan Khoronzhuk --- drivers/input/keyboard/gpio_keys.c | 31 ++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 62bfce4..aa49aef 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -46,6 +46,7 @@ struct gpio_keys_drvdata { struct input_dev *input; struct mutex disable_lock; unsigned int n_buttons; + int suspended; int (*enable)(struct device *dev); void (*disable)(struct device *dev); struct gpio_button_data data[0]; @@ -328,14 +329,40 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; + struct gpio_keys_drvdata *ddata = input_get_drvdata(input); unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; + /* +* Don't generate input event while resuming, +* it will be generated at gpio_keys_resume function + */ + if (ddata->suspended) { + /* +* missed press event while resuming so set +* key_pressed flag to generate press and up events +* while gpio_keys_resume function. +*/ + if (button->wakeup && state == 0) + bdata->key_pressed = 1; + return; + } + if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); } else { - input_event(input, type, button->code, !!state); + /* +* missed press key, so generate press event then up event +*/ + if (bdata->key_pressed) { + input_event(bdata->input, EV_KEY, button->code, 1); + input_sync(bdata->input); + input_event(bdata->input, EV_KEY, button->code, 0); + bdata->key_pressed = 0; + } else { + input_event(input, type, button->code, !!state); + } } input_sync(input); } @@ -792,6 +819,7 @@ static int gpio_keys_suspend(struct device *dev) } } + ddata->suspended = 1; return 0; } @@ -800,6 +828,7 @@ static int gpio_keys_resume(struct device *dev) struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); int i; + ddata->suspended = 0; for (i = 0; i < ddata->n_buttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->wakeup && device_may_wakeup(dev)) -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 1/3] net: ethernet: ti: cpdma: fix lockup in cpdma_ctlr_destroy()
On 22.07.16 16:58, Grygorii Strashko wrote: Fix deadlock in cpdma_ctlr_destroy() which is triggered now on cpsw module removal: cpsw_remove() - cpdma_ctlr_destroy() - spin_lock_irqsave(&ctlr->lock, flags) - cpdma_ctlr_stop() - spin_lock_irqsave(&ctlr->lock, flags); <- deadlock - cpdma_chan_destroy() - spin_lock_irqsave(&ctlr->lock, flags); <- deadlock The issue has not been observed before because CPDMA channels have been destroyed manually by CPSW until commit d941ebe88a41 ("net: ethernet: ti: cpsw: use destroy ctlr to destroy channels") was merged. Signed-off-by: Grygorii Strashko --- drivers/net/ethernet/ti/davinci_cpdma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index a68652a..89242e9 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -436,7 +436,6 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr) if (!ctlr) return -EINVAL; - spin_lock_irqsave(&ctlr->lock, flags); Should ctlr->state be checked under lock? Seems like here should be used unlocked static versions of cpdma_ctlr_stop() and cpdma_chan_destroy() instead. if (ctlr->state != CPDMA_STATE_IDLE) cpdma_ctlr_stop(ctlr); @@ -444,7 +443,6 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr) cpdma_chan_destroy(ctlr->channels[i]); cpdma_desc_pool_destroy(ctlr->pool); - spin_unlock_irqrestore(&ctlr->lock, flags); return ret; } EXPORT_SYMBOL_GPL(cpdma_ctlr_destroy); -- Regards, Ivan Khoronzhuk
Re: [PATCH] net: ethernet: ti: cpdma: remove used_desc counter
On 04.08.16 18:20, Grygorii Strashko wrote: The struct cpdma_desc_pool->used_desc field can be safely removed from CPDMA driver (and hot patch) because used_descs counter is used just for pool consistency check at CPDMA deinitialization and now this check can be re-implemnted using gen_pool_size(pool->gen_pool) != gen_pool_avail(pool->gen_pool). More over, this will allow to get rid of warnings in cpdma_desc_pool_destro()-> WARN_ON(pool->used_desc) which may happen because the used_descs is used unprotected, since CPDMA has been switched to use genalloc, and may get wrong values on SMP. Hence, remove used_desc from struct cpdma_desc_pool. Signed-off-by: Grygorii Strashko Reviewed-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/davinci_cpdma.c | 18 +++--- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 19e5f32..cf72b33 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -86,7 +86,7 @@ struct cpdma_desc_pool { void __iomem*iomap; /* ioremap map */ void*cpumap;/* dma_alloc map */ int desc_size, mem_size; - int num_desc, used_desc; + int num_desc; struct device *dev; struct gen_pool *gen_pool; }; @@ -148,7 +148,10 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool) if (!pool) return; - WARN_ON(pool->used_desc); + WARN(gen_pool_size(pool->gen_pool) != gen_pool_avail(pool->gen_pool), +"cpdma_desc_pool size %d != avail %d", +gen_pool_size(pool->gen_pool), +gen_pool_avail(pool->gen_pool)); if (pool->cpumap) dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap, pool->phys); @@ -232,21 +235,14 @@ desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma) static struct cpdma_desc __iomem * cpdma_desc_alloc(struct cpdma_desc_pool *pool) { - struct cpdma_desc __iomem *desc = NULL; - - desc = (struct cpdma_desc __iomem *)gen_pool_alloc(pool->gen_pool, - pool->desc_size); - if (desc) - pool->used_desc++; - - return desc; + return (struct cpdma_desc __iomem *) + gen_pool_alloc(pool->gen_pool, pool->desc_size); } static void cpdma_desc_free(struct cpdma_desc_pool *pool, struct cpdma_desc __iomem *desc, int num_desc) { gen_pool_free(pool->gen_pool, (unsigned long)desc, pool->desc_size); - pool->used_desc--; } struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) -- Regards, Ivan Khoronzhuk
[PATCH 1/2] net: core: ethtool: add per queue bandwidth command
Signed-off-by: Ivan Khoronzhuk --- include/linux/ethtool.h | 4 ++ include/uapi/linux/ethtool.h | 2 + net/core/ethtool.c | 102 +++ 3 files changed, 108 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 9ded8c6..7e64c17 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -273,6 +273,8 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, * a TX queue has this number, return -EINVAL. If only a RX queue or a TX * queue has this number, ignore the inapplicable fields. * Returns a negative error code or zero. + * @get_per_queue_bandwidth: get per-queue bandwidth + * @set_per_queue_bandwidth: set per-queue bandwidth * @get_link_ksettings: When defined, takes precedence over the * %get_settings method. Get various device settings * including Ethernet link settings. The %cmd and @@ -368,6 +370,8 @@ struct ethtool_ops { struct ethtool_coalesce *); int (*set_per_queue_coalesce)(struct net_device *, u32, struct ethtool_coalesce *); + int (*get_per_queue_bandwidth)(struct net_device *, u32, int *); + int (*set_per_queue_bandwidth)(struct net_device *, u32, int); int (*get_link_ksettings)(struct net_device *, struct ethtool_link_ksettings *); int (*set_link_ksettings)(struct net_device *, diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index b8f38e8..0fcfe9e 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1314,6 +1314,8 @@ struct ethtool_per_queue_op { #define ETHTOOL_GLINKSETTINGS 0x004c /* Get ethtool_link_settings */ #define ETHTOOL_SLINKSETTINGS 0x004d /* Set ethtool_link_settings */ +#define ETHTOOL_GBANDWIDTH 0x004e /* Get ethtool per queue bandwidth */ +#define ETHTOOL_SBANDWIDTH 0x004f /* Set ethtool per queue bandwidth */ /* compatibility with older code */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9774898..f31d539 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2346,6 +2346,102 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev, return 0; } +static int +ethtool_get_per_queue_bandwidth(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + u32 bit; + int ret; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + + if (!dev->ethtool_ops->get_per_queue_bandwidth) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + int bandwidth; + + ret = dev->ethtool_ops->get_per_queue_bandwidth(dev, bit, + &bandwidth); + if (ret != 0) + return ret; + if (copy_to_user(useraddr, &bandwidth, sizeof(bandwidth))) + return -EFAULT; + useraddr += sizeof(bandwidth); + } + + return 0; +} + +static int +ethtool_set_per_queue_bandwidth(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + u32 bit; + int n_queue; + int i, ret = 0; + int *backup = NULL, *tmp = NULL; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + + if ((!dev->ethtool_ops->set_per_queue_bandwidth) || + (!dev->ethtool_ops->get_per_queue_bandwidth)) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE); + tmp = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + backup = tmp; + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + int bandwidth; + + ret = dev->ethtool_ops->get_per_queue_bandwidth(dev, bit, tmp); + if (ret != 0) + goto roll_back; + + tmp++; + + if (copy_from_user(&bandwidth, useraddr, sizeof(bandwidth))) { + ret = -EFAULT; + goto roll_back; + } + + ret = de
[PATCH] net: ethernet: ti: cpsw: split common driver data and slaves data
Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 761 +++-- 1 file changed, 359 insertions(+), 402 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c51f346..38b04bf 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -141,8 +141,8 @@ do { \ #define CPSW_CMINTMIN_INTVL((1000 / CPSW_CMINTMAX_CNT) + 1) #define cpsw_slave_index(priv) \ - ((priv->data.dual_emac) ? priv->emac_port : \ - priv->data.active_slave) + ((cpsw->data.dual_emac) ? priv->emac_port : \ + cpsw->data.active_slave) static int debug_level; module_param(debug_level, int, 0); @@ -364,29 +364,33 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) } struct cpsw_priv { - struct platform_device *pdev; struct net_device *ndev; - struct napi_struct napi_rx; - struct napi_struct napi_tx; struct device *dev; + u8 mac_addr[ETH_ALEN]; + boolrx_pause; + booltx_pause; + u32 msg_enable; + u32 emac_port; +}; + +struct cpsw_common { + struct platform_device *pdev; struct cpsw_platform_data data; + struct napi_struct napi_rx; + struct napi_struct napi_tx; + struct cpdma_chan *txch, *rxch; + struct cpsw_slave *slaves; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; - u32 msg_enable; - u32 version; - u32 coal_intvl; - u32 bus_freq_mhz; - int rx_packet_max; struct clk *clk; - u8 mac_addr[ETH_ALEN]; - struct cpsw_slave *slaves; struct cpdma_ctlr *dma; - struct cpdma_chan *txch, *rxch; struct cpsw_ale *ale; - boolrx_pause; - booltx_pause; + int rx_packet_max; + u32 bus_freq_mhz; + u32 version; + u32 coal_intvl; boolquirk_irq; boolrx_irq_disabled; booltx_irq_disabled; @@ -394,8 +398,9 @@ struct cpsw_priv { u32 irqs_table[4]; u32 num_irqs; struct cpts *cpts; - u32 emac_port; -}; +} + +static struct cpsw_common *cpsw; struct cpsw_stats { char stat_string[ETH_GSTRING_LEN]; @@ -485,78 +490,79 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { #define CPSW_STATS_LEN ARRAY_SIZE(cpsw_gstrings_stats) -#define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) #define for_each_slave(priv, func, arg...) \ do {\ struct cpsw_slave *slave; \ int n; \ - if (priv->data.dual_emac) \ - (func)((priv)->slaves + priv->emac_port, ##arg);\ + if (cpsw->data.dual_emac) \ + (func)(cpsw->slaves + priv->emac_port, ##arg);\ else\ - for (n = (priv)->data.slaves, \ - slave = (priv)->slaves; \ + for (n = cpsw->data.slaves, \ + slave = cpsw->slaves; \ n; n--) \ (func)(slave++, ##arg); \ } while (0) -#define cpsw_get_slave_ndev(priv, __slave_no__) \ - ((__slave_no__ < priv->data.slaves) ? \ - priv->slaves[__slave_no__].ndev : NULL) -#define cpsw_get_slave_priv(priv, __slave_no__) \ - (((__slave_no__ < priv->data.sla
[PATCH 2/2] net: core: ethtool: add ringparam perqueue command
It useful feature to be able to configure number of buffers for every queue. Signed-off-by: Ivan Khoronzhuk --- include/linux/ethtool.h | 4 ++ net/core/ethtool.c | 104 2 files changed, 108 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 7e64c17..7109736 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -372,6 +372,10 @@ struct ethtool_ops { struct ethtool_coalesce *); int (*get_per_queue_bandwidth)(struct net_device *, u32, int *); int (*set_per_queue_bandwidth)(struct net_device *, u32, int); + int (*get_per_queue_ringparam)(struct net_device *, u32, + struct ethtool_ringparam *); + int (*set_per_queue_ringparam)(struct net_device *, u32, + struct ethtool_ringparam *); int (*get_link_ksettings)(struct net_device *, struct ethtool_link_ksettings *); int (*set_link_ksettings)(struct net_device *, diff --git a/net/core/ethtool.c b/net/core/ethtool.c index f31d539..42a7cb3 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2347,6 +2347,104 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev, } static int +ethtool_get_per_queue_ringparam(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + u32 bit; + int ret; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + + if (!dev->ethtool_ops->get_per_queue_ringparam) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + struct ethtool_ringparam + ringparam = { .cmd = ETHTOOL_GRINGPARAM }; + + ret = dev->ethtool_ops->get_per_queue_ringparam(dev, bit, + &ringparam); + if (ret != 0) + return ret; + if (copy_to_user(useraddr, &ringparam, sizeof(ringparam))) + return -EFAULT; + useraddr += sizeof(ringparam); + } + + return 0; +} + +static int +ethtool_set_per_queue_ringparam(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + struct ethtool_ringparam *backup = NULL, *tmp = NULL; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + int i, ret = 0; + int n_queue; + u32 bit; + + if ((!dev->ethtool_ops->set_per_queue_ringparam) || + (!dev->ethtool_ops->get_per_queue_ringparam)) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE); + tmp = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + backup = tmp; + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + struct ethtool_ringparam + ringparam = { .cmd = ETHTOOL_SRINGPARAM }; + + ret = dev->ethtool_ops->get_per_queue_ringparam(dev, bit, tmp); + if (ret != 0) + goto roll_back; + + tmp++; + + if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) { + ret = -EFAULT; + goto roll_back; + } + + ret = dev->ethtool_ops->set_per_queue_ringparam(dev, bit, + &ringparam); + if (ret != 0) + goto roll_back; + + useraddr += sizeof(ringparam); + } + +roll_back: + if (ret != 0) { + tmp = backup; + for_each_set_bit(i, queue_mask, bit) { + dev->ethtool_ops->set_per_queue_ringparam(dev, i, tmp); + tmp++; + } + } + kfree(backup); + + return ret; +} + +static int ethtool_get_per_queue_bandwidth(struct net_device *dev, void __user *useraddr, struct ethtool_per_queue_op *per_queue_opt) @@ -2509,6 +2607,12 @@ stati
[PATCH 0/2] Add ability to configure ethernet h/w shaper
These two patches can be used to set per queue bandwidth with ethtool. I've create them as logical continuation of patchset from intel, that have introduced per-queue setting command month ago for ethtool interface (http://kernel.opensuse.org/cgit/kernel-source/commit/?h=rpm-4.4.9-36&; id=feaab26abfffe381fb4c8c10d2762a753d481c6c). Actually I've not tested this interface and planning to send it in parallel with "net: ethernet: ti: cpsw: add multi-queue support" (https://lkml.org/lkml/2016/6/30/603), as it contains only changes to ethtool interface. First patch can be used to set per-channel bandwidth, second to tune number of per-channel descriptors. It can solve issues described by Schuyler. In case if per-channel bandwidth is equal to maximum for every channel, the driver could be switched to priority mode. Ivan Khoronzhuk (2): net: core: ethtool: add per queue bandwidth command net: core: ethtool: add ringparam perqueue command include/linux/ethtool.h | 8 ++ include/uapi/linux/ethtool.h | 2 + net/core/ethtool.c | 206 +++ 3 files changed, 216 insertions(+) -- 1.9.1
[PATCH 0/3] net: ethernet: ti: cpsw: split driver data and per ndev data
In dual_emac mode the driver can handle 2 network devices. Each of them can use its own private data and common data/resources. This patchset splits common driver data/resources and private per net device data. Doesn't have bad impact on performance. Based on net-next/master Ivan Khoronzhuk (3): net: ethernet: ti: cpsw: simplify submit routine net: ethernet: ti: cpsw: remove redundant check in napi poll net: ethernet: ti: cpsw: split common driver data and private net data drivers/net/ethernet/ti/cpsw.c | 797 +++-- 1 file changed, 369 insertions(+), 428 deletions(-) -- 1.9.1
[PATCH 3/3] net: ethernet: ti: cpsw: split common driver data and private net data
Simplify driver by splitting common driver data and net dev private data. In case of dual_emac mode 2 networks devices are created, each of them contains its own private data. But 2 net devices share a bunch of h/w resources, that shouldn't be duplicated. This patch leads to the following: - no functional changes - reduce code size - reduce memory usage - reduce number of conversion to priv function - reduce number of arguments for some functions - increase code readability - create prerequisites to add multichannel support, when channels are shared between net devices Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 775 +++-- 1 file changed, 364 insertions(+), 411 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 85ee9f5..7a84515 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -141,8 +141,8 @@ do { \ #define CPSW_CMINTMIN_INTVL((1000 / CPSW_CMINTMAX_CNT) + 1) #define cpsw_slave_index(priv) \ - ((priv->data.dual_emac) ? priv->emac_port : \ - priv->data.active_slave) + ((cpsw->data.dual_emac) ? priv->emac_port : \ + cpsw->data.active_slave) static int debug_level; module_param(debug_level, int, 0); @@ -364,29 +364,34 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) } struct cpsw_priv { - struct platform_device *pdev; struct net_device *ndev; - struct napi_struct napi_rx; - struct napi_struct napi_tx; struct device *dev; + u8 mac_addr[ETH_ALEN]; + boolrx_pause; + booltx_pause; + u32 msg_enable; + u32 emac_port; +}; + +struct cpsw_common { + struct net_device *ndev; /* holds base ndev */ + struct platform_device *pdev; struct cpsw_platform_data data; + struct napi_struct napi_rx; + struct napi_struct napi_tx; + struct cpdma_chan *txch, *rxch; + struct cpsw_slave *slaves; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; - u32 msg_enable; - u32 version; - u32 coal_intvl; - u32 bus_freq_mhz; - int rx_packet_max; struct clk *clk; - u8 mac_addr[ETH_ALEN]; - struct cpsw_slave *slaves; struct cpdma_ctlr *dma; - struct cpdma_chan *txch, *rxch; struct cpsw_ale *ale; - boolrx_pause; - booltx_pause; + int rx_packet_max; + u32 bus_freq_mhz; + u32 version; + u32 coal_intvl; boolquirk_irq; boolrx_irq_disabled; booltx_irq_disabled; @@ -394,9 +399,10 @@ struct cpsw_priv { u32 irqs_table[4]; u32 num_irqs; struct cpts *cpts; - u32 emac_port; }; +static struct cpsw_common *cpsw; + struct cpsw_stats { char stat_string[ETH_GSTRING_LEN]; int type; @@ -485,78 +491,79 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { #define CPSW_STATS_LEN ARRAY_SIZE(cpsw_gstrings_stats) -#define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) #define for_each_slave(priv, func, arg...) \ do {\ struct cpsw_slave *slave; \ int n; \ - if (priv->data.dual_emac) \ - (func)((priv)->slaves + priv->emac_port, ##arg);\ + if (cpsw->data.dual_emac) \ + (func)(cpsw->slaves + priv->emac_port, ##arg);\ else\ - for (n = (priv)->data.slaves, \ -
[PATCH] priority improvement
Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 45 +- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 9ddaccc..cd12f52 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -788,22 +788,16 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) { struct cpsw_priv*priv = napi_to_priv(napi_tx); int num_tx, ch; - u32 ch_map; + unsigned long ch_map; /* process every unprocessed channel */ - ch_map = cpdma_ctrl_txchs_state(priv->dma); - for (ch = 0, num_tx = 0; num_tx < budget; ch_map >>= 1, ch++) { - if (!ch_map) { - ch_map = cpdma_ctrl_txchs_state(priv->dma); - if (!ch_map) - break; - - ch = 0; - } - - if (!(ch_map & 0x01)) - continue; + for (num_tx = 0; num_tx < budget;) { + ch_map = cpdma_ctrl_txchs_state(priv->dma); + if (!ch_map) + break; + /* process beginning from higher priority queue */ + ch = __fls(ch_map); num_tx += cpdma_chan_process(priv->txch[ch], budget - num_tx); } @@ -829,19 +823,13 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) u32 ch_map; /* process every unprocessed channel */ - ch_map = cpdma_ctrl_rxchs_state(priv->dma); - for (ch = 0, num_rx = 0; num_rx < budget; ch_map >>= 1, ch++) { - if (!ch_map) { - ch_map = cpdma_ctrl_rxchs_state(priv->dma); - if (!ch_map) - break; - - ch = 0; - } - - if (!(ch_map & 0x01)) - continue; + for (num_rx = 0; num_rx < budget;) { + ch_map = cpdma_ctrl_rxchs_state(priv->dma); + if (!ch_map) + break; + /* process beginning from higher priority queue */ + ch = __fls(ch_map); num_rx += cpdma_chan_process(priv->rxch[ch], budget - num_rx); } @@ -1130,8 +1118,11 @@ cpsw_tx_queue_mapping(struct cpsw_priv *priv, struct sk_buff *skb) { unsigned int q_idx = skb_get_queue_mapping(skb); - if (q_idx >= priv->tx_ch_num) - q_idx = q_idx % priv->tx_ch_num; + /* cpsw h/w has backward order queue priority, 7 - highest */ + if (likely(q_idx < priv->tx_ch_num)) + q_idx = priv->tx_ch_num - q_idx - 1; + else + q_idx = 0; return priv->txch[q_idx]; } -- 1.9.1
[PATCH 2/3] net: ethernet: ti: cpsw: remove redundant check in napi poll
No need to check number of handled packets, when in most cases (> 99%) it's not 0. It can be 0 only in rarely cases, even in this case it's not bad to print just 0. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 8 ++-- 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 8972bf6..85ee9f5 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -793,9 +793,7 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) } } - if (num_tx) - cpsw_dbg(priv, intr, "poll %d tx pkts\n", num_tx); - + cpsw_dbg(priv, intr, "poll %d tx pkts\n", num_tx); return num_tx; } @@ -814,9 +812,7 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) } } - if (num_rx) - cpsw_dbg(priv, intr, "poll %d rx pkts\n", num_rx); - + cpsw_dbg(priv, intr, "poll %d rx pkts\n", num_rx); return num_rx; } -- 1.9.1
Re: [PATCH 0/2] Add ability to configure ethernet h/w shaper
Please, ignore it It was sent by mistake On 05.08.16 00:11, Ivan Khoronzhuk wrote: These two patches can be used to set per queue bandwidth with ethtool. I've create them as logical continuation of patchset from intel, that have introduced per-queue setting command month ago for ethtool interface (http://kernel.opensuse.org/cgit/kernel-source/commit/?h=rpm-4.4.9-36&; id=feaab26abfffe381fb4c8c10d2762a753d481c6c). Actually I've not tested this interface and planning to send it in parallel with "net: ethernet: ti: cpsw: add multi-queue support" (https://lkml.org/lkml/2016/6/30/603), as it contains only changes to ethtool interface. First patch can be used to set per-channel bandwidth, second to tune number of per-channel descriptors. It can solve issues described by Schuyler. In case if per-channel bandwidth is equal to maximum for every channel, the driver could be switched to priority mode. Ivan Khoronzhuk (2): net: core: ethtool: add per queue bandwidth command net: core: ethtool: add ringparam perqueue command include/linux/ethtool.h | 8 ++ include/uapi/linux/ethtool.h | 2 + net/core/ethtool.c | 206 +++ 3 files changed, 216 insertions(+) -- Regards, Ivan Khoronzhuk
Re: [PATCH] net: ethernet: ti: cpsw: split common driver data and slaves data
Please, ignore it. It was sent by mistake. On 05.08.16 00:11, Ivan Khoronzhuk wrote: Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 761 +++-- 1 file changed, 359 insertions(+), 402 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c51f346..38b04bf 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -141,8 +141,8 @@ do { \ #define CPSW_CMINTMIN_INTVL((1000 / CPSW_CMINTMAX_CNT) + 1) #define cpsw_slave_index(priv) \ - ((priv->data.dual_emac) ? priv->emac_port : \ - priv->data.active_slave) + ((cpsw->data.dual_emac) ? priv->emac_port : \ + cpsw->data.active_slave) static int debug_level; module_param(debug_level, int, 0); @@ -364,29 +364,33 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) } struct cpsw_priv { - struct platform_device *pdev; struct net_device *ndev; - struct napi_struct napi_rx; - struct napi_struct napi_tx; struct device *dev; + u8 mac_addr[ETH_ALEN]; + boolrx_pause; + booltx_pause; + u32 msg_enable; + u32 emac_port; +}; + +struct cpsw_common { + struct platform_device *pdev; struct cpsw_platform_data data; + struct napi_struct napi_rx; + struct napi_struct napi_tx; + struct cpdma_chan *txch, *rxch; + struct cpsw_slave *slaves; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; - u32 msg_enable; - u32 version; - u32 coal_intvl; - u32 bus_freq_mhz; - int rx_packet_max; struct clk *clk; - u8 mac_addr[ETH_ALEN]; - struct cpsw_slave *slaves; struct cpdma_ctlr *dma; - struct cpdma_chan *txch, *rxch; struct cpsw_ale *ale; - boolrx_pause; - booltx_pause; + int rx_packet_max; + u32 bus_freq_mhz; + u32 version; + u32 coal_intvl; boolquirk_irq; boolrx_irq_disabled; booltx_irq_disabled; @@ -394,8 +398,9 @@ struct cpsw_priv { u32 irqs_table[4]; u32 num_irqs; struct cpts *cpts; - u32 emac_port; -}; +} + +static struct cpsw_common *cpsw; struct cpsw_stats { char stat_string[ETH_GSTRING_LEN]; @@ -485,78 +490,79 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { #define CPSW_STATS_LEN ARRAY_SIZE(cpsw_gstrings_stats) -#define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) #define for_each_slave(priv, func, arg...) \ do {\ struct cpsw_slave *slave; \ int n; \ - if (priv->data.dual_emac)\ - (func)((priv)->slaves + priv->emac_port, ##arg);\ + if (cpsw->data.dual_emac)\ + (func)(cpsw->slaves + priv->emac_port, ##arg);\ else\ - for (n = (priv)->data.slaves,\ - slave = (priv)->slaves; \ + for (n = cpsw->data.slaves, \ + slave = cpsw->slaves;\ n; n--) \ (func)(slave++, ##arg); \ } while (0) -#define cpsw_get_slave_ndev(priv, __slave_no__) \ - ((__slave_no__ < priv->data.slaves) ? \ - priv->slaves[__slave_no__].nde
[PATCH 1/3] net: ethernet: ti: cpsw: simplify submit routine
As second net dev is created only in case of dual_emac mode, port number can be figured out in simpler way. Also no need to pass redundant ndev struct. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 18 +- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c51f346..8972bf6 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1065,19 +1065,11 @@ static int cpsw_common_res_usage_state(struct cpsw_priv *priv) return usage_count; } -static inline int cpsw_tx_packet_submit(struct net_device *ndev, - struct cpsw_priv *priv, struct sk_buff *skb) +static inline int cpsw_tx_packet_submit(struct cpsw_priv *priv, + struct sk_buff *skb) { - if (!priv->data.dual_emac) - return cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 0); - - if (ndev == cpsw_get_slave_ndev(priv, 0)) - return cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 1); - else - return cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 2); + return cpdma_chan_submit(priv->txch, skb, skb->data, skb->len, +priv->emac_port + priv->data.dual_emac); } static inline void cpsw_add_dual_emac_def_ale_entries( @@ -1406,7 +1398,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); - ret = cpsw_tx_packet_submit(ndev, priv, skb); + ret = cpsw_tx_packet_submit(priv, skb); if (unlikely(ret != 0)) { cpsw_err(priv, tx_err, "desc submit failed\n"); goto fail; -- 1.9.1
Re: [PATCH 1/2] net: core: ethtool: add per queue bandwidth command
Please, ignore it It was sent by mistake On 05.08.16 00:11, Ivan Khoronzhuk wrote: Signed-off-by: Ivan Khoronzhuk --- include/linux/ethtool.h | 4 ++ include/uapi/linux/ethtool.h | 2 + net/core/ethtool.c | 102 +++ 3 files changed, 108 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 9ded8c6..7e64c17 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -273,6 +273,8 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, * a TX queue has this number, return -EINVAL. If only a RX queue or a TX * queue has this number, ignore the inapplicable fields. * Returns a negative error code or zero. + * @get_per_queue_bandwidth: get per-queue bandwidth + * @set_per_queue_bandwidth: set per-queue bandwidth * @get_link_ksettings: When defined, takes precedence over the * %get_settings method. Get various device settings * including Ethernet link settings. The %cmd and @@ -368,6 +370,8 @@ struct ethtool_ops { struct ethtool_coalesce *); int (*set_per_queue_coalesce)(struct net_device *, u32, struct ethtool_coalesce *); + int (*get_per_queue_bandwidth)(struct net_device *, u32, int *); + int (*set_per_queue_bandwidth)(struct net_device *, u32, int); int (*get_link_ksettings)(struct net_device *, struct ethtool_link_ksettings *); int (*set_link_ksettings)(struct net_device *, diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index b8f38e8..0fcfe9e 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1314,6 +1314,8 @@ struct ethtool_per_queue_op { #define ETHTOOL_GLINKSETTINGS 0x004c /* Get ethtool_link_settings */ #define ETHTOOL_SLINKSETTINGS 0x004d /* Set ethtool_link_settings */ +#define ETHTOOL_GBANDWIDTH 0x004e /* Get ethtool per queue bandwidth */ +#define ETHTOOL_SBANDWIDTH 0x004f /* Set ethtool per queue bandwidth */ /* compatibility with older code */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9774898..f31d539 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2346,6 +2346,102 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev, return 0; } +static int +ethtool_get_per_queue_bandwidth(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + u32 bit; + int ret; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + + if (!dev->ethtool_ops->get_per_queue_bandwidth) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + int bandwidth; + + ret = dev->ethtool_ops->get_per_queue_bandwidth(dev, bit, + &bandwidth); + if (ret != 0) + return ret; + if (copy_to_user(useraddr, &bandwidth, sizeof(bandwidth))) + return -EFAULT; + useraddr += sizeof(bandwidth); + } + + return 0; +} + +static int +ethtool_set_per_queue_bandwidth(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + u32 bit; + int n_queue; + int i, ret = 0; + int *backup = NULL, *tmp = NULL; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + + if ((!dev->ethtool_ops->set_per_queue_bandwidth) || + (!dev->ethtool_ops->get_per_queue_bandwidth)) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE); + tmp = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + backup = tmp; + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + int bandwidth; + + ret = dev->ethtool_ops->get_per_queue_bandwidth(dev, bit, tmp); + if (ret != 0) + goto roll_back; + + tmp++; + + if (copy_from_user(&bandwidth, useraddr, sizeof(bandwidth))) { + ret =
Re: [PATCH] priority improvement
Please, ignore it It was sent by mistake On 05.08.16 00:11, Ivan Khoronzhuk wrote: Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 45 +- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 9ddaccc..cd12f52 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -788,22 +788,16 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) { struct cpsw_priv*priv = napi_to_priv(napi_tx); int num_tx, ch; - u32 ch_map; + unsigned long ch_map; /* process every unprocessed channel */ - ch_map = cpdma_ctrl_txchs_state(priv->dma); - for (ch = 0, num_tx = 0; num_tx < budget; ch_map >>= 1, ch++) { - if (!ch_map) { - ch_map = cpdma_ctrl_txchs_state(priv->dma); - if (!ch_map) - break; - - ch = 0; - } - - if (!(ch_map & 0x01)) - continue; + for (num_tx = 0; num_tx < budget;) { + ch_map = cpdma_ctrl_txchs_state(priv->dma); + if (!ch_map) + break; + /* process beginning from higher priority queue */ + ch = __fls(ch_map); num_tx += cpdma_chan_process(priv->txch[ch], budget - num_tx); } @@ -829,19 +823,13 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) u32 ch_map; /* process every unprocessed channel */ - ch_map = cpdma_ctrl_rxchs_state(priv->dma); - for (ch = 0, num_rx = 0; num_rx < budget; ch_map >>= 1, ch++) { - if (!ch_map) { - ch_map = cpdma_ctrl_rxchs_state(priv->dma); - if (!ch_map) - break; - - ch = 0; - } - - if (!(ch_map & 0x01)) - continue; + for (num_rx = 0; num_rx < budget;) { + ch_map = cpdma_ctrl_rxchs_state(priv->dma); + if (!ch_map) + break; + /* process beginning from higher priority queue */ + ch = __fls(ch_map); num_rx += cpdma_chan_process(priv->rxch[ch], budget - num_rx); } @@ -1130,8 +1118,11 @@ cpsw_tx_queue_mapping(struct cpsw_priv *priv, struct sk_buff *skb) { unsigned int q_idx = skb_get_queue_mapping(skb); - if (q_idx >= priv->tx_ch_num) - q_idx = q_idx % priv->tx_ch_num; + /* cpsw h/w has backward order queue priority, 7 - highest */ + if (likely(q_idx < priv->tx_ch_num)) + q_idx = priv->tx_ch_num - q_idx - 1; + else + q_idx = 0; return priv->txch[q_idx]; } -- Regards, Ivan Khoronzhuk
Re: [PATCH 2/2] net: core: ethtool: add ringparam perqueue command
Please, ignore it It was sent by mistake On 05.08.16 00:11, Ivan Khoronzhuk wrote: It useful feature to be able to configure number of buffers for every queue. Signed-off-by: Ivan Khoronzhuk --- include/linux/ethtool.h | 4 ++ net/core/ethtool.c | 104 2 files changed, 108 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 7e64c17..7109736 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -372,6 +372,10 @@ struct ethtool_ops { struct ethtool_coalesce *); int (*get_per_queue_bandwidth)(struct net_device *, u32, int *); int (*set_per_queue_bandwidth)(struct net_device *, u32, int); + int (*get_per_queue_ringparam)(struct net_device *, u32, + struct ethtool_ringparam *); + int (*set_per_queue_ringparam)(struct net_device *, u32, + struct ethtool_ringparam *); int (*get_link_ksettings)(struct net_device *, struct ethtool_link_ksettings *); int (*set_link_ksettings)(struct net_device *, diff --git a/net/core/ethtool.c b/net/core/ethtool.c index f31d539..42a7cb3 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2347,6 +2347,104 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev, } static int +ethtool_get_per_queue_ringparam(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + u32 bit; + int ret; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + + if (!dev->ethtool_ops->get_per_queue_ringparam) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + struct ethtool_ringparam + ringparam = { .cmd = ETHTOOL_GRINGPARAM }; + + ret = dev->ethtool_ops->get_per_queue_ringparam(dev, bit, + &ringparam); + if (ret != 0) + return ret; + if (copy_to_user(useraddr, &ringparam, sizeof(ringparam))) + return -EFAULT; + useraddr += sizeof(ringparam); + } + + return 0; +} + +static int +ethtool_set_per_queue_ringparam(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) +{ + struct ethtool_ringparam *backup = NULL, *tmp = NULL; + DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE); + int i, ret = 0; + int n_queue; + u32 bit; + + if ((!dev->ethtool_ops->set_per_queue_ringparam) || + (!dev->ethtool_ops->get_per_queue_ringparam)) + return -EOPNOTSUPP; + + useraddr += sizeof(*per_queue_opt); + + bitmap_from_u32array(queue_mask, +MAX_NUM_QUEUE, +per_queue_opt->queue_mask, +DIV_ROUND_UP(MAX_NUM_QUEUE, 32)); + n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE); + tmp = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + backup = tmp; + + for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) { + struct ethtool_ringparam + ringparam = { .cmd = ETHTOOL_SRINGPARAM }; + + ret = dev->ethtool_ops->get_per_queue_ringparam(dev, bit, tmp); + if (ret != 0) + goto roll_back; + + tmp++; + + if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) { + ret = -EFAULT; + goto roll_back; + } + + ret = dev->ethtool_ops->set_per_queue_ringparam(dev, bit, + &ringparam); + if (ret != 0) + goto roll_back; + + useraddr += sizeof(ringparam); + } + +roll_back: + if (ret != 0) { + tmp = backup; + for_each_set_bit(i, queue_mask, bit) { + dev->ethtool_ops->set_per_queue_ringparam(dev, i, tmp); + tmp++; + } + } + kfree(backup); + + return ret; +} + +static int ethtool_get_per_queue_bandwidth(struct net_device *dev, void __user *useraddr,
Re: [PATCH 3/3] net: ethernet: ti: cpsw: split common driver data and private net data
On 05.08.16 15:14, Grygorii Strashko wrote: On 08/05/2016 12:14 AM, Ivan Khoronzhuk wrote: Simplify driver by splitting common driver data and net dev private data. In case of dual_emac mode 2 networks devices are created, each of them contains its own private data. But 2 net devices share a bunch of h/w resources, that shouldn't be duplicated. This patch leads to the following: - no functional changes - reduce code size - reduce memory usage - reduce number of conversion to priv function - reduce number of arguments for some functions - increase code readability - create prerequisites to add multichannel support, when channels are shared between net devices Even if it sounds reasonable, I have to NACK this patch - main reason below, but there are few more: - could you pls split this change as it's too big and I'm pretty sure it's possible; - could you pls avoid unrelated changes like variable reordering in structures Ok. v2 will include it. and thanks a lot for working on this. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 775 +++-- 1 file changed, 364 insertions(+), 411 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 85ee9f5..7a84515 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -141,8 +141,8 @@ do { \ #define CPSW_CMINTMIN_INTVL((1000 / CPSW_CMINTMAX_CNT) + 1) #define cpsw_slave_index(priv) \ - ((priv->data.dual_emac) ? priv->emac_port : \ - priv->data.active_slave) + ((cpsw->data.dual_emac) ? priv->emac_port : \ + cpsw->data.active_slave) static int debug_level; module_param(debug_level, int, 0); @@ -364,29 +364,34 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) } struct cpsw_priv { - struct platform_device *pdev; struct net_device *ndev; - struct napi_struct napi_rx; - struct napi_struct napi_tx; struct device *dev; + u8 mac_addr[ETH_ALEN]; + boolrx_pause; + booltx_pause; + u32 msg_enable; + u32 emac_port; +}; + +struct cpsw_common { + struct net_device *ndev; /* holds base ndev */ + struct platform_device *pdev; struct cpsw_platform_data data; + struct napi_struct napi_rx; + struct napi_struct napi_tx; + struct cpdma_chan *txch, *rxch; + struct cpsw_slave *slaves; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; - u32 msg_enable; - u32 version; - u32 coal_intvl; - u32 bus_freq_mhz; - int rx_packet_max; struct clk *clk; - u8 mac_addr[ETH_ALEN]; - struct cpsw_slave *slaves; struct cpdma_ctlr *dma; - struct cpdma_chan *txch, *rxch; struct cpsw_ale *ale; - boolrx_pause; - booltx_pause; + int rx_packet_max; + u32 bus_freq_mhz; + u32 version; + u32 coal_intvl; boolquirk_irq; boolrx_irq_disabled; booltx_irq_disabled; @@ -394,9 +399,10 @@ struct cpsw_priv { u32 irqs_table[4]; u32 num_irqs; struct cpts *cpts; - u32 emac_port; }; +static struct cpsw_common *cpsw; + Sry, but NACK - no new static variables pls. Ok. -- Regards, Ivan Khoronzhuk
[PATCH v2 07/14] net: ethernet: ti: cpsw: replace pdev on dev
No need to hold pdev link when only dev is needed. This allows to simplify a bunch of cpsw->pdev->dev now and farther. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 65 ++ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index ac875b3..a813bac 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -364,7 +364,7 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) } struct cpsw_common { - struct platform_device *pdev; + struct device *dev; }; struct cpsw_priv { @@ -1159,7 +1159,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) phy_start(slave->phy); /* Configure GMII_SEL register */ - cpsw_phy_sel(&cpsw->pdev->dev, slave->phy->interface, slave->slave_num); + cpsw_phy_sel(cpsw->dev, slave->phy->interface, slave->slave_num); } static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) @@ -1245,9 +1245,9 @@ static int cpsw_ndo_open(struct net_device *ndev) int i, ret; u32 reg; - ret = pm_runtime_get_sync(&cpsw->pdev->dev); + ret = pm_runtime_get_sync(cpsw->dev); if (ret < 0) { - pm_runtime_put_noidle(&cpsw->pdev->dev); + pm_runtime_put_noidle(cpsw->dev); return ret; } @@ -1324,7 +1324,7 @@ static int cpsw_ndo_open(struct net_device *ndev) */ cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); - if (cpts_register(&cpsw->pdev->dev, priv->cpts, + if (cpts_register(cpsw->dev, priv->cpts, priv->data.cpts_clock_mult, priv->data.cpts_clock_shift)) dev_err(priv->dev, "error registering cpts device\n"); @@ -1349,7 +1349,7 @@ static int cpsw_ndo_open(struct net_device *ndev) err_cleanup: cpdma_ctlr_stop(priv->dma); for_each_slave(priv, cpsw_slave_stop, priv); - pm_runtime_put_sync(&cpsw->pdev->dev); + pm_runtime_put_sync(cpsw->dev); netif_carrier_off(priv->ndev); return ret; } @@ -1374,7 +1374,7 @@ static int cpsw_ndo_stop(struct net_device *ndev) cpsw_ale_stop(priv->ale); } for_each_slave(priv, cpsw_slave_stop, priv); - pm_runtime_put_sync(&cpsw->pdev->dev); + pm_runtime_put_sync(cpsw->dev); if (priv->data.dual_emac) priv->slaves[priv->emac_port].open_stat = false; return 0; @@ -1614,9 +1614,9 @@ static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - ret = pm_runtime_get_sync(&cpsw->pdev->dev); + ret = pm_runtime_get_sync(cpsw->dev); if (ret < 0) { - pm_runtime_put_noidle(&cpsw->pdev->dev); + pm_runtime_put_noidle(cpsw->dev); return ret; } @@ -1634,7 +1634,7 @@ static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); for_each_slave(priv, cpsw_set_slave_mac, priv); - pm_runtime_put(&cpsw->pdev->dev); + pm_runtime_put(cpsw->dev); return 0; } @@ -1706,9 +1706,9 @@ static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, if (vid == priv->data.default_vlan) return 0; - ret = pm_runtime_get_sync(&cpsw->pdev->dev); + ret = pm_runtime_get_sync(cpsw->dev); if (ret < 0) { - pm_runtime_put_noidle(&cpsw->pdev->dev); + pm_runtime_put_noidle(cpsw->dev); return ret; } @@ -1728,7 +1728,7 @@ static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid); ret = cpsw_add_vlan_ale_entry(priv, vid); - pm_runtime_put(&cpsw->pdev->dev); + pm_runtime_put(cpsw->dev); return ret; } @@ -1742,9 +1742,9 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, if (vid == priv->data.default_vlan) return 0; - ret = pm_runtime_get_sync(&cpsw->pdev->dev); + ret = pm_runtime_get_sync(cpsw->dev); if (ret < 0) { - pm_runtime_put_noidle(&cpsw->pdev->dev); + pm_runtime_put_noidle(cpsw->dev); return ret; } @@ -1769,7 +1769,7 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device
[PATCH v2 14/14] net: ethernet: ti: cpsw: move ale, cpts and drivers params under cpsw_common
The ale, cpts, version, limit, freq, interrupt pacing parameters are common per net device that uses the same h/w. So, move them to common driver structure. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 237 ++--- 1 file changed, 106 insertions(+), 131 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index e0a1b80..bd0ea71 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -373,13 +373,19 @@ struct cpsw_common { struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; + u32 version; + u32 coal_intvl; + u32 bus_freq_mhz; + int rx_packet_max; struct cpsw_slave *slaves; struct cpdma_ctlr *dma; struct cpdma_chan *txch, *rxch; + struct cpsw_ale *ale; boolquirk_irq; boolrx_irq_disabled; booltx_irq_disabled; u32 irqs_table[IRQ_NUM]; + struct cpts *cpts; int intr_dbg_msg; }; @@ -387,15 +393,9 @@ struct cpsw_priv { struct net_device *ndev; struct device *dev; u32 msg_enable; - u32 version; - u32 coal_intvl; - u32 bus_freq_mhz; - int rx_packet_max; u8 mac_addr[ETH_ALEN]; - struct cpsw_ale *ale; boolrx_pause; booltx_pause; - struct cpts *cpts; u32 emac_port; struct cpsw_common *cpsw; }; @@ -505,22 +505,16 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { } while (0) #define cpsw_get_slave_ndev(cpsw, __slave_no__) \ cpsw->slaves[__slave_no__].ndev -#define cpsw_get_slave_priv(cpsw, __slave_no__) \ - (((__slave_no__ < cpsw->data.slaves) && \ - (cpsw->slaves[__slave_no__].ndev)) ?\ - netdev_priv(cpsw->slaves[__slave_no__].ndev) : NULL)\ -#define cpsw_dual_emac_src_port_detect(cpsw, status, priv, ndev, skb) \ +#define cpsw_dual_emac_src_port_detect(cpsw, status, ndev, skb) \ do {\ if (!cpsw->data.dual_emac) \ break; \ if (CPDMA_RX_SOURCE_PORT(status) == 1) {\ ndev = cpsw_get_slave_ndev(cpsw, 0);\ - priv = netdev_priv(ndev); \ skb->dev = ndev;\ } else if (CPDMA_RX_SOURCE_PORT(status) == 2) { \ ndev = cpsw_get_slave_ndev(cpsw, 1);\ - priv = netdev_priv(ndev); \ skb->dev = ndev;\ } \ } while (0) @@ -531,11 +525,11 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { priv->emac_port;\ int slave_port = cpsw_get_slave_port( \ slave->slave_num); \ - cpsw_ale_add_mcast(priv->ale, addr, \ + cpsw_ale_add_mcast(cpsw->ale, addr, \ 1 << slave_port | ALE_PORT_HOST,\ ALE_VLAN, slave->port_vlan, 0); \ } else {\ - cpsw_ale_add_mcast(priv->ale, addr, \ + cpsw_ale_add_mcast(cpsw->ale, addr, \ ALE_ALL_PORTS, \ 0, 0, 0); \ } \ @@ -548,9 +542,8 @@ static inline int cpsw_get_slave_port(u32 slave_num) static void cpsw_set_promiscious(struct net_device *ndev, bool enable) { - struct cpsw_priv *priv =
[PATCH v2 08/14] net: ethernet: ti: cpsw: move links on h/w registers to cpsw_common
The pointers on h/w registers are common for every cpsw_private instance, so no need to hold them for every ndev. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 97 +++--- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a813bac..6fc22df 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -365,6 +365,10 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) struct cpsw_common { struct device *dev; + struct cpsw_ss_regs __iomem *regs; + struct cpsw_wr_regs __iomem *wr_regs; + u8 __iomem *hw_stats; + struct cpsw_host_regs __iomem *host_port_regs; }; struct cpsw_priv { @@ -373,10 +377,6 @@ struct cpsw_priv { struct napi_struct napi_tx; struct device *dev; struct cpsw_platform_data data; - struct cpsw_ss_regs __iomem *regs; - struct cpsw_wr_regs __iomem *wr_regs; - u8 __iomem *hw_stats; - struct cpsw_host_regs __iomem *host_port_regs; u32 msg_enable; u32 version; u32 coal_intvl; @@ -658,8 +658,10 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) static void cpsw_intr_enable(struct cpsw_priv *priv) { - __raw_writel(0xFF, &priv->wr_regs->tx_en); - __raw_writel(0xFF, &priv->wr_regs->rx_en); + struct cpsw_common *cpsw = priv->cpsw; + + __raw_writel(0xFF, &cpsw->wr_regs->tx_en); + __raw_writel(0xFF, &cpsw->wr_regs->rx_en); cpdma_ctlr_int_ctrl(priv->dma, true); return; @@ -667,8 +669,10 @@ static void cpsw_intr_enable(struct cpsw_priv *priv) static void cpsw_intr_disable(struct cpsw_priv *priv) { - __raw_writel(0, &priv->wr_regs->tx_en); - __raw_writel(0, &priv->wr_regs->rx_en); + struct cpsw_common *cpsw = priv->cpsw; + + __raw_writel(0, &cpsw->wr_regs->tx_en); + __raw_writel(0, &cpsw->wr_regs->rx_en); cpdma_ctlr_int_ctrl(priv->dma, false); return; @@ -752,8 +756,9 @@ requeue: static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) { struct cpsw_priv *priv = dev_id; + struct cpsw_common *cpsw = priv->cpsw; - writel(0, &priv->wr_regs->tx_en); + writel(0, &cpsw->wr_regs->tx_en); cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); if (priv->quirk_irq) { @@ -768,9 +773,10 @@ static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) { struct cpsw_priv *priv = dev_id; + struct cpsw_common *cpsw = priv->cpsw; cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); - writel(0, &priv->wr_regs->rx_en); + writel(0, &cpsw->wr_regs->rx_en); if (priv->quirk_irq) { disable_irq_nosync(priv->irqs_table[0]); @@ -785,11 +791,12 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) { struct cpsw_priv*priv = napi_to_priv(napi_tx); int num_tx; + struct cpsw_common *cpsw = priv->cpsw; num_tx = cpdma_chan_process(priv->txch, budget); if (num_tx < budget) { napi_complete(napi_tx); - writel(0xff, &priv->wr_regs->tx_en); + writel(0xff, &cpsw->wr_regs->tx_en); if (priv->quirk_irq && priv->tx_irq_disabled) { priv->tx_irq_disabled = false; enable_irq(priv->irqs_table[1]); @@ -804,11 +811,12 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) { struct cpsw_priv*priv = napi_to_priv(napi_rx); int num_rx; + struct cpsw_common *cpsw = priv->cpsw; num_rx = cpdma_chan_process(priv->rxch, budget); if (num_rx < budget) { napi_complete(napi_rx); - writel(0xff, &priv->wr_regs->rx_en); + writel(0xff, &cpsw->wr_regs->rx_en); if (priv->quirk_irq && priv->rx_irq_disabled) { priv->rx_irq_disabled = false; enable_irq(priv->irqs_table[0]); @@ -929,10 +937,11 @@ static int cpsw_set_coalesce(struct net_device *ndev, u32 prescale = 0; u32 addnl_dvdr = 1; u32 coal_intvl = 0; + struct cpsw_common *cpsw = priv->cpsw; coal_intvl = coal->rx_coalesce_usecs; - int_ctrl = readl(&priv->wr_regs->int_co
[PATCH v2 11/14] net: ethernet: ti: cpsw: move data platform data and slaves info to cpsw_common
These data are common per net dev. No need to hold it in every priv instance, so move them under cpsw_common. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 271 + 1 file changed, 140 insertions(+), 131 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 4080487..29ff489 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -140,9 +140,9 @@ do { \ #define CPSW_CMINTMAX_INTVL(1000 / CPSW_CMINTMIN_CNT) #define CPSW_CMINTMIN_INTVL((1000 / CPSW_CMINTMAX_CNT) + 1) -#define cpsw_slave_index(priv) \ - ((priv->data.dual_emac) ? priv->emac_port : \ - priv->data.active_slave) +#define cpsw_slave_index(cpsw, priv) \ + ((cpsw->data.dual_emac) ? priv->emac_port : \ + cpsw->data.active_slave) #define IRQ_NUM2 static int debug_level; @@ -366,10 +366,12 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) struct cpsw_common { struct device *dev; + struct cpsw_platform_data data; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; + struct cpsw_slave *slaves; struct cpdma_ctlr *dma; struct cpdma_chan *txch, *rxch; boolquirk_irq; @@ -383,14 +385,12 @@ struct cpsw_priv { struct napi_struct napi_rx; struct napi_struct napi_tx; struct device *dev; - struct cpsw_platform_data data; u32 msg_enable; u32 version; u32 coal_intvl; u32 bus_freq_mhz; int rx_packet_max; u8 mac_addr[ETH_ALEN]; - struct cpsw_slave *slaves; struct cpsw_ale *ale; boolrx_pause; booltx_pause; @@ -492,40 +492,41 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { #define for_each_slave(priv, func, arg...) \ do {\ struct cpsw_slave *slave; \ + struct cpsw_common *cpsw = (priv)->cpsw;\ int n; \ - if (priv->data.dual_emac) \ - (func)((priv)->slaves + priv->emac_port, ##arg);\ + if (cpsw->data.dual_emac) \ + (func)((cpsw)->slaves + priv->emac_port, ##arg);\ else\ - for (n = (priv)->data.slaves, \ - slave = (priv)->slaves; \ + for (n = cpsw->data.slaves, \ + slave = cpsw->slaves; \ n; n--) \ (func)(slave++, ##arg); \ } while (0) -#define cpsw_get_slave_ndev(priv, __slave_no__) \ - priv->slaves[__slave_no__].ndev -#define cpsw_get_slave_priv(priv, __slave_no__) \ - (((__slave_no__ < priv->data.slaves) && \ - (priv->slaves[__slave_no__].ndev)) ?\ - netdev_priv(priv->slaves[__slave_no__].ndev) : NULL)\ - -#define cpsw_dual_emac_src_port_detect(status, priv, ndev, skb) \ +#define cpsw_get_slave_ndev(cpsw, __slave_no__) \ + cpsw->slaves[__slave_no__].ndev +#define cpsw_get_slave_priv(cpsw, __slave_no__) \ + (((__slave_no__ < cpsw->data.slaves) && \ + (cpsw->slaves[__slave_no__].ndev)) ?\ + netdev_priv(cpsw->slaves[__slave_no__].ndev) : NULL)\ + +#define cpsw_dual_emac_src_port_detect(cpsw, status, priv, ndev, skb) \ do {\ - if
[PATCH v2 05/14] net: ethernet: ti: cpsw: don't check slave num in runtime
No need to check const slave num in runtime for every packet, and ndev for slaves w/o ndev is anyway NULL. So remove redundant check. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 70a9570..19aa4bb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -498,8 +498,7 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { (func)(slave++, ##arg); \ } while (0) #define cpsw_get_slave_ndev(priv, __slave_no__) \ - ((__slave_no__ < priv->data.slaves) ? \ - priv->slaves[__slave_no__].ndev : NULL) + priv->slaves[__slave_no__].ndev #define cpsw_get_slave_priv(priv, __slave_no__) \ (((__slave_no__ < priv->data.slaves) && \ (priv->slaves[__slave_no__].ndev)) ?\ -- 1.9.1
[PATCH v2 01/14] net: ethernet: ti: cpsw: simplify submit routine
As second net dev is created only in case of dual_emac mode, port number can be figured out in simpler way. Also no need to pass redundant ndev struct. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 18 +- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c51f346..8972bf6 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1065,19 +1065,11 @@ static int cpsw_common_res_usage_state(struct cpsw_priv *priv) return usage_count; } -static inline int cpsw_tx_packet_submit(struct net_device *ndev, - struct cpsw_priv *priv, struct sk_buff *skb) +static inline int cpsw_tx_packet_submit(struct cpsw_priv *priv, + struct sk_buff *skb) { - if (!priv->data.dual_emac) - return cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 0); - - if (ndev == cpsw_get_slave_ndev(priv, 0)) - return cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 1); - else - return cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 2); + return cpdma_chan_submit(priv->txch, skb, skb->data, skb->len, +priv->emac_port + priv->data.dual_emac); } static inline void cpsw_add_dual_emac_def_ale_entries( @@ -1406,7 +1398,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); - ret = cpsw_tx_packet_submit(ndev, priv, skb); + ret = cpsw_tx_packet_submit(priv, skb); if (unlikely(ret != 0)) { cpsw_err(priv, tx_err, "desc submit failed\n"); goto fail; -- 1.9.1
[PATCH v2 06/14] net: ethernet: ti: cpsw: create common struct to hold shared driver data
This patch simply create holder for common data and as a start moves pdev var to it. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 62 ++ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 19aa4bb..ac875b3 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -363,8 +363,11 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) __raw_writel(val, slave->regs + offset); } -struct cpsw_priv { +struct cpsw_common { struct platform_device *pdev; +}; + +struct cpsw_priv { struct net_device *ndev; struct napi_struct napi_rx; struct napi_struct napi_tx; @@ -394,6 +397,7 @@ struct cpsw_priv { u32 num_irqs; struct cpts *cpts; u32 emac_port; + struct cpsw_common *cpsw; }; struct cpsw_stats { @@ -484,6 +488,7 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { #define CPSW_STATS_LEN ARRAY_SIZE(cpsw_gstrings_stats) +#define ndev_to_cpsw(ndev) (((struct cpsw_priv *)netdev_priv(ndev))->cpsw) #define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) #define for_each_slave(priv, func, arg...) \ do {\ @@ -1095,6 +1100,7 @@ static void soft_reset_slave(struct cpsw_slave *slave) static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) { u32 slave_port; + struct cpsw_common *cpsw = priv->cpsw; soft_reset_slave(slave); @@ -1153,7 +1159,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) phy_start(slave->phy); /* Configure GMII_SEL register */ - cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface, slave->slave_num); + cpsw_phy_sel(&cpsw->pdev->dev, slave->phy->interface, slave->slave_num); } static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) @@ -1235,12 +1241,13 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv) static int cpsw_ndo_open(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; int i, ret; u32 reg; - ret = pm_runtime_get_sync(&priv->pdev->dev); + ret = pm_runtime_get_sync(&cpsw->pdev->dev); if (ret < 0) { - pm_runtime_put_noidle(&priv->pdev->dev); + pm_runtime_put_noidle(&cpsw->pdev->dev); return ret; } @@ -1317,7 +1324,7 @@ static int cpsw_ndo_open(struct net_device *ndev) */ cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); - if (cpts_register(&priv->pdev->dev, priv->cpts, + if (cpts_register(&cpsw->pdev->dev, priv->cpts, priv->data.cpts_clock_mult, priv->data.cpts_clock_shift)) dev_err(priv->dev, "error registering cpts device\n"); @@ -1342,7 +1349,7 @@ static int cpsw_ndo_open(struct net_device *ndev) err_cleanup: cpdma_ctlr_stop(priv->dma); for_each_slave(priv, cpsw_slave_stop, priv); - pm_runtime_put_sync(&priv->pdev->dev); + pm_runtime_put_sync(&cpsw->pdev->dev); netif_carrier_off(priv->ndev); return ret; } @@ -1350,6 +1357,7 @@ err_cleanup: static int cpsw_ndo_stop(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; cpsw_info(priv, ifdown, "shutting down cpsw device\n"); netif_stop_queue(priv->ndev); @@ -1366,7 +1374,7 @@ static int cpsw_ndo_stop(struct net_device *ndev) cpsw_ale_stop(priv->ale); } for_each_slave(priv, cpsw_slave_stop, priv); - pm_runtime_put_sync(&priv->pdev->dev); + pm_runtime_put_sync(&cpsw->pdev->dev); if (priv->data.dual_emac) priv->slaves[priv->emac_port].open_stat = false; return 0; @@ -1598,6 +1606,7 @@ static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) { struct cpsw_priv *priv = netdev_priv(ndev); struct sockaddr *addr = (struct sockaddr *)p; + struct cpsw_common *cpsw = priv->cpsw; int flags = 0; u16 vid = 0; int ret; @@ -1605,9 +1614,9 @@ static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - ret = pm_runtime_get_sync(&
[PATCH v2 13/14] net: ethernet: ti: cpsw: move napi struct to cpsw_common
The napi structs are common for both net devices in dual_emac mode, In order to not hold duplicate links to them, move to cpsw_common. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 50 +++--- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 395531d..e0a1b80 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -367,6 +367,8 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) struct cpsw_common { struct device *dev; struct cpsw_platform_data data; + struct napi_struct napi_rx; + struct napi_struct napi_tx; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; @@ -383,8 +385,6 @@ struct cpsw_common { struct cpsw_priv { struct net_device *ndev; - struct napi_struct napi_rx; - struct napi_struct napi_tx; struct device *dev; u32 msg_enable; u32 version; @@ -489,7 +489,7 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { #define CPSW_STATS_LEN ARRAY_SIZE(cpsw_gstrings_stats) #define ndev_to_cpsw(ndev) (((struct cpsw_priv *)netdev_priv(ndev))->cpsw) -#define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) +#define napi_to_cpsw(napi) container_of(napi, struct cpsw_common, napi) #define for_each_slave(priv, func, arg...) \ do {\ struct cpsw_slave *slave; \ @@ -755,8 +755,7 @@ requeue: static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) { - struct cpsw_priv *priv = dev_id; - struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_common *cpsw = dev_id; writel(0, &cpsw->wr_regs->tx_en); cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_TX); @@ -766,14 +765,13 @@ static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) cpsw->tx_irq_disabled = true; } - napi_schedule(&priv->napi_tx); + napi_schedule(&cpsw->napi_tx); return IRQ_HANDLED; } static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) { - struct cpsw_priv *priv = dev_id; - struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_common *cpsw = dev_id; cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX); writel(0, &cpsw->wr_regs->rx_en); @@ -783,15 +781,14 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) cpsw->rx_irq_disabled = true; } - napi_schedule(&priv->napi_rx); + napi_schedule(&cpsw->napi_rx); return IRQ_HANDLED; } static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) { - struct cpsw_priv*priv = napi_to_priv(napi_tx); + struct cpsw_common *cpsw = napi_to_cpsw(napi_tx); int num_tx; - struct cpsw_common *cpsw = priv->cpsw; num_tx = cpdma_chan_process(cpsw->txch, budget); if (num_tx < budget) { @@ -811,9 +808,8 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) { - struct cpsw_priv*priv = napi_to_priv(napi_rx); + struct cpsw_common *cpsw = napi_to_cpsw(napi_rx); int num_rx; - struct cpsw_common *cpsw = priv->cpsw; num_rx = cpdma_chan_process(cpsw->rxch, budget); if (num_rx < budget) { @@ -1292,7 +1288,6 @@ static int cpsw_ndo_open(struct net_device *ndev) ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0); if (!cpsw_common_res_usage_state(cpsw)) { - struct cpsw_priv *priv_sl0 = cpsw_get_slave_priv(cpsw, 0); int buf_num; /* setup tx dma to fixed prio and zero offset */ @@ -1308,8 +1303,8 @@ static int cpsw_ndo_open(struct net_device *ndev) /* Enable internal fifo flow control */ writel(0x7, &cpsw->regs->flow_control); - napi_enable(&priv_sl0->napi_rx); - napi_enable(&priv_sl0->napi_tx); + napi_enable(&cpsw->napi_rx); + napi_enable(&cpsw->napi_tx); if (cpsw->tx_irq_disabled) { cpsw->tx_irq_disabled = false; @@ -1384,8 +1379,8 @@ static int cpsw_ndo_stop(struct net_device *ndev) if (cpsw_common_res_usage_state(cpsw) <= 1) { struct cpsw_priv *priv_s
[PATCH v2 12/14] net: ethernet: ti: cpsw: fix int dbg message
While poll handlers there is no possibility to figure out which network device is handling packets, as cpdma channels are common for both network devices in dual_emac mode. Currently, the messages are printed only for one device, in fact, there is two. So, better to print integrated num_tx value for both devices if any of them is allowed to. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 36 ++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 29ff489..395531d 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -378,6 +378,7 @@ struct cpsw_common { boolrx_irq_disabled; booltx_irq_disabled; u32 irqs_table[IRQ_NUM]; + int intr_dbg_msg; }; struct cpsw_priv { @@ -802,7 +803,9 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) } } - cpsw_dbg(priv, intr, "poll %d tx pkts\n", num_tx); + if (cpsw->intr_dbg_msg && net_ratelimit()) + dev_dbg(cpsw->dev, "poll %d tx pkts\n", num_tx); + return num_tx; } @@ -822,7 +825,9 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) } } - cpsw_dbg(priv, intr, "poll %d rx pkts\n", num_rx); + if (cpsw->intr_dbg_msg && net_ratelimit()) + dev_dbg(cpsw->dev, "poll %d tx pkts\n", num_rx); + return num_rx; } @@ -1848,8 +1853,35 @@ static u32 cpsw_get_msglevel(struct net_device *ndev) static void cpsw_set_msglevel(struct net_device *ndev, u32 value) { + int i; + struct cpsw_priv *sl_priv; struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + priv->msg_enable = value; + + /* There is no possibility to at napi poll level +* to know which netdev is handled, so enable +* common dbg msg print if any interface is enabled to +*/ + cpsw->intr_dbg_msg = 0; + if (!cpsw->data.dual_emac) { + if (netif_msg_intr(priv)) + cpsw->intr_dbg_msg = 1; + return; + } + + for (i = 0; i < cpsw->data.slaves; i++) { + ndev = netdev_priv(cpsw->slaves[i].ndev); + if (!ndev) + continue; + + sl_priv = netdev_priv(ndev); + if (netif_msg_intr(sl_priv)) { + cpsw->intr_dbg_msg = 1; + break; + } + } } static int cpsw_get_ts_info(struct net_device *ndev, -- 1.9.1
[PATCH v2 10/14] net; ethernet: ti: cpsw: move irq stuff under cpsw_common
The irq data are common per net device. So no need to hold these data per net dev, move it under cpsw_common. Also delete irq_num var, as after optimization it's not needed. Correct number of irqs to 2, as anyway, driver is using only 2, at least for now. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 65 +++--- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index d3af373..4080487 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -143,6 +143,7 @@ do { \ #define cpsw_slave_index(priv) \ ((priv->data.dual_emac) ? priv->emac_port : \ priv->data.active_slave) +#define IRQ_NUM2 static int debug_level; module_param(debug_level, int, 0); @@ -371,6 +372,10 @@ struct cpsw_common { struct cpsw_host_regs __iomem *host_port_regs; struct cpdma_ctlr *dma; struct cpdma_chan *txch, *rxch; + boolquirk_irq; + boolrx_irq_disabled; + booltx_irq_disabled; + u32 irqs_table[IRQ_NUM]; }; struct cpsw_priv { @@ -389,12 +394,6 @@ struct cpsw_priv { struct cpsw_ale *ale; boolrx_pause; booltx_pause; - boolquirk_irq; - boolrx_irq_disabled; - booltx_irq_disabled; - /* snapshot of IRQ numbers */ - u32 irqs_table[4]; - u32 num_irqs; struct cpts *cpts; u32 emac_port; struct cpsw_common *cpsw; @@ -758,9 +757,9 @@ static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) writel(0, &cpsw->wr_regs->tx_en); cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_TX); - if (priv->quirk_irq) { - disable_irq_nosync(priv->irqs_table[1]); - priv->tx_irq_disabled = true; + if (cpsw->quirk_irq) { + disable_irq_nosync(cpsw->irqs_table[1]); + cpsw->tx_irq_disabled = true; } napi_schedule(&priv->napi_tx); @@ -775,9 +774,9 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX); writel(0, &cpsw->wr_regs->rx_en); - if (priv->quirk_irq) { - disable_irq_nosync(priv->irqs_table[0]); - priv->rx_irq_disabled = true; + if (cpsw->quirk_irq) { + disable_irq_nosync(cpsw->irqs_table[0]); + cpsw->rx_irq_disabled = true; } napi_schedule(&priv->napi_rx); @@ -794,9 +793,9 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) if (num_tx < budget) { napi_complete(napi_tx); writel(0xff, &cpsw->wr_regs->tx_en); - if (priv->quirk_irq && priv->tx_irq_disabled) { - priv->tx_irq_disabled = false; - enable_irq(priv->irqs_table[1]); + if (cpsw->quirk_irq && cpsw->tx_irq_disabled) { + cpsw->tx_irq_disabled = false; + enable_irq(cpsw->irqs_table[1]); } } @@ -814,9 +813,9 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) if (num_rx < budget) { napi_complete(napi_rx); writel(0xff, &cpsw->wr_regs->rx_en); - if (priv->quirk_irq && priv->rx_irq_disabled) { - priv->rx_irq_disabled = false; - enable_irq(priv->irqs_table[0]); + if (cpsw->quirk_irq && cpsw->rx_irq_disabled) { + cpsw->rx_irq_disabled = false; + enable_irq(cpsw->irqs_table[0]); } } @@ -1303,14 +1302,14 @@ static int cpsw_ndo_open(struct net_device *ndev) napi_enable(&priv_sl0->napi_rx); napi_enable(&priv_sl0->napi_tx); - if (priv_sl0->tx_irq_disabled) { - priv_sl0->tx_irq_disabled = false; - enable_irq(priv->irqs_table[1]); + if (cpsw->tx_irq_disabled) { + cpsw->tx_irq_disabled = false; + enable_irq(cpsw->irqs_table[1]); } - if (priv_sl0->rx_irq_disabled) { - priv_sl0->rx_irq_disabled = false; - enable_irq(priv->irqs_table[0
[PATCH v2 04/14] net: ethernet: ti: cpsw: remove clk var from priv
There is no need to hold link to clk, it's used only once while probe. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 10 -- 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 30e1ddb..70a9570 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -379,7 +379,6 @@ struct cpsw_priv { u32 coal_intvl; u32 bus_freq_mhz; int rx_packet_max; - struct clk *clk; u8 mac_addr[ETH_ALEN]; struct cpsw_slave *slaves; struct cpdma_ctlr *dma; @@ -2179,8 +2178,6 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN); priv_sl2->slaves = priv->slaves; - priv_sl2->clk = priv->clk; - priv_sl2->coal_intvl = 0; priv_sl2->bus_freq_mhz = priv->bus_freq_mhz; @@ -2258,6 +2255,7 @@ MODULE_DEVICE_TABLE(of, cpsw_of_mtable); static int cpsw_probe(struct platform_device *pdev) { + struct clk *clk; struct cpsw_platform_data *data; struct net_device *ndev; struct cpsw_priv*priv; @@ -2336,14 +2334,14 @@ static int cpsw_probe(struct platform_device *pdev) priv->slaves[0].ndev = ndev; priv->emac_port = 0; - priv->clk = devm_clk_get(&pdev->dev, "fck"); - if (IS_ERR(priv->clk)) { + clk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { dev_err(priv->dev, "fck is not found\n"); ret = -ENODEV; goto clean_runtime_disable_ret; } priv->coal_intvl = 0; - priv->bus_freq_mhz = clk_get_rate(priv->clk) / 100; + priv->bus_freq_mhz = clk_get_rate(clk) / 100; ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ss_regs = devm_ioremap_resource(&pdev->dev, ss_res); -- 1.9.1
[PATCH v2 03/14] net: ethernet: ti: cpsw: remove priv from cpsw_get_slave_port() parameters list
There is no need in priv here. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 10 +- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 85ee9f5..30e1ddb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -525,7 +525,7 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { if (priv->data.dual_emac) { \ struct cpsw_slave *slave = priv->slaves + \ priv->emac_port;\ - int slave_port = cpsw_get_slave_port(priv, \ + int slave_port = cpsw_get_slave_port( \ slave->slave_num); \ cpsw_ale_add_mcast(priv->ale, addr, \ 1 << slave_port | ALE_PORT_HOST,\ @@ -537,7 +537,7 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = { } \ } while (0) -static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) +static inline int cpsw_get_slave_port(u32 slave_num) { return slave_num + 1; } @@ -849,7 +849,7 @@ static void _cpsw_adjust_link(struct cpsw_slave *slave, if (!phy) return; - slave_port = cpsw_get_slave_port(priv, slave->slave_num); + slave_port = cpsw_get_slave_port(slave->slave_num); if (phy->link) { mac_control = priv->data.mac_control; @@ -1120,7 +1120,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) slave->mac_control = 0; /* no link yet */ - slave_port = cpsw_get_slave_port(priv, slave->slave_num); + slave_port = cpsw_get_slave_port(slave->slave_num); if (priv->data.dual_emac) cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port); @@ -1222,7 +1222,7 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv) { u32 slave_port; - slave_port = cpsw_get_slave_port(priv, slave->slave_num); + slave_port = cpsw_get_slave_port(slave->slave_num); if (!slave->phy) return; -- 1.9.1
[PATCH v2 02/14] net: ethernet: ti: cpsw: remove redundant check in napi poll
No need to check number of handled packets, when in most cases (> 99%) it's not 0. It can be 0 only in rare cases, even in this case it's not bad to print just 0. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 8 ++-- 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 8972bf6..85ee9f5 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -793,9 +793,7 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) } } - if (num_tx) - cpsw_dbg(priv, intr, "poll %d tx pkts\n", num_tx); - + cpsw_dbg(priv, intr, "poll %d tx pkts\n", num_tx); return num_tx; } @@ -814,9 +812,7 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) } } - if (num_rx) - cpsw_dbg(priv, intr, "poll %d rx pkts\n", num_rx); - + cpsw_dbg(priv, intr, "poll %d rx pkts\n", num_rx); return num_rx; } -- 1.9.1
[PATCH v2 00/14] net: ethernet: ti: cpsw: split driver data and per ndev data
In dual_emac mode the driver can handle 2 network devices. Each of them can use its own private data and common data/resources. This patchset splits common driver data/resources and private per net device data. It leads to: - reduce memory usage - increase code readability - allows add a bunch of simplification - create prerequisites to add multi-channel support, when channels are shared between net devices Doesn't have bad impact on performance. v1: https://lkml.org/lkml/2016/8/4/616 Since v1: - added several patch improvements - avoided variable reordering in structures - removed static variable for common function - split big patch on several patches: net: ethernet: ti: cpsw: remove priv from cpsw_get_slave_port() parameters list net: ethernet: ti: cpsw: remove clk var from priv net: ethernet: ti: cpsw: don't check slave num in runtime net: ethernet: ti: cpsw: create common struct to hold shared driver data net: ethernet: ti: cpsw: replace pdev on dev net: ethernet: ti: cpsw: move links on h/w registers to cpsw_common net: ethernet: ti: cpsw: move cpdma resources to cpsw_common net; ethernet: ti: cpsw: move irq stuff under cpsw_common net: ethernet: ti: cpsw: move data platform data and slaves info to cpsw_common net: ethernet: ti: cpsw: fix int dbg message net: ethernet: ti: cpsw: move napi struct to cpsw_common net: ethernet: ti: cpsw: move ale, cpts and drivers params under Based on net-next/master Ivan Khoronzhuk (14): net: ethernet: ti: cpsw: simplify submit routine net: ethernet: ti: cpsw: remove redundant check in napi poll net: ethernet: ti: cpsw: remove priv from cpsw_get_slave_port() parameters list net: ethernet: ti: cpsw: remove clk var from priv net: ethernet: ti: cpsw: don't check slave num in runtime net: ethernet: ti: cpsw: create common struct to hold shared driver data net: ethernet: ti: cpsw: replace pdev on dev net: ethernet: ti: cpsw: move links on h/w registers to cpsw_common net: ethernet: ti: cpsw: move cpdma resources to cpsw_common net; ethernet: ti: cpsw: move irq stuff under cpsw_common net: ethernet: ti: cpsw: move data platform data and slaves info to cpsw_common net: ethernet: ti: cpsw: fix int dbg message net: ethernet: ti: cpsw: move napi struct to cpsw_common net: ethernet: ti: cpsw: move ale, cpts and drivers params under cpsw_common drivers/net/ethernet/ti/cpsw.c | 879 + 1 file changed, 447 insertions(+), 432 deletions(-) -- 1.9.1
[PATCH v2 09/14] net: ethernet: ti: cpsw: move cpdma resources to cpsw_common
Every net device private struct holds links to shared cpdma resources. No need to save and every time synchronize these resources per net dev. So, move it to common driver struct. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 97 +- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 6fc22df..d3af373 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -369,6 +369,8 @@ struct cpsw_common { struct cpsw_wr_regs __iomem *wr_regs; u8 __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; + struct cpdma_ctlr *dma; + struct cpdma_chan *txch, *rxch; }; struct cpsw_priv { @@ -384,8 +386,6 @@ struct cpsw_priv { int rx_packet_max; u8 mac_addr[ETH_ALEN]; struct cpsw_slave *slaves; - struct cpdma_ctlr *dma; - struct cpdma_chan *txch, *rxch; struct cpsw_ale *ale; boolrx_pause; booltx_pause; @@ -656,25 +656,21 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) } } -static void cpsw_intr_enable(struct cpsw_priv *priv) +static void cpsw_intr_enable(struct cpsw_common *cpsw) { - struct cpsw_common *cpsw = priv->cpsw; - __raw_writel(0xFF, &cpsw->wr_regs->tx_en); __raw_writel(0xFF, &cpsw->wr_regs->rx_en); - cpdma_ctlr_int_ctrl(priv->dma, true); + cpdma_ctlr_int_ctrl(cpsw->dma, true); return; } -static void cpsw_intr_disable(struct cpsw_priv *priv) +static void cpsw_intr_disable(struct cpsw_common *cpsw) { - struct cpsw_common *cpsw = priv->cpsw; - __raw_writel(0, &cpsw->wr_regs->tx_en); __raw_writel(0, &cpsw->wr_regs->rx_en); - cpdma_ctlr_int_ctrl(priv->dma, false); + cpdma_ctlr_int_ctrl(cpsw->dma, false); return; } @@ -702,6 +698,7 @@ static void cpsw_rx_handler(void *token, int len, int status) struct net_device *ndev = skb->dev; struct cpsw_priv*priv = netdev_priv(ndev); int ret = 0; + struct cpsw_common *cpsw = priv->cpsw; cpsw_dual_emac_src_port_detect(status, priv, ndev, skb); @@ -747,8 +744,8 @@ static void cpsw_rx_handler(void *token, int len, int status) } requeue: - ret = cpdma_chan_submit(priv->rxch, new_skb, new_skb->data, - skb_tailroom(new_skb), 0); + ret = cpdma_chan_submit(cpsw->rxch, new_skb, new_skb->data, + skb_tailroom(new_skb), 0); if (WARN_ON(ret < 0)) dev_kfree_skb_any(new_skb); } @@ -759,7 +756,7 @@ static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) struct cpsw_common *cpsw = priv->cpsw; writel(0, &cpsw->wr_regs->tx_en); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); + cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_TX); if (priv->quirk_irq) { disable_irq_nosync(priv->irqs_table[1]); @@ -775,7 +772,7 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) struct cpsw_priv *priv = dev_id; struct cpsw_common *cpsw = priv->cpsw; - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); + cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX); writel(0, &cpsw->wr_regs->rx_en); if (priv->quirk_irq) { @@ -793,7 +790,7 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) int num_tx; struct cpsw_common *cpsw = priv->cpsw; - num_tx = cpdma_chan_process(priv->txch, budget); + num_tx = cpdma_chan_process(cpsw->txch, budget); if (num_tx < budget) { napi_complete(napi_tx); writel(0xff, &cpsw->wr_regs->tx_en); @@ -813,7 +810,7 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) int num_rx; struct cpsw_common *cpsw = priv->cpsw; - num_rx = cpdma_chan_process(priv->rxch, budget); + num_rx = cpdma_chan_process(cpsw->rxch, budget); if (num_rx < budget) { napi_complete(napi_rx); writel(0xff, &cpsw->wr_regs->rx_en); @@ -1024,17 +1021,16 @@ static void cpsw_get_strings(struct net_device *ndev, u32 stringset, u8 *data) static void cpsw_get_ethtool_stats(struct net_device *ndev, struct ethtool_stats *stats, u64 *data) { - struct cpsw_priv *priv = netdev_priv(ndev); struct cpdma_chan_stats rx_stats; struc
[PATCH] net: ethernet: ti: cpsw: fix tx vlan priority mapping
The CPDMA_TX_PRIORITY_MAP in real is vlan pcp field priority mapping register and basically replaces vlan pcp field for tagged packets. So, set it to be 1:1 mapping. Signed-off-by: Ivan Khoronzhuk --- Based on net/master drivers/net/ethernet/ti/cpsw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 3037127..74f8284 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -129,7 +129,7 @@ do { \ #define RX_PRIORITY_MAPPING0x76543210 #define TX_PRIORITY_MAPPING0x33221100 -#define CPDMA_TX_PRIORITY_MAP 0x01234567 +#define CPDMA_TX_PRIORITY_MAP 0x76543210 #define CPSW_VLAN_AWAREBIT(1) #define CPSW_RX_VLAN_ENCAP BIT(2) -- 2.7.4
Re: [PATCH net-next] net: ethernet: ti: cpsw: drop vid0 configuration in dual_mac modey
On Mon, Nov 26, 2018 at 12:57:20PM -0600, Grygorii Strashko wrote: On 11/26/18 10:26 AM, Ivan Khoronzhuk wrote: On Sun, Nov 25, 2018 at 05:46:26PM -0600, Grygorii Strashko wrote: In dual_mac mode CPSW driver uses vid1 and vid2 by default to implement dual mac mode wich are used to configure pvids for each external ports. But, historicaly, it also adds vid0 to ALE table and sets "untag" bits for both ext. ports. As result, it's imposible to use priority tagged packets in dual mac mode. Hence, drop vid0 configuration in dual mac mode as it's not required for dual mac mode functionality and, this way, make it possible to use priority tagged packet in dual mac mode. So, now it's enabled to be added via regular ndo. I have similar change in mind, but was going to send it after mcast/ucast, and - enabling same vlans patch... 2 things stopped me to add this: 1) Moving it to be enabled via regular call is Ok, but in dual mac mode it causes overlaps, at least while vid deletion. So decided to wait till same vlans series is applied. TI driver documentation mentions this restriction "While adding VLAN id to the eth interfaces, same VLAN id should not be added in both interfaces which will lead to VLAN forwarding and act as switch" It's not accurate now. This sw bug "acting like a switch" was fixed indirectly in LKML ). And at least for upstream version, not TISDK, desc should be updated, but better do this when it fixed completely and merged with TISDK. I know about this "written" restriction (for tiSDK, and it's not TRM after all ...), it can be avoided and it's partly avoided now ... Also, for notice, while you add any of the vlans to any of the ports, vlan0 is added to both of them.restricted it or not. Thanks to last changes in the driver it's not "acting like a switch" The patch in question enables this in ndo, not me. #ip link add link eth0 name eth0.400 type vlan id 400 [ 326.538989] 8021q: 802.1Q VLAN Support v1.8 [ 326.543217] 8021q: adding VLAN 0 to HW filter on device eth0 [ 326.554645] 8021q: adding VLAN 0 to HW filter on device eth1 [ 326.572236] net eth0: Adding vlanid 400 to vlan filter I just propose to extend it later, when it's correct to do. But if no harm (basically no harm, only if someone decides to add vlan0 to both ports and then delete on one of them) , at least you should take this into account. 2) Wanted implement somehow similar handling for single port boards in one patch, not only for dual mac mode. This part was not clear and not verified completely... So, if it's needed now, maybe better at this moment only remove untag field? and remove vlan0 later, once other vlan changes applied. Say: cpsw_ale_add_vlan(cpsw->ale, cpsw->data.default_vlan, Â ALE_ALL_PORTS, 0, ALE_ALL_PORTS, 0); instead of: cpsw_ale_add_vlan(cpsw->ale, cpsw->data.default_vlan, Â ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0); This patch affects only dual_mac mode and in this mode adding vid0 by default is definitely make no sense in any case. The above proposition is only to your change, only for dual-mac. Signed-off-by: Grygorii Strashko --- drivers/net/ethernet/ti/cpsw.c | 7 ++- 1 file changed, 2 insertions(+), 5 deletions(-) -- regards, -grygorii -- Regards, Ivan Khoronzhuk
[PATCH v2 net-next 6/6] Documentation: networking: cpsw: add MQPRIO & CBS offload examples
This document describes MQPRIO and CBS Qdisc offload configuration for cpsw driver based on examples. It potentially can be used in audio video bridging (AVB) and time sensitive networking (TSN). Signed-off-by: Ivan Khoronzhuk --- Documentation/networking/ti-cpsw.txt | 540 +++ 1 file changed, 540 insertions(+) create mode 100644 Documentation/networking/ti-cpsw.txt diff --git a/Documentation/networking/ti-cpsw.txt b/Documentation/networking/ti-cpsw.txt new file mode 100644 index ..f5d58f502e52 --- /dev/null +++ b/Documentation/networking/ti-cpsw.txt @@ -0,0 +1,540 @@ +* Texas Instruments CPSW ethernet driver + +Multiqueue & CBS & MQPRIO += += + +The cpsw has 3 CBS shapers for each external ports. This document +describes MQPRIO and CBS Qdisc offload configuration for cpsw driver +based on examples. It potentially can be used in audio video bridging +(AVB) and time sensitive networking (TSN). + +The following examples was tested on AM572x EVM and BBB boards. + +Test setup +== + +Under consideration two examples with AM52xx EVM running cpsw driver +in dual_emac mode. + +Several prerequisites: +- TX queues must be rated starting from txq0 that has highest priority +- Traffic classes are used starting from 0, that has highest priority +- CBS shapers should be used with rated queues +- The bandwidth for CBS shapers has to be set a little bit more then + potential incoming rate, thus, rate of all incoming tx queues has + to be a little less +- Real rates can differ, due to discreetness +- Map skb-priority to txq is not enough, also skb-priority to l2 prio + map has to be created with ip or vconfig tool +- Any l2/socket prio (0 - 7) for classes can be used, but for + simplicity default values are used: 3 and 2 +- only 2 classes tested: A and B, but checked and can work with more, + maximum allowed 4, but only for 3 rate can be set. + +Test setup for examples +=== ++---+ +|--+| +| | Workstation0 | +|E | MAC 18:03:73:66:87:42 | ++-+ +--|t || +|| 1 | E | | |h |./tsn_listener -d \ | +| Target board: | 0 | t |--+ |0 | 18:03:73:66:87:42 -i eth0 \| +| AM572x EVM| 0 | h | | | -s 1500| +|| 0 | 0 | |--+| +| Only 2 classes: |Mb +---| +---+ +| class A, class B || +||+---| +---+ +|| 1 | E | |--+| +|| 0 | t | | | Workstation1 | +|| 0 | h |--+ |E | MAC 20:cf:30:85:7d:fd | +||Mb | 1 | +--|t || ++-+ |h |./tsn_listener -d \ | +|0 | 20:cf:30:85:7d:fd -i eth0 \| +| | -s 1500| +|--+| ++---+ + +* +* +* +Example 1: One port tx AVB configuration scheme for target board +-- +(prints and scheme for AM52xx evm, applicable for single port boards) + +tc - traffic class +txq - transmit queue +p - priority +f - fifo (cpsw fifo) +S - shaper configured + ++--+ u +| +---+ +---+ +--+ +--+ | s +| | | | | | | | | | e +| | App 1 | | App 2 | | Apps | | Apps | | r +| | Class A | | Class B | | Rest | | Rest | | +| | Eth0 | | Eth0 | | Eth0 | | Eth1 | | s +| | VLAN100 | | VLAN100 | | | | | | | | p +| | 40 Mb/s | | 20 Mb/s | | | | | | | | a +| | SO_PRIORITY=3 | | SO_PRIORITY=2 | | | | | | | | c +| | | | | | | | | | | | |
[RFC PATCH 2/6] net: ethernet: ti: cpdma: fit rated channels in backward order
According to TRM tx rated channels should be in 7..0 order, so correct it. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/davinci_cpdma.c | 31 - 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 31ae04117f0a..37fbdc668cc7 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -406,37 +406,36 @@ static int cpdma_chan_fit_rate(struct cpdma_chan *ch, u32 rate, struct cpdma_chan *chan; u32 old_rate = ch->rate; u32 new_rmask = 0; - int rlim = 1; + int rlim = 0; int i; - *prio_mode = 0; for (i = tx_chan_num(0); i < tx_chan_num(CPDMA_MAX_CHANNELS); i++) { chan = ctlr->channels[i]; - if (!chan) { - rlim = 0; + if (!chan) continue; - } if (chan == ch) chan->rate = rate; if (chan->rate) { - if (rlim) { - new_rmask |= chan->mask; - } else { - ch->rate = old_rate; - dev_err(ctlr->dev, "Prev channel of %dch is not rate limited\n", - chan->chan_num); - return -EINVAL; - } - } else { - *prio_mode = 1; - rlim = 0; + rlim = 1; + new_rmask |= chan->mask; + continue; } + + if (rlim) + goto err; } *rmask = new_rmask; + *prio_mode = rlim; return 0; + +err: + ch->rate = old_rate; + dev_err(ctlr->dev, "Upper cpdma ch%d is not rate limited\n", + chan->chan_num); + return -EINVAL; } static u32 cpdma_chan_set_factors(struct cpdma_ctlr *ctlr, -- 2.17.0
[RFC PATCH 5/6] net: ethernet: ti: cpsw: restore shaper configuration while down/up
Need to restore shapers configuration after interface was down/up. This is needed as appropriate configuration is still replicated in kernel settings. This only shapers context restore, so vlan configuration should be restored by user if needed, especially for devices with one port where vlan frames are sent via ALE. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 47 ++ 1 file changed, 47 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c7710b0e1c17..c3e88be36c1b 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1807,6 +1807,51 @@ static int cpsw_set_cbs(struct net_device *ndev, return ret; } +static void cpsw_cbs_resume(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + int fifo, bw; + + for (fifo = CPSW_FIFO_SHAPERS_NUM; fifo > 0; fifo--) { + bw = priv->fifo_bw[fifo]; + if (!bw) + continue; + + cpsw_set_fifo_rlimit(priv, fifo, bw); + } +} + +static void cpsw_mqprio_resume(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + struct cpsw_common *cpsw = priv->cpsw; + u32 tx_prio_map = 0; + int i, tc, fifo; + u32 tx_prio_rg; + + if (!priv->mqprio_hw) + return; + + for (i = 0; i < 8; i++) { + tc = netdev_get_prio_tc_map(priv->ndev, i); + fifo = CPSW_FIFO_SHAPERS_NUM - tc; + tx_prio_map |= fifo << (4 * i); + } + + tx_prio_rg = cpsw->version == CPSW_VERSION_1 ? +CPSW1_TX_PRI_MAP : CPSW2_TX_PRI_MAP; + + slave_write(slave, tx_prio_map, tx_prio_rg); +} + +/* restore resources after port reset */ +static void cpsw_restore(struct cpsw_priv *priv) +{ + /* restore MQPRIO offload */ + for_each_slave(priv, cpsw_mqprio_resume, priv); + + /* restore CBS offload */ + for_each_slave(priv, cpsw_cbs_resume, priv); +} + static int cpsw_ndo_open(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); @@ -1886,6 +1931,8 @@ static int cpsw_ndo_open(struct net_device *ndev) } + cpsw_restore(priv); + /* Enable Interrupt pacing if configured */ if (cpsw->coal_intvl != 0) { struct ethtool_coalesce coal; -- 2.17.0
[RFC PATCH 0/6] net: ethernet: ti: cpsw: add MQPRIO and CBS Qdisc offload
This series adds MQPRIO and CBS Qdisc offload for TI cpsw driver. It potentially can be used in audio video bridging (AVB) and time sensitive networking (TSN). Patchset was tested on AM572x EVM and BBB boards. Last patch from this series adds detailed description of configuration with examples. For consistency reasons, in role of talker and listener, tools from patchset "TSN: Add qdisc based config interface for CBS" were used and can be seen here: https://www.spinics.net/lists/netdev/msg460869.html Based on net-next/master Ivan Khoronzhuk (6): net: ethernet: ti: cpsw: use cpdma channels in backward order for txq net: ethernet: ti: cpdma: fit rated channels in backward order net: ethernet: ti: cpsw: add MQPRIO Qdisc offload net: ethernet: ti: cpsw: add CBS Qdisc offload net: ethernet: ti: cpsw: restore shaper configuration while down/up Documentation: networking: cpsw: add MQPRIO & CBS offload examples Documentation/networking/cpsw.txt | 540 drivers/net/ethernet/ti/cpsw.c | 364 +++- drivers/net/ethernet/ti/davinci_cpdma.c | 31 +- 3 files changed, 913 insertions(+), 22 deletions(-) create mode 100644 Documentation/networking/cpsw.txt -- 2.17.0
[RFC PATCH 6/6] Documentation: networking: cpsw: add MQPRIO & CBS offload examples
This document describes MQPRIO and CBS Qdisc offload configuration for cpsw driver based on examples. It potentially can be used in audio video bridging (AVB) and time sensitive networking (TSN). Signed-off-by: Ivan Khoronzhuk --- Documentation/networking/cpsw.txt | 540 ++ 1 file changed, 540 insertions(+) create mode 100644 Documentation/networking/cpsw.txt diff --git a/Documentation/networking/cpsw.txt b/Documentation/networking/cpsw.txt new file mode 100644 index ..28c64896d59d --- /dev/null +++ b/Documentation/networking/cpsw.txt @@ -0,0 +1,540 @@ +* Texas Instruments CPSW ethernet driver + +Multiqueue & CBS & MQPRIO += += + +The cpsw has 3 CBS shapers for each external ports. This document +describes MQPRIO and CBS Qdisc offload configuration for cpsw driver +based on examples. It potentially can be used in audio video bridging +(AVB) and time sensitive networking (TSN). + +The following examples was tested on AM572x EVM and BBB boards. + +Test setup +== + +Under consideration two examples with AM52xx EVM running cpsw driver +in dual_emac mode. + +Several prerequisites: +- TX queues must be rated starting from txq0 that has highest priority +- Traffic classes are used starting from 0, that has highest priority +- CBS shapers should be used with rated queues +- The bandwidth for CBS shapers has to be set a little bit more then + potential incoming rate, thus, rate of all incoming tx queues has + to be a little less +- Real rates can differ, due to discreetness +- Map skb-priority to txq is not enough, also skb-priority to l2 prio + map has to be created with ip or vconfig tool +- Any l2/socket prio (0 - 7) for classes can be used, but for + simplicity default values are used: 3 and 2 +- only 2 classes tested: A and B, but checked and can work with more, + maximum allowed 4, but only for 3 rate can be set. + +Test setup for examples +=== ++---+ +|--+| +| | Workstation0 | +|E | MAC 18:03:73:66:87:42 | ++-+ +--|t || +|| 1 | E | | |h |./tsn_listener -d \ | +| Target board: | 0 | t |--+ |0 | 18:03:73:66:87:42 -i eth0 \| +| AM572x EVM| 0 | h | | | -s 1500| +|| 0 | 0 | |--+| +| Only 2 classes: |Mb +---| +---+ +| class A, class B || +||+---| +---+ +|| 1 | E | |--+| +|| 0 | t | | | Workstation1 | +|| 0 | h |--+ |E | MAC 20:cf:30:85:7d:fd | +||Mb | 1 | +--|t || ++-+ |h |./tsn_listener -d \ | +|0 | 20:cf:30:85:7d:fd -i eth0 \| +| | -s 1500| +|--+| ++---+ + +* +* +* +Example 1: One port tx AVB configuration scheme for target board +-- +(prints and scheme for AM52xx evm, applicable for single port boards) + +tc - traffic class +txq - transmit queue +p - priority +f - fifo (cpsw fifo) +S - shaper configured + ++--+ u +| +---+ +---+ +--+ +--+ | s +| | | | | | | | | | e +| | App 1 | | App 2 | | Apps | | Apps | | r +| | Class A | | Class B | | Rest | | Rest | | +| | Eth0 | | Eth0 | | Eth0 | | Eth1 | | s +| | VLAN100 | | VLAN100 | | | | | | | | p +| | 40 Mb/s | | 20 Mb/s | | | | | | | | a +| | SO_PRIORITY=3 | | SO_PRIORITY=2 | | | | | | | | c +| | | | | | | | | | | | |
[RFC PATCH 3/6] net: ethernet: ti: cpsw: add MQPRIO Qdisc offload
That's possible to offload vlan to tc priority mapping with assumption sk_prio == L2 prio. Example: $ ethtool -L eth0 rx 1 tx 4 $ qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \ map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1 $ tc -g class show dev eth0 +---(100:ffe2) mqprio | Â Â Â +---(100:3) mqprio | Â Â Â +---(100:4) mqprio | Â Â Â +---(100:ffe1) mqprio | Â Â Â +---(100:2) mqprio | Â Â Â +---(100:ffe0) mqprio +---(100:1) mqprio Here, 100:1 is txq0, 100:2 is txq1, 100:3 is txq2, 100:4 is txq3 txq0 belongs to tc0, txq1 to tc1, txq2 and txq3 to tc2 The offload part only maps L2 prio to classes of traffic, but not to transmit queues, so to direct traffic to traffic class vlan has to be created with appropriate egress map. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 82 ++ 1 file changed, 82 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 9bd615da04d3..4b232cda5436 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -39,6 +39,7 @@ #include #include +#include #include "cpsw.h" #include "cpsw_ale.h" @@ -153,6 +154,8 @@ do { \ #define IRQ_NUM2 #define CPSW_MAX_QUEUES8 #define CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT 256 +#define CPSW_TC_NUM4 +#define CPSW_FIFO_SHAPERS_NUM (CPSW_TC_NUM - 1) #define CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT 29 #define CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSKGENMASK(2, 0) @@ -453,6 +456,7 @@ struct cpsw_priv { u8 mac_addr[ETH_ALEN]; boolrx_pause; booltx_pause; + boolmqprio_hw; u32 emac_port; struct cpsw_common *cpsw; }; @@ -1577,6 +1581,14 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_common *cpsw) soft_reset_slave(slave); } +static int cpsw_tc_to_fifo(int tc, int num_tc) +{ + if (tc == num_tc - 1) + return 0; + + return CPSW_FIFO_SHAPERS_NUM - tc; +} + static int cpsw_ndo_open(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); @@ -2190,6 +2202,75 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) return ret; } +static int cpsw_set_tc(struct net_device *ndev, void *type_data) +{ + struct tc_mqprio_qopt_offload *mqprio = type_data; + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + int fifo, num_tc, count, offset; + struct cpsw_slave *slave; + u32 tx_prio_map = 0; + int i, tc, ret; + + num_tc = mqprio->qopt.num_tc; + if (num_tc > CPSW_TC_NUM) + return -EINVAL; + + if (mqprio->mode != TC_MQPRIO_MODE_DCB) + return -EINVAL; + + ret = pm_runtime_get_sync(cpsw->dev); + if (ret < 0) { + pm_runtime_put_noidle(cpsw->dev); + return ret; + } + + if (num_tc) { + for (i = 0; i < 8; i++) { + tc = mqprio->qopt.prio_tc_map[i]; + fifo = cpsw_tc_to_fifo(tc, num_tc); + tx_prio_map |= fifo << (4 * i); + } + + netdev_set_num_tc(ndev, num_tc); + for (i = 0; i < num_tc; i++) { + count = mqprio->qopt.count[i]; + offset = mqprio->qopt.offset[i]; + netdev_set_tc_queue(ndev, i, count, offset); + } + } + + if (!mqprio->qopt.hw) { + /* restore default configuration */ + netdev_reset_tc(ndev); + tx_prio_map = TX_PRIORITY_MAPPING; + } + + priv->mqprio_hw = mqprio->qopt.hw; + + offset = cpsw->version == CPSW_VERSION_1 ? +CPSW1_TX_PRI_MAP : CPSW2_TX_PRI_MAP; + + slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)]; + slave_write(slave, tx_prio_map, offset); + + pm_runtime_put_sync(cpsw->dev); + + return 0; +} + +static int cpsw_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type, +void *type_data) +{ + switch (type) { + case TC_SETUP_QDISC_MQPRIO: + return cpsw_set_tc(ndev, type_data); + + default: + return -EOPNOTSUPP; + } +} + static const struct net_device_ops cpsw_netdev_ops = { .ndo_open = cpsw_ndo_open, .ndo_stop = cpsw_ndo_stop, @@ -2205,6 +2286,7 @@ static const struct net_device_ops cpsw_netdev_ops = { #endif .ndo_vlan_rx_add_vid= cpsw_ndo_vlan_rx_add_vid, .ndo_vlan_rx_ki
[RFC PATCH 4/6] net: ethernet: ti: cpsw: add CBS Qdisc offload
The cpsw has up to 4 FIFOs per port and upper 3 FIFOs can feed rate limited queue with shaping. In order to set and enable shaping for those 3 FIFOs queues the network device with CBS qdisc attached is needed. The CBS configuration is added for dual-emac/single port mode only, but potentially can be used in switch mode also, based on switchdev for instance. Despite the FIFO shapers can work w/o cpdma level shapers the base usage must be in combine with cpdma level shapers as described in TRM, that are set as maximum rates for interface queues with sysfs. One of the possible configuration with txq shapers and CBS shapers: Configured with echo RATE > /sys/class/net/eth0/queues/tx-0/tx_maxrate /--- / /cpdma level shapers ++ ++ ++ ++ ++ ++ ++ ++ | c7 | | c6 | | c5 | | c4 | | c3 | | c2 | | c1 | | c0 | \/ \/ \/ \/ \/ \/ \/ \/ \ / \ / \ / \ / \ / \ / \ / \ / \/ \/ \/ \/ \/ \/ \/ \/ +-|--|--|--|-+ |++ | | +---+ | || ++ | | | |v v v v | | ++ ++ ++ ++ pp++ ++ ++ ++ | | || || || || oo|| || || || | | | f3 | | f2 | | f1 | | f0 | r CPSW r| f3 | | f2 | | f1 | | f0 | | | || || || || tt|| || || || | | \/ \/ \/ \/ 01\/ \/ \/ \/ | | \ X \ / \ / \ / \ / \ / \ / \ / | | \/ \ \/ \/ \/ \/ \/ \/ \/| +---\+ \ \ FIFO shaper, set with CBS offload added in this patch, \ FIFO0 cannot be rate limited -- CBS shaper configuration is supposed to be used with root MQPRIO Qdisc offload allowing to add sk_prio->tc->txq maps that direct traffic to appropriate tx queue and maps L2 priority to FIFO shaper. The CBS shaper is intended to be used for AVB where L2 priority (pcp field) is used to differentiate class of traffic. So additionally vlan needs to be created with appropriate egress sk_prio->l2 prio map. If CBS has several tx queues assigned to it, the sum of their bandwidth has not overlap bandwidth set for CBS. It's recomended the CBS bandwidth to be a little bit more. The CBS shaper is configured with CBS qdisc offload interface using tc tool from iproute2 packet. For instance: $ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \ map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1 $ tc -g class show dev eth0 +---(100:ffe2) mqprio | Â Â Â +---(100:3) mqprio | Â Â Â +---(100:4) mqprio | Â Â Â +---(100:ffe1) mqprio | Â Â Â +---(100:2) mqprio | Â Â Â +---(100:ffe0) mqprio +---(100:1) mqprio $ tc qdisc add dev eth0 parent 100:1 cbs locredit -1440 \ hicredit 60 sendslope -96 idleslope 4 offload 1 $ tc qdisc add dev eth0 parent 100:2 cbs locredit -1470 \ hicredit 62 sendslope -98 idleslope 2 offload 1 The above code set CBS shapers for tc0 and tc1, for that txq0 and txq1 is used. Pay attention, the real set bandwidth can differ a bit due to discreteness of configuration parameters. Here parameters like locredit, hicredit and sendslope are ignored internally and are supposed to be set with assumption that maximum frame size for frame - 1500. It's supposed that interface speed is not changed while reconnection, not always is true, so inform user in case speed of interface was changed, as it can impact on dependent shapers configuration. For more examples see Documentation. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 221 + 1 file changed, 221 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 4b232cda5436..c7710b0e1c17 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -46,6 +46,8 @@ #include "cpts.h" #include "davinci_cpdma.h" +#include + #define CPSW_DEBUG (NETIF_MSG_HW | NETIF_MSG_WOL | \ NETIF_MSG_DRV | NETIF_MSG_LINK| \ NETIF_MSG_IFUP | NETIF_MSG_INTR| \ @@ -154,8 +156,12 @@ do { \ #define IRQ_NUM2 #define CPSW_MAX_QUEUES8 #define CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT 256 +#define
[RFC PATCH 1/6] net: ethernet: ti: cpsw: use cpdma channels in backward order for txq
The cpdma channel highest priority is from hi to lo number. The driver has limited number of descriptors that are shared between number of cpdma channels. Number of queues can be tuned with ethtool, that allows to not spend descriptors on not needed cpdma channels. In AVB usually only 2 tx queues can be enough with rate limitation. The rate limitation can be used only for hi priority queues. Thus, to use only 2 queues the 8 has to be created. It's wasteful. So, in order to allow using only needed number of rate limited tx queues, save resources, and be able to set rate limitation for them, let assign tx cpdma channels in backward order to queues. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 14 -- 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a7285dddfd29..9bd615da04d3 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -967,8 +967,8 @@ static int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget) /* process every unprocessed channel */ ch_map = cpdma_ctrl_txchs_state(cpsw->dma); - for (ch = 0, num_tx = 0; ch_map; ch_map >>= 1, ch++) { - if (!(ch_map & 0x01)) + for (ch = 0, num_tx = 0; ch_map & 0xff; ch_map <<= 1, ch++) { + if (!(ch_map & 0x80)) continue; txv = &cpsw->txv[ch]; @@ -2431,7 +2431,7 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) void (*handler)(void *, int, int); struct netdev_queue *queue; struct cpsw_vector *vec; - int ret, *ch; + int ret, *ch, vch; if (rx) { ch = &cpsw->rx_ch_num; @@ -2444,7 +2444,8 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) } while (*ch < ch_num) { - vec[*ch].ch = cpdma_chan_create(cpsw->dma, *ch, handler, rx); + vch = rx ? *ch : 7 - *ch; + vec[*ch].ch = cpdma_chan_create(cpsw->dma, vch, handler, rx); queue = netdev_get_tx_queue(priv->ndev, *ch); queue->tx_maxrate = 0; @@ -2980,7 +2981,7 @@ static int cpsw_probe(struct platform_device *pdev) u32 slave_offset, sliver_offset, slave_size; const struct soc_device_attribute *soc; struct cpsw_common *cpsw; - int ret = 0, i; + int ret = 0, i, ch; int irq; cpsw = devm_kzalloc(&pdev->dev, sizeof(struct cpsw_common), GFP_KERNEL); @@ -3155,7 +3156,8 @@ static int cpsw_probe(struct platform_device *pdev) if (soc) cpsw->quirk_irq = 1; - cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_tx_handler, 0); + ch = cpsw->quirk_irq ? 0 : 7; + cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, ch, cpsw_tx_handler, 0); if (IS_ERR(cpsw->txv[0].ch)) { dev_err(priv->dev, "error initializing tx dma channel\n"); ret = PTR_ERR(cpsw->txv[0].ch); -- 2.17.0
[RFC PATCH net-next 4/4] net: ethernet: ti: cpsw: fix vlan configuration while down/up
The vlan configuration is not restored after interface donw/up sequence (if dual-emac - both interfaces). Tested on am572x EVM. Steps to check: ~# ip link add link eth1 name eth1.100 type vlan id 100 ~# ifconfig eth0 down ~# ifconfig eth1 down This is TI sdk tool, dumping all ALE entries, see they are present ~# switch-config -d ~# ifconfig eth1 up See entry for vid 100 is absent ~# switch-config -d Try to remove vid and observe warning: ~# ip link del eth1.100 [ 739.526757] net eth1: removing vlanid 100 from vlan filter [ 739.533322] failed to kill vid 0081/100 for device eth1 This patch fixes it, restoring only vlan ALE entries and all other unicast/multicast entries are restored by system calling rx_mode ndo. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 5967484619d4..0b28a90b62bb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -565,6 +565,9 @@ static const struct cpsw_stats cpsw_gstrings_ch_stats[] = { (func)(slave++, ##arg); \ } while (0) +static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, + __be16 proto, u16 vid); + static inline int cpsw_get_slave_port(u32 slave_num) { return slave_num + 1; @@ -1955,9 +1958,23 @@ static void cpsw_mqprio_resume(struct cpsw_slave *slave, struct cpsw_priv *priv) slave_write(slave, tx_prio_map, tx_prio_rg); } +static int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg) +{ + struct cpsw_priv *priv = arg; + + if (!vdev) + return 0; + + cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid); + return 0; +} + /* restore resources after port reset */ static void cpsw_restore(struct cpsw_priv *priv) { + /* restore vlan configurations */ + vlan_for_each(priv->ndev, cpsw_restore_vlans, priv); + /* restore MQPRIO offload */ for_each_slave(priv, cpsw_mqprio_resume, priv); -- 2.17.1
[PATCH 2/2] net: ethernet: ti: cpsw: fix lost of mcast packets while rx_mode update
Whenever kernel or user decides to call rx mode update, it clears every multicast entry from forwarding table and in some time adds it again. This time can be enough to drop incoming multicast packets. That's why clear only staled multicast entries and update or add new one afterwards. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 46 +- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 832bce07c385..b7a6a2a0f71d 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -570,7 +570,7 @@ static inline int cpsw_get_slave_port(u32 slave_num) return slave_num + 1; } -static void cpsw_add_mcast(struct cpsw_priv *priv, u8 *addr) +static void cpsw_add_mcast(struct cpsw_priv *priv, const u8 *addr) { struct cpsw_common *cpsw = priv->cpsw; @@ -662,16 +662,35 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable) } } -static void cpsw_ndo_set_rx_mode(struct net_device *ndev) +static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + cpsw_add_mcast(priv, addr); + return 0; +} + +static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr) { struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; - int vid; + int vid, flags; - if (cpsw->data.dual_emac) + if (cpsw->data.dual_emac) { vid = cpsw->slaves[priv->emac_port].port_vlan; - else - vid = cpsw->data.default_vlan; + flags = ALE_VLAN; + } else { + vid = 0; + flags = 0; + } + + cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid); + return 0; +} + +static void cpsw_ndo_set_rx_mode(struct net_device *ndev) +{ + struct cpsw_common *cpsw = ndev_to_cpsw(ndev); if (ndev->flags & IFF_PROMISC) { /* Enable promiscuous mode */ @@ -684,19 +703,9 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) } /* Restore allmulti on vlans if necessary */ - cpsw_ale_set_allmulti(cpsw->ale, priv->ndev->flags & IFF_ALLMULTI); - - /* Clear all mcast from ALE */ - cpsw_ale_flush_multicast(cpsw->ale, ALE_ALL_PORTS, vid); + cpsw_ale_set_allmulti(cpsw->ale, ndev->flags & IFF_ALLMULTI); - if (!netdev_mc_empty(ndev)) { - struct netdev_hw_addr *ha; - - /* program multicast address list into ALE register */ - netdev_for_each_mc_addr(ha, ndev) { - cpsw_add_mcast(priv, ha->addr); - } - } + __dev_mc_sync(ndev, cpsw_add_mc_addr, cpsw_del_mc_addr); } static void cpsw_intr_enable(struct cpsw_common *cpsw) @@ -1956,6 +1965,7 @@ static int cpsw_ndo_stop(struct net_device *ndev) struct cpsw_common *cpsw = priv->cpsw; cpsw_info(priv, ifdown, "shutting down cpsw device\n"); + __dev_mc_unsync(priv->ndev, cpsw_del_mc_addr); netif_tx_stop_all_queues(priv->ndev); netif_carrier_off(priv->ndev); -- 2.17.1
[PATCH 0/2] net: ethernet: ti: cpsw fix mcast packet lost
The patchset omits redundant refresh of mcast address table and prevents mcast packet lost. Based on net-next/master tested on am572x evm Ivan Khoronzhuk (2): net: ethernet: ti: cpsw_ale: use const for API having pointer on mac address net: ethernet: ti: cpsw: fix lost of mcast packets while rx_mode update drivers/net/ethernet/ti/cpsw.c | 46 ++ drivers/net/ethernet/ti/cpsw_ale.c | 12 drivers/net/ethernet/ti/cpsw_ale.h | 8 +++--- 3 files changed, 38 insertions(+), 28 deletions(-) -- 2.17.1
[PATCH 1/2] net: ethernet: ti: cpsw_ale: use const for API having pointer on mac address
It allows to use function under callbacks with same const qualifier of mac address for farther changes. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw_ale.c | 12 ++-- drivers/net/ethernet/ti/cpsw_ale.h | 8 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index 5766225a4ce1..798c989d5d93 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -136,7 +136,7 @@ static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr) addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8); } -static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr) +static inline void cpsw_ale_set_addr(u32 *ale_entry, const u8 *addr) { int i; @@ -175,7 +175,7 @@ static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry) return idx; } -static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid) +static int cpsw_ale_match_addr(struct cpsw_ale *ale, const u8 *addr, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; @@ -309,7 +309,7 @@ static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry, } } -int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, +int cpsw_ale_add_ucast(struct cpsw_ale *ale, const u8 *addr, int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -336,7 +336,7 @@ int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, } EXPORT_SYMBOL_GPL(cpsw_ale_add_ucast); -int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, +int cpsw_ale_del_ucast(struct cpsw_ale *ale, const u8 *addr, int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -352,7 +352,7 @@ int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, } EXPORT_SYMBOL_GPL(cpsw_ale_del_ucast); -int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, +int cpsw_ale_add_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, int flags, u16 vid, int mcast_state) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -386,7 +386,7 @@ int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, } EXPORT_SYMBOL_GPL(cpsw_ale_add_mcast); -int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, +int cpsw_ale_del_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h index d4fe9016429b..cd07a3e96d57 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.h +++ b/drivers/net/ethernet/ti/cpsw_ale.h @@ -105,13 +105,13 @@ void cpsw_ale_start(struct cpsw_ale *ale); void cpsw_ale_stop(struct cpsw_ale *ale); int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid); -int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, +int cpsw_ale_add_ucast(struct cpsw_ale *ale, const u8 *addr, int port, int flags, u16 vid); -int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, +int cpsw_ale_del_ucast(struct cpsw_ale *ale, const u8 *addr, int port, int flags, u16 vid); -int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, +int cpsw_ale_add_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, int flags, u16 vid, int mcast_state); -int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, +int cpsw_ale_del_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, int flags, u16 vid); int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, int reg_mcast, int unreg_mcast); -- 2.17.1
[PATCH net-next] net: ethernet: ti: cpsw: use for mcast entries only host port
In dual-emac mode the cpsw driver sends directed packets, that means that packets go to the directed port, but an ALE lookup is performed to determine untagged egress only. It means that on tx side no need to add port bit for ALE mcast entry mask, and basically ALE entry for port identification is needed only on rx side. So, add only host port in dual_emac mode as used directed transmission, and no need in one more port. For single port boards and switch mode all ports used, as usual, so no changes for them. Also it simplifies farther changes. In other words, mcast entries for dual-emac should behave exactly like unicast. It also can help avoid leaking packets between ports with same vlan on h/w level if ports could became members of same vid. So now, for instance, if mcast address 33:33:00:00:00:01 is added then entries in ALE table: vid = 1, addr = 33:33:00:00:00:01, port_mask = 0x1 vid = 2, addr = 33:33:00:00:00:01, port_mask = 0x1 Instead of: vid = 1, addr = 33:33:00:00:00:01, port_mask = 0x3 vid = 2, addr = 33:33:00:00:00:01, port_mask = 0x5 With the same considerations, set only host port for unregistered mcast for dual-emac mode in case of IFF_ALLMULTI is set, exactly like it's done in cpsw_ale_set_allmulti(). Signed-off-by: Ivan Khoronzhuk --- Based on net-next/master drivers/net/ethernet/ti/cpsw.c | 13 +++-- 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 16dcbf36f8cc..7bfb7ee3a261 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -576,10 +576,8 @@ static void cpsw_add_mcast(struct cpsw_priv *priv, u8 *addr) if (cpsw->data.dual_emac) { struct cpsw_slave *slave = cpsw->slaves + priv->emac_port; - int slave_port = cpsw_get_slave_port(slave->slave_num); - cpsw_ale_add_mcast(cpsw->ale, addr, - 1 << slave_port | ALE_PORT_HOST, + cpsw_ale_add_mcast(cpsw->ale, addr, ALE_PORT_HOST, ALE_VLAN, slave->port_vlan, 0); return; } @@ -1410,7 +1408,7 @@ static inline void cpsw_add_dual_emac_def_ale_entries( cpsw_ale_add_vlan(cpsw->ale, slave->port_vlan, port_mask, port_mask, port_mask, 0); cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, - port_mask, ALE_VLAN, slave->port_vlan, 0); + ALE_PORT_HOST, ALE_VLAN, slave->port_vlan, 0); cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM, ALE_VLAN | ALE_SECURE, slave->port_vlan); @@ -2293,16 +2291,19 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, { int ret; int unreg_mcast_mask = 0; + int mcast_mask; u32 port_mask; struct cpsw_common *cpsw = priv->cpsw; if (cpsw->data.dual_emac) { port_mask = (1 << (priv->emac_port + 1)) | ALE_PORT_HOST; + mcast_mask = ALE_PORT_HOST; if (priv->ndev->flags & IFF_ALLMULTI) - unreg_mcast_mask = port_mask; + unreg_mcast_mask = mcast_mask; } else { port_mask = ALE_ALL_PORTS; + mcast_mask = port_mask; if (priv->ndev->flags & IFF_ALLMULTI) unreg_mcast_mask = ALE_ALL_PORTS; @@ -2321,7 +2322,7 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, goto clean_vid; ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, -port_mask, ALE_VLAN, vid, 0); +mcast_mask, ALE_VLAN, vid, 0); if (ret != 0) goto clean_vlan_ucast; return 0; -- 2.17.1
[PATCH net-next] net: ethernet: ti: cpsw: don't flush mcast entries while switch promisc mode
No need now to flush mcast entries in switch mode while toggling to promiscuous mode. It's not needed as vlan reg_mcast = ALL_PORTS and mcast/vlan ports = ALL_PORTS, the same happening for vlan unreg_mcast, it's set to ALL_PORT_MASK just after calling promisc mode routine by calling set allmulti. I suppose main reason to flush them is to use unreg_mcast to receive all to host port. Thus, now, all mcast packets are received anyway and no reason to flush mcast entries unsafely, as they were synced with __dev_mc_sync() previously and are not restored. Another way is to _dev_mc_unsync() them, but no need. Signed-off-by: Ivan Khoronzhuk --- Based on net-next/master Tasted on am572x EVM and BBB drivers/net/ethernet/ti/cpsw.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 226be2a56c1f..0e475020a674 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -638,9 +638,6 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable) } while (time_after(timeout, jiffies)); cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1); - /* Clear all mcast from ALE */ - cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1); - /* Flood All Unicast Packets to Host port */ cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1); dev_dbg(&ndev->dev, "promiscuity enabled\n"); -- 2.17.1
Re: [RFC PATCH net-next 2/4] net: 8021q: vlan_core: allow use list of vlans for real device
On Fri, Oct 19, 2018 at 01:22:20PM +0200, Bjørn Mork wrote: Ivan Khoronzhuk writes: @@ -236,6 +239,13 @@ __vlan_find_dev_deep_rcu(struct net_device *real_dev, return NULL; } +static inline int +vlan_for_each(struct net_device *dev, + int (*action)(struct net_device *dev, int vid, void *arg), + void *arg) +{ +} + This stub should return 0, shouldn't it? yes, it has. Bjørn -- Regards, Ivan Khoronzhuk
[PATCH net-next] net: ethernet: ti: cpsw: unsync mcast entries while switch promisc mode
After flushing all mcast entries from the table, the ones contained in mc list of ndev are not restored when promisc mode is toggled off, because they are considered as synched with ALE, thus, in order to restore them after promisc mode - reset syncing info. This fix touches only switch mode devices, including single port boards like Beagle Bone. Signed-off-by: Ivan Khoronzhuk --- Based on net-nex/master and is logical continuation of the https://lore.kernel.org/patchwork/patch/1001633/ drivers/net/ethernet/ti/cpsw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 226be2a56c1f..f7753b240ced 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -640,6 +640,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable) /* Clear all mcast from ALE */ cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1); + __dev_mc_unsync(ndev, NULL); /* Flood All Unicast Packets to Host port */ cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1); -- 2.17.1
[PATCH 1/2] firmware: dmi_scan: add symbol to get SMBIOS entry area
There are situations when code needs to access SMBIOS entry table area. For example, to pass it via sysfs to userspace when it's not allowed to get SMBIOS info via /dev/mem. Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi_scan.c | 34 ++ include/linux/dmi.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 420c8d8..ae9204a 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -113,6 +113,7 @@ static void dmi_table(u8 *buf, int len, int num, } } +static unsigned char smbios_header[32]; static phys_addr_t dmi_base; static u16 dmi_len; static u16 dmi_num; @@ -474,6 +475,7 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { smbios_ver = get_unaligned_be16(buf + 6); + memcpy(smbios_header, buf, buf[5]); /* Some BIOS report weird SMBIOS version, fix that up */ switch (smbios_ver) { @@ -505,6 +507,7 @@ static int __init dmi_present(const u8 *buf) pr_info("SMBIOS %d.%d present.\n", dmi_ver >> 8, dmi_ver & 0xFF); } else { + memcpy(smbios_header, buf, 15); dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F); pr_info("Legacy DMI %d.%d present.\n", @@ -531,6 +534,7 @@ static int __init dmi_smbios3_present(const u8 *buf) dmi_ver &= 0xFF; dmi_len = get_unaligned_le32(buf + 12); dmi_base = get_unaligned_le64(buf + 16); + memcpy(smbios_header, buf, buf[6]); /* * The 64-bit SMBIOS 3.0 entry point no longer has a field @@ -944,3 +948,33 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device) } } EXPORT_SYMBOL_GPL(dmi_memdev_name); + +/** + * dmi_get_smbios_entry_area - copy SMBIOS entry point area to array. + * @entry - pointer on array to read area in, current max size is 32 bytes. + * + * returns -ENODATA if table is not available, otherwise returns actual + * size of SMBIOS entry point area. + */ +int dmi_get_smbios_entry_area(char *table) +{ + int size = 0; + + if (!dmi_available) + return -ENODATA; + + if (memcmp(smbios_header, "_SM3_", 5) == 0) + size = smbios_header[6]; + else if (memcmp(smbios_header, "_SM_", 4) == 0) + size = smbios_header[5]; + else if (memcmp(smbios_header, "_DMI_", 5) == 0) + size = 15; + + memcpy(table, smbios_header, size); + + if (!size) + return -ENODATA; + + return size; +} +EXPORT_SYMBOL_GPL(dmi_get_smbios_entry_area); diff --git a/include/linux/dmi.h b/include/linux/dmi.h index f820f0a..7cae713 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -109,6 +109,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), void *private_data); extern bool dmi_match(enum dmi_field f, const char *str); extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); +int dmi_get_smbios_entry_area(char *table); #else @@ -140,6 +141,7 @@ static inline void dmi_memdev_name(u16 handle, const char **bank, const char **device) { } static inline const struct dmi_system_id * dmi_first_match(const struct dmi_system_id *list) { return NULL; } +static inline int dmi_get_smbios_entry_area(char *table) { return -ENODATA; } #endif -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 0/2] firmware: dmi-sysfs: add SMBIOS
Some utils, like dmidecode and smbios, needs to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry. So this patch series adds SMBIOS area to dmi sysfs in order to allow utils in question to work correctly with dmi sysfs. Ivan Khoronzhuk (2): firmware: dmi_scan: add symbol to get SMBIOS entry area firmware: dmi-sysfs: add SMBIOS entry point area attribute drivers/firmware/dmi-sysfs.c | 42 ++ drivers/firmware/dmi_scan.c | 34 ++ include/linux/dmi.h | 2 ++ 3 files changed, 78 insertions(+) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 2/2] firmware: dmi-sysfs: add SMBIOS entry point area attribute
There are situations when code needs to access SMBIOS entry table area, but cannot use /dev/mem for this. As the table format is consistent only for a version, and can be changed, use binary attribute to give access to raw SMBIOS entry table area. Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi-sysfs.c | 42 ++ 1 file changed, 42 insertions(+) diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3..b5c0558 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -46,6 +46,8 @@ struct dmi_sysfs_entry { static LIST_HEAD(entry_list); static DEFINE_SPINLOCK(entry_list_lock); +static unsigned char smbios_raw_header[32]; + /* dmi_sysfs_attribute - Top level attribute. used by all entries. */ struct dmi_sysfs_attribute { struct attribute attr; @@ -646,9 +648,37 @@ static void cleanup_entry_list(void) } } +static ssize_t smbios_entry_area_raw_read(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t size; + + size = bin_attr->size; + + if (size > pos) + size -= pos; + else + return 0; + + if (count < size) + size = count; + + memcpy(buf, &smbios_raw_header[pos], size); + + return size; +} + +static struct bin_attribute smbios_entry_area_raw_attr = { + .read = smbios_entry_area_raw_read, + .attr = {.name = "smbios_raw_header", .mode = 0400}, +}; + static int __init dmi_sysfs_init(void) { int error = -ENOMEM; + int size; int val; /* Set up our directory */ @@ -669,6 +699,18 @@ static int __init dmi_sysfs_init(void) goto err; } + size = dmi_get_smbios_entry_area(smbios_raw_header); + if (size == -ENODATA) { + pr_debug("dmi-sysfs: SMBIOS raw data is not available.\n"); + error = size; + goto err; + } + + /* Create the raw binary file to access the entry area */ + smbios_entry_area_raw_attr.size = size; + if (sysfs_create_bin_file(dmi_kobj, &smbios_entry_area_raw_attr)) + goto err; + pr_debug("dmi-sysfs: loaded.\n"); return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 1/2] firmware: dmi_scan: add symbol to get SMBIOS entry area
On 01/26/2015 10:44 AM, Ard Biesheuvel wrote: On 23 January 2015 at 20:21, Ivan Khoronzhuk wrote: There are situations when code needs to access SMBIOS entry table area. For example, to pass it via sysfs to userspace when it's not allowed to get SMBIOS info via /dev/mem. Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi_scan.c | 34 ++ include/linux/dmi.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 420c8d8..ae9204a 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -113,6 +113,7 @@ static void dmi_table(u8 *buf, int len, int num, } } +static unsigned char smbios_header[32]; static phys_addr_t dmi_base; static u16 dmi_len; static u16 dmi_num; @@ -474,6 +475,7 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { smbios_ver = get_unaligned_be16(buf + 6); + memcpy(smbios_header, buf, buf[5]); /* Some BIOS report weird SMBIOS version, fix that up */ switch (smbios_ver) { @@ -505,6 +507,7 @@ static int __init dmi_present(const u8 *buf) pr_info("SMBIOS %d.%d present.\n", dmi_ver >> 8, dmi_ver & 0xFF); } else { + memcpy(smbios_header, buf, 15); dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F); pr_info("Legacy DMI %d.%d present.\n", @@ -531,6 +534,7 @@ static int __init dmi_smbios3_present(const u8 *buf) dmi_ver &= 0xFF; dmi_len = get_unaligned_le32(buf + 12); dmi_base = get_unaligned_le64(buf + 16); + memcpy(smbios_header, buf, buf[6]); /* * The 64-bit SMBIOS 3.0 entry point no longer has a field @@ -944,3 +948,33 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device) } } EXPORT_SYMBOL_GPL(dmi_memdev_name); + +/** + * dmi_get_smbios_entry_area - copy SMBIOS entry point area to array. + * @entry - pointer on array to read area in, current max size is 32 bytes. + * + * returns -ENODATA if table is not available, otherwise returns actual + * size of SMBIOS entry point area. + */ +int dmi_get_smbios_entry_area(char *table) +{ What about const u8 *dmi_get_smbios_header(int *size) and return the buffer (or NULL if no data) and the size previously recorded in *size (see below) The reason is that, in the second patch, you are copying the data into yet another char[32], which doesn't make a great deal of sense is the data is not captured right at that time (I suggested earlier that the correct way to implement this was to populate the header at the same time you traverse the dmi tree to populate the sysfs entries. If you capture the data at init time, there is no reason to copy it yet again at dmi_sysfs module_init time) yes. Why I don't trust to return pointer even in case of const. Even don't remember. I'll update with const. Thanks. + int size = 0; + + if (!dmi_available) + return -ENODATA; + + if (memcmp(smbios_header, "_SM3_", 5) == 0) + size = smbios_header[6]; + else if (memcmp(smbios_header, "_SM_", 4) == 0) + size = smbios_header[5]; + else if (memcmp(smbios_header, "_DMI_", 5) == 0) + size = 15; + You are duplicating work here: could we record smbios_header_size when you capture the data itself? ~ I'll update soon. + memcpy(table, smbios_header, size); + + if (!size) + return -ENODATA; + + return size; +} +EXPORT_SYMBOL_GPL(dmi_get_smbios_entry_area); diff --git a/include/linux/dmi.h b/include/linux/dmi.h index f820f0a..7cae713 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -109,6 +109,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), void *private_data); extern bool dmi_match(enum dmi_field f, const char *str); extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); +int dmi_get_smbios_entry_area(char *table); #else @@ -140,6 +141,7 @@ static inline void dmi_memdev_name(u16 handle, const char **bank, const char **device) { } static inline const struct dmi_system_id * dmi_first_match(const struct dmi_system_id *list) { return NULL; } +static inline int dmi_get_smbios_entry_area(char *table) { return -ENODATA; } #endif -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
[Patch v2 0/2] firmware: dmi-sysfs: add SMBIOS entry point area raw attribute
Some utils, like dmidecode and smbios, needs to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry. So this patch series adds SMBIOS area to dmi sysfs in order to allow utils in question to work correctly with dmi sysfs. v1: https://lkml.org/lkml/2015/1/23/643 v2..v1: firmware: dmi_scan: add symbol to get SMBIOS entry area - used additional static var to hold SMBIOS raw table size - changed format of get_smbios_entry_area symbol returned pointer on const smbios table firmware: dmi-sysfs: add SMBIOS entry point area attribute - adopted to updated get_smbios_entry_area symbol - removed redundant array to save smbios table Ivan Khoronzhuk (2): firmware: dmi_scan: add symbol to get SMBIOS entry area firmware: dmi-sysfs: add SMBIOS entry point area attribute drivers/firmware/dmi-sysfs.c | 42 ++ drivers/firmware/dmi_scan.c | 26 ++ include/linux/dmi.h | 3 +++ 3 files changed, 71 insertions(+) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v2 2/2] firmware: dmi-sysfs: add SMBIOS entry point area attribute
There are situations when code needs to access SMBIOS entry table area, but cannot use /dev/mem for this. As the table format is consistent only for a version, and can be changed, use binary attribute to give access to raw SMBIOS entry table area. Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi-sysfs.c | 42 ++ 1 file changed, 42 insertions(+) diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3..61b6a38 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -29,6 +29,8 @@ #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider the top entry type is only 8 bits */ +static const u8 *smbios_raw_header; + struct dmi_sysfs_entry { struct dmi_header dh; struct kobject kobj; @@ -646,9 +648,37 @@ static void cleanup_entry_list(void) } } +static ssize_t smbios_entry_area_raw_read(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t size; + + size = bin_attr->size; + + if (size > pos) + size -= pos; + else + return 0; + + if (count < size) + size = count; + + memcpy(buf, &smbios_raw_header[pos], size); + + return size; +} + +static struct bin_attribute smbios_raw_area_attr = { + .read = smbios_entry_area_raw_read, + .attr = {.name = "smbios_raw_header", .mode = 0400}, +}; + static int __init dmi_sysfs_init(void) { int error = -ENOMEM; + int size; int val; /* Set up our directory */ @@ -669,6 +699,18 @@ static int __init dmi_sysfs_init(void) goto err; } + smbios_raw_header = dmi_get_smbios_entry_area(&size); + if (!smbios_raw_header) { + pr_debug("dmi-sysfs: SMBIOS raw data is not available.\n"); + error = -ENODATA; + goto err; + } + + /* Create the raw binary file to access the entry area */ + smbios_raw_area_attr.size = size; + if (sysfs_create_bin_file(dmi_kobj, &smbios_raw_area_attr)) + goto err; + pr_debug("dmi-sysfs: loaded.\n"); return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v2 1/2] firmware: dmi_scan: add symbol to get SMBIOS entry area
There are situations when code needs to access SMBIOS entry table area. For example, to pass it via sysfs to userspace when it's not allowed to get SMBIOS info via /dev/mem. Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi_scan.c | 26 ++ include/linux/dmi.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 420c8d8..d94c6b7 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -113,6 +113,8 @@ static void dmi_table(u8 *buf, int len, int num, } } +static unsigned char smbios_header[32]; +static int smbios_header_size; static phys_addr_t dmi_base; static u16 dmi_len; static u16 dmi_num; @@ -474,6 +476,8 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { smbios_ver = get_unaligned_be16(buf + 6); + smbios_header_size = buf[5]; + memcpy(smbios_header, buf, smbios_header_size); /* Some BIOS report weird SMBIOS version, fix that up */ switch (smbios_ver) { @@ -505,6 +509,8 @@ static int __init dmi_present(const u8 *buf) pr_info("SMBIOS %d.%d present.\n", dmi_ver >> 8, dmi_ver & 0xFF); } else { + smbios_header_size = 15; + memcpy(smbios_header, buf, smbios_header_size); dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F); pr_info("Legacy DMI %d.%d present.\n", @@ -531,6 +537,8 @@ static int __init dmi_smbios3_present(const u8 *buf) dmi_ver &= 0xFF; dmi_len = get_unaligned_le32(buf + 12); dmi_base = get_unaligned_le64(buf + 16); + smbios_header_size = buf[6]; + memcpy(smbios_header, buf, smbios_header_size); /* * The 64-bit SMBIOS 3.0 entry point no longer has a field @@ -944,3 +952,21 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device) } } EXPORT_SYMBOL_GPL(dmi_memdev_name); + +/** + * dmi_get_smbios_entry_area - copy SMBIOS entry point area to array. + * @size - pointer to assign actual size of SMBIOS entry point area. + * + * returns NULL if table is not available, otherwise returns pointer on + * SMBIOS entry point area array. + */ +const u8 *dmi_get_smbios_entry_area(int *size) +{ + if (!smbios_header_size || !dmi_available) + return NULL; + + *size = smbios_header_size; + + return smbios_header; +} +EXPORT_SYMBOL_GPL(dmi_get_smbios_entry_area); diff --git a/include/linux/dmi.h b/include/linux/dmi.h index f820f0a..8e1a28d 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -109,6 +109,7 @@ extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), void *private_data); extern bool dmi_match(enum dmi_field f, const char *str); extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); +const u8 *dmi_get_smbios_entry_area(int *size); #else @@ -140,6 +141,8 @@ static inline void dmi_memdev_name(u16 handle, const char **bank, const char **device) { } static inline const struct dmi_system_id * dmi_first_match(const struct dmi_system_id *list) { return NULL; } +static inline const u8 *dmi_get_smbios_entry_area(int *size) + { return NULL; } #endif -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [Patch v2 0/2] firmware: dmi-sysfs: add SMBIOS entry point area raw attribute
On 01/27/2015 06:23 PM, Grant Likely wrote: On Mon, 26 Jan 2015 15:28:36 +0200 , Ivan Khoronzhuk wrote: Some utils, like dmidecode and smbios, needs to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry. So this patch series adds SMBIOS area to dmi sysfs in order to allow utils in question to work correctly with dmi sysfs. v1: https://lkml.org/lkml/2015/1/23/643 Hi Ivan, The change looks good to me, but it is an ABI addition, so it needs to be documented. You'll need to add a description to: Documentation/ABI/testing/sysfs-testing/sysfs-firmware-dmi Ups... Ok, It'll be in Documentation/ABI/testing/sysfs-firmware-dmi Thanks! Second, (minor point), there is no reason to split this up into two patches. It is one feature, and would be better as a single patch. g. Ok, it'll be one patch, including documentation v2..v1: firmware: dmi_scan: add symbol to get SMBIOS entry area - used additional static var to hold SMBIOS raw table size - changed format of get_smbios_entry_area symbol returned pointer on const smbios table firmware: dmi-sysfs: add SMBIOS entry point area attribute - adopted to updated get_smbios_entry_area symbol - removed redundant array to save smbios table Ivan Khoronzhuk (2): firmware: dmi_scan: add symbol to get SMBIOS entry area firmware: dmi-sysfs: add SMBIOS entry point area attribute drivers/firmware/dmi-sysfs.c | 42 ++ drivers/firmware/dmi_scan.c | 26 ++ include/linux/dmi.h | 3 +++ 3 files changed, 71 insertions(+) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v3] firmware: dmi-sysfs: add SMBIOS entry point area raw attribute
Some utils, like dmidecode and smbios, needs to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry. So this patch adds SMBIOS area to dmi-sysfs in order to allow utils in question to work correctly with dmi sysfs interface. Reviewed-by: Ard Biesheuvel Signed-off-by: Ivan Khoronzhuk --- v1: https://lkml.org/lkml/2015/1/23/643 v2: https://lkml.org/lkml/2015/1/26/345 v3..v2: firmware: dmi_scan: add symbol to get SMBIOS entry area firmware: dmi-sysfs: add SMBIOS entry point area attribute combined in one patch added SMBIOS information to ABI sysfs-dmi documentaton v2..v1: firmware: dmi_scan: add symbol to get SMBIOS entry area - used additional static var to hold SMBIOS raw table size - changed format of get_smbios_entry_area symbol returned pointer on const smbios table firmware: dmi-sysfs: add SMBIOS entry point area attribute - adopted to updated get_smbios_entry_area symbol - removed redundant array to save smbios table Documentation/ABI/testing/sysfs-firmware-dmi | 10 +++ drivers/firmware/dmi-sysfs.c | 42 drivers/firmware/dmi_scan.c | 26 + include/linux/dmi.h | 3 ++ 4 files changed, 81 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi b/Documentation/ABI/testing/sysfs-firmware-dmi index c78f9ab..3a9ffe8 100644 --- a/Documentation/ABI/testing/sysfs-firmware-dmi +++ b/Documentation/ABI/testing/sysfs-firmware-dmi @@ -12,6 +12,16 @@ Description: cannot ensure that the data as exported to userland is without error either. + The firmware provides DMI structures as a packed list of + data referenced by a SMBIOS table entry point. The SMBIOS + entry point contains general information, like SMBIOS + version, DMI table size, etc. The structure, content and + size of SMBIOS entry point is dependent on SMBIOS version. + That's why SMBIOS entry point is represented in dmi sysfs + like a raw attribute and is accessible via + /sys/firmware/dmi/smbios_raw_header. The format of SMBIOS + entry point header can be read in SMBIOS specification. + DMI is structured as a large table of entries, where each entry has a common header indicating the type and length of the entry, as well as a firmware-provided diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3..61b6a38 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -29,6 +29,8 @@ #define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider the top entry type is only 8 bits */ +static const u8 *smbios_raw_header; + struct dmi_sysfs_entry { struct dmi_header dh; struct kobject kobj; @@ -646,9 +648,37 @@ static void cleanup_entry_list(void) } } +static ssize_t smbios_entry_area_raw_read(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t size; + + size = bin_attr->size; + + if (size > pos) + size -= pos; + else + return 0; + + if (count < size) + size = count; + + memcpy(buf, &smbios_raw_header[pos], size); + + return size; +} + +static struct bin_attribute smbios_raw_area_attr = { + .read = smbios_entry_area_raw_read, + .attr = {.name = "smbios_raw_header", .mode = 0400}, +}; + static int __init dmi_sysfs_init(void) { int error = -ENOMEM; + int size; int val; /* Set up our directory */ @@ -669,6 +699,18 @@ static int __init dmi_sysfs_init(void) goto err; } + smbios_raw_header = dmi_get_smbios_entry_area(&size); + if (!smbios_raw_header) { + pr_debug("dmi-sysfs: SMBIOS raw data is not available.\n"); + error = -ENODATA; + goto err; + } + + /* Create the raw binary file to access the entry area */ + smbios_raw_area_attr.size = size; + if (sysfs_create_bin_file(dmi_kobj, &smbios_raw_area_attr)) + goto err; + pr_debug("dmi-sysfs: loaded.\n"); return 0; diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 420c8d8..d94c6b7 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi
[Patch 2/3] firmware: dmi_scan: add SBMIOS entry and DMI tables
Some utils, like dmidecode and smbios, need to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry and adds code/delay redundancy when direct access for table is needed. So this patch creates dmi/tables and adds SMBIOS entry point to allow utils in question to work correctly without /dev/mem. Also patch adds raw dmi table to simplify dmi table processing in user space, as proposed by Jean Delvare. Signed-off-by: Ivan Khoronzhuk --- .../ABI/testing/sysfs-firmware-dmi-tables | 22 ++ drivers/firmware/dmi-sysfs.c | 11 ++- drivers/firmware/dmi_scan.c| 80 ++ include/linux/dmi.h| 1 + 4 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-firmware-dmi-tables diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi-tables b/Documentation/ABI/testing/sysfs-firmware-dmi-tables new file mode 100644 index 000..f46158c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-tables @@ -0,0 +1,22 @@ +What: /sys/firmware/dmi/tables/ +Date: April 2015 +Contact: Ivan Khoronzhuk +Description: + The firmware provides DMI structures as a packed list of + data referenced by a SMBIOS table entry point. The SMBIOS + entry point contains general information, like SMBIOS + version, DMI table size, etc. The structure, content and + size of SMBIOS entry point is dependent on SMBIOS version. + The format of SMBIOS entry point, equal as DMI structures + can be read in SMBIOS specification. + + The dmi/tables provides raw SMBIOS entry point and DMI tables + through sysfs as an alternative to utilities reading them + from /dev/mem. The raw SMBIOS entry point and DMI table are + presented as raw attributes and are accessible via: + + /sys/firmware/dmi/tables/smbios_entry_point + /sys/firmware/dmi/tables/DMI + + The complete DMI information can be taken using these two + tables. diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3..8e1a411 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = { .default_attrs = dmi_sysfs_entry_attrs, }; -static struct kobject *dmi_kobj; static struct kset *dmi_kset; /* Global count of all instances seen. Only for setup */ @@ -651,10 +650,11 @@ static int __init dmi_sysfs_init(void) int error = -ENOMEM; int val; - /* Set up our directory */ - dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); - if (!dmi_kobj) + if (!dmi_kobj) { + pr_err("dmi-sysfs: dmi entry is absent.\n"); + error = -ENOSYS; goto err; + } dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); if (!dmi_kset) @@ -675,7 +675,6 @@ static int __init dmi_sysfs_init(void) err: cleanup_entry_list(); kset_unregister(dmi_kset); - kobject_put(dmi_kobj); return error; } @@ -685,8 +684,6 @@ static void __exit dmi_sysfs_exit(void) pr_debug("dmi-sysfs: unloading.\n"); cleanup_entry_list(); kset_unregister(dmi_kset); - kobject_del(dmi_kobj); - kobject_put(dmi_kobj); } module_init(dmi_sysfs_init); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index d3aae09..bb19f8b 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -10,6 +10,9 @@ #include #include +struct kobject *dmi_kobj; +EXPORT_SYMBOL_GPL(dmi_kobj); + /* * DMI stands for "Desktop Management Interface". It is part * of and an antecedent to, SMBIOS, which stands for System @@ -20,6 +23,9 @@ static const char dmi_empty_string[] = ""; static u32 dmi_ver __initdata; static u32 dmi_len; static u16 dmi_num; +static u8 smbios_entry_point[32]; +static int smbios_entry_point_size; + /* * Catch too early calls to dmi_check_system(): */ @@ -118,6 +124,7 @@ static void dmi_decode_table(u8 *buf, } static phys_addr_t dmi_base; +static u8 *dmi_table; static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, void *)) @@ -476,6 +483,8 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { smbios_ver = get_unaligned_be16(buf + 6); + smbios
[Patch 0/3] firmware: dmi_scan: add SBMIOS entry point and DMI tables
This series adds SMBIOS entry point table and DMI table under /sys/firmware/dmi/tables in order to use as an alternative to utilities reading them from /dev/mem. This is logical continuation of "[Patch] firmware: dmi_scan: split dmisubsystem from dmi-sysfs" https://lkml.org/lkml/2015/3/16/1070 Based on efi/next Last changes: - renamed dmi_table to dmi_decode_table - moved tables under /sys/firmware/dmi/tables/ - avoid "subsystem" word - split in separate patch correction of dmi-sysfs documentation - using -ENOSYS instead of -EINVAL - use user read only rights for table attributes - use "static" for attributes - check only dmi_available for table presents - don't correct dmi_walk to constantly map dmi_table - explicitly delete binary attributes - assign dmi_kobj = NULL in case of error - don't export dmi_kobj if CONFIG_DMI is not set - improve read callback for table attributes Ivan Khoronzhuk (3): firmware: dmi_scan: rename dmi_table to dmi_decode_table firmware: dmi_scan: add SBMIOS entry and DMI tables Documentation: ABI: sysfs-firmware-dmi: add -entries suffix to file name ...sfs-firmware-dmi => sysfs-firmware-dmi-entries} | 2 +- .../ABI/testing/sysfs-firmware-dmi-tables | 22 ++ drivers/firmware/dmi-sysfs.c | 11 +-- drivers/firmware/dmi_scan.c| 90 -- include/linux/dmi.h| 1 + 5 files changed, 113 insertions(+), 13 deletions(-) rename Documentation/ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} (99%) create mode 100644 Documentation/ABI/testing/sysfs-firmware-dmi-tables -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch 3/3] Documentation: ABI: sysfs-firmware-dmi: add -entries suffix to file name
The dmi-sysfs module adds DMI table structures entries under /sys/firmware/dmi/entries only, so rename documentation file to sysfs-firmware-dmi-entries as more appropriate. Without renaming it's confusing to differ this from sysfs-firmware-dmi-tables that adds raw DMI table and actually adds "dmi" kobject. Signed-off-by: Ivan Khoronzhuk --- .../ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Documentation/ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} (99%) diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi b/Documentation/ABI/testing/sysfs-firmware-dmi-entries similarity index 99% rename from Documentation/ABI/testing/sysfs-firmware-dmi rename to Documentation/ABI/testing/sysfs-firmware-dmi-entries index c78f9ab..210ad44 100644 --- a/Documentation/ABI/testing/sysfs-firmware-dmi +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-entries @@ -1,4 +1,4 @@ -What: /sys/firmware/dmi/ +What: /sys/firmware/dmi/entries/ Date: February 2011 Contact: Mike Waychison Description: -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch 1/3] firmware: dmi_scan: rename dmi_table to dmi_decode_table
The "dmi_table" function looks like data instance, but it does DMI table decode. This patch renames it to "dmi_decode_table" name as more appropriate. That allows us to use "dmi_table" name for correct purposes. Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi_scan.c | 10 +- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index c9cb725..d3aae09 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -80,9 +80,9 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s) * We have to be cautious here. We have seen BIOSes with DMI pointers * pointing to completely the wrong place for example */ -static void dmi_table(u8 *buf, - void (*decode)(const struct dmi_header *, void *), - void *private_data) +static void dmi_decode_table(u8 *buf, +void (*decode)(const struct dmi_header *, void *), +void *private_data) { u8 *data = buf; int i = 0; @@ -128,7 +128,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, if (buf == NULL) return -1; - dmi_table(buf, decode, NULL); + dmi_decode_table(buf, decode, NULL); add_device_randomness(buf, dmi_len); @@ -906,7 +906,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *), if (buf == NULL) return -1; - dmi_table(buf, decode, private_data); + dmi_decode_table(buf, decode, private_data); dmi_unmap(buf); return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v2 0/3] firmware: dmi_scan: add SBMIOS entry point and DMI tables
This series adds SMBIOS entry point table and DMI table under /sys/firmware/dmi/tables in order to use as an alternative to utilities reading them from /dev/mem. Based on linux next + 2 patches that have not been sent by Jean Dalvere yet. They can be changed, but, anyway, the series should be based on smth and it's desirable w/o conflicts. The patches can be obtained with links: http://jdelvare.nerim.net/devel/linux-3/jdelvare-dmi/firmware-dmi-03-simplify-displayed-version.patch http://jdelvare.nerim.net/devel/linux-3/jdelvare-dmi/firmware-dmi-04-fix-product-uuid.patch Link on V1: https://lkml.org/lkml/2015/4/2/297 Changes since V1 - correct error path in dmi-sysfs - don't use globally dmi_table var - use "DMI" in attribute name - correct error path in dmi_init - leave dmi_kobj even in case of error - include linux/kobject.h in header Ivan Khoronzhuk (3): firmware: dmi_scan: rename dmi_table to dmi_decode_table firmware: dmi_scan: add SBMIOS entry and DMI tables Documentation: ABI: sysfs-firmware-dmi: add -entries suffix to file name ...sfs-firmware-dmi => sysfs-firmware-dmi-entries} | 2 +- .../ABI/testing/sysfs-firmware-dmi-tables | 22 ++ drivers/firmware/dmi-sysfs.c | 17 ++-- drivers/firmware/dmi_scan.c| 92 -- include/linux/dmi.h| 2 + 5 files changed, 120 insertions(+), 15 deletions(-) rename Documentation/ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} (99%) create mode 100644 Documentation/ABI/testing/sysfs-firmware-dmi-tables -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v2 2/3] firmware: dmi_scan: add SBMIOS entry and DMI tables
Some utils, like dmidecode and smbios, need to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry and adds code/delay redundancy when direct access for table is needed. So this patch creates dmi/tables and adds SMBIOS entry point to allow utils in question to work correctly without /dev/mem. Also patch adds raw dmi table to simplify dmi table processing in user space, as proposed by Jean Delvare. Signed-off-by: Ivan Khoronzhuk --- .../ABI/testing/sysfs-firmware-dmi-tables | 22 ++ drivers/firmware/dmi-sysfs.c | 17 +++-- drivers/firmware/dmi_scan.c| 82 ++ include/linux/dmi.h| 2 + 4 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-firmware-dmi-tables diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi-tables b/Documentation/ABI/testing/sysfs-firmware-dmi-tables new file mode 100644 index 000..ff3cac8 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-tables @@ -0,0 +1,22 @@ +What: /sys/firmware/dmi/tables/ +Date: April 2015 +Contact: Ivan Khoronzhuk +Description: + The firmware provides DMI structures as a packed list of + data referenced by a SMBIOS table entry point. The SMBIOS + entry point contains general information, like SMBIOS + version, DMI table size, etc. The structure, content and + size of SMBIOS entry point is dependent on SMBIOS version. + The format of SMBIOS entry point and DMI structures + can be read in SMBIOS specification. + + The dmi/tables provides raw SMBIOS entry point and DMI tables + through sysfs as an alternative to utilities reading them + from /dev/mem. The raw SMBIOS entry point and DMI table are + presented as binary attributes and are accessible via: + + /sys/firmware/dmi/tables/smbios_entry_point + /sys/firmware/dmi/tables/DMI + + The complete DMI information can be obtained using these two + tables. diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3..ef76e5e 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = { .default_attrs = dmi_sysfs_entry_attrs, }; -static struct kobject *dmi_kobj; static struct kset *dmi_kset; /* Global count of all instances seen. Only for setup */ @@ -648,17 +647,20 @@ static void cleanup_entry_list(void) static int __init dmi_sysfs_init(void) { - int error = -ENOMEM; + int error; int val; - /* Set up our directory */ - dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); - if (!dmi_kobj) + if (!dmi_kobj) { + pr_err("dmi-sysfs: dmi entry is absent.\n"); + error = -ENODATA; goto err; + } dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); - if (!dmi_kset) + if (!dmi_kset) { + error = -ENOMEM; goto err; + } val = 0; error = dmi_walk(dmi_sysfs_register_handle, &val); @@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void) err: cleanup_entry_list(); kset_unregister(dmi_kset); - kobject_put(dmi_kobj); return error; } @@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void) pr_debug("dmi-sysfs: unloading.\n"); cleanup_entry_list(); kset_unregister(dmi_kset); - kobject_del(dmi_kobj); - kobject_put(dmi_kobj); } module_init(dmi_sysfs_init); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index a864a6b..9705be2 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -10,6 +10,9 @@ #include #include +struct kobject *dmi_kobj; +EXPORT_SYMBOL_GPL(dmi_kobj); + /* * DMI stands for "Desktop Management Interface". It is part * of and an antecedent to, SMBIOS, which stands for System @@ -20,6 +23,9 @@ static const char dmi_empty_string[] = ""; static u32 dmi_ver __initdata; static u32 dmi_len; static u16 dmi_num; +static u8 smbios_entry_point[32]; +static int smbios_entry_point_size; + /* * Catch too early calls to dmi_check_system(): */ @@ -478,6 +484,8 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { smbios_ver = get_unaligned_be16(buf
[Patch v2 3/3] Documentation: ABI: sysfs-firmware-dmi: add -entries suffix to file name
The dmi-sysfs module adds DMI table structures entries under /sys/firmware/dmi/entries only, so rename documentation file to sysfs-firmware-dmi-entries as more appropriate. Without renaming it's confusing to differ this from sysfs-firmware-dmi-tables that adds raw DMI table and actually adds "dmi" kobject. Reviewed-by: Jean Delvare Signed-off-by: Ivan Khoronzhuk --- .../ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Documentation/ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} (99%) diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi b/Documentation/ABI/testing/sysfs-firmware-dmi-entries similarity index 99% rename from Documentation/ABI/testing/sysfs-firmware-dmi rename to Documentation/ABI/testing/sysfs-firmware-dmi-entries index c78f9ab..210ad44 100644 --- a/Documentation/ABI/testing/sysfs-firmware-dmi +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-entries @@ -1,4 +1,4 @@ -What: /sys/firmware/dmi/ +What: /sys/firmware/dmi/entries/ Date: February 2011 Contact: Mike Waychison Description: -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v2 1/3] firmware: dmi_scan: rename dmi_table to dmi_decode_table
The "dmi_table" function looks like data instance, but it does DMI table decode. This patch renames it to "dmi_decode_table" name as more appropriate. That allows us to use "dmi_table" name for correct purposes. Reviewed-by: Jean Delvare Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi_scan.c | 10 +- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 97b1616..a864a6b 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -80,9 +80,9 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s) * We have to be cautious here. We have seen BIOSes with DMI pointers * pointing to completely the wrong place for example */ -static void dmi_table(u8 *buf, - void (*decode)(const struct dmi_header *, void *), - void *private_data) +static void dmi_decode_table(u8 *buf, +void (*decode)(const struct dmi_header *, void *), +void *private_data) { u8 *data = buf; int i = 0; @@ -130,7 +130,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, if (buf == NULL) return -1; - dmi_table(buf, decode, NULL); + dmi_decode_table(buf, decode, NULL); add_device_randomness(buf, dmi_len); @@ -897,7 +897,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *), if (buf == NULL) return -1; - dmi_table(buf, decode, private_data); + dmi_decode_table(buf, decode, private_data); dmi_unmap(buf); return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v3 3/3] Documentation: ABI: sysfs-firmware-dmi: add -entries suffix to file name
The dmi-sysfs module adds DMI table structures entries under /sys/firmware/dmi/entries only, so rename documentation file to sysfs-firmware-dmi-entries as more appropriate. Without renaming it's confusing to differ this from sysfs-firmware-dmi-tables that adds raw DMI table and actually adds "dmi" kobject. Reviewed-by: Jean Delvare Signed-off-by: Ivan Khoronzhuk --- .../ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Documentation/ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} (99%) diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi b/Documentation/ABI/testing/sysfs-firmware-dmi-entries similarity index 99% rename from Documentation/ABI/testing/sysfs-firmware-dmi rename to Documentation/ABI/testing/sysfs-firmware-dmi-entries index c78f9ab..210ad44 100644 --- a/Documentation/ABI/testing/sysfs-firmware-dmi +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-entries @@ -1,4 +1,4 @@ -What: /sys/firmware/dmi/ +What: /sys/firmware/dmi/entries/ Date: February 2011 Contact: Mike Waychison Description: -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v3 1/3] firmware: dmi_scan: rename dmi_table to dmi_decode_table
The "dmi_table" function looks like data instance, but it does DMI table decode. This patch renames it to "dmi_decode_table" name as more appropriate. That allows us to use "dmi_table" name for correct purposes. Reviewed-by: Jean Delvare Signed-off-by: Ivan Khoronzhuk --- drivers/firmware/dmi_scan.c | 10 +- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 97b1616..a864a6b 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -80,9 +80,9 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s) * We have to be cautious here. We have seen BIOSes with DMI pointers * pointing to completely the wrong place for example */ -static void dmi_table(u8 *buf, - void (*decode)(const struct dmi_header *, void *), - void *private_data) +static void dmi_decode_table(u8 *buf, +void (*decode)(const struct dmi_header *, void *), +void *private_data) { u8 *data = buf; int i = 0; @@ -130,7 +130,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, if (buf == NULL) return -1; - dmi_table(buf, decode, NULL); + dmi_decode_table(buf, decode, NULL); add_device_randomness(buf, dmi_len); @@ -897,7 +897,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *), if (buf == NULL) return -1; - dmi_table(buf, decode, private_data); + dmi_decode_table(buf, decode, private_data); dmi_unmap(buf); return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[Patch v3 2/3] firmware: dmi_scan: add SBMIOS entry and DMI tables
Some utils, like dmidecode and smbios, need to access SMBIOS entry table area in order to get information like SMBIOS version, size, etc. Currently it's done via /dev/mem. But for situation when /dev/mem usage is disabled, the utils have to use dmi sysfs instead, which doesn't represent SMBIOS entry and adds code/delay redundancy when direct access for table is needed. So this patch creates dmi/tables and adds SMBIOS entry point to allow utils in question to work correctly without /dev/mem. Also patch adds raw dmi table to simplify dmi table processing in user space, as proposed by Jean Delvare. Tested-by: Roy Franz Signed-off-by: Ivan Khoronzhuk --- .../ABI/testing/sysfs-firmware-dmi-tables | 22 ++ drivers/firmware/dmi-sysfs.c | 17 +++-- drivers/firmware/dmi_scan.c| 78 ++ include/linux/dmi.h| 2 + 4 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-firmware-dmi-tables diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi-tables b/Documentation/ABI/testing/sysfs-firmware-dmi-tables new file mode 100644 index 000..ff3cac8 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-dmi-tables @@ -0,0 +1,22 @@ +What: /sys/firmware/dmi/tables/ +Date: April 2015 +Contact: Ivan Khoronzhuk +Description: + The firmware provides DMI structures as a packed list of + data referenced by a SMBIOS table entry point. The SMBIOS + entry point contains general information, like SMBIOS + version, DMI table size, etc. The structure, content and + size of SMBIOS entry point is dependent on SMBIOS version. + The format of SMBIOS entry point and DMI structures + can be read in SMBIOS specification. + + The dmi/tables provides raw SMBIOS entry point and DMI tables + through sysfs as an alternative to utilities reading them + from /dev/mem. The raw SMBIOS entry point and DMI table are + presented as binary attributes and are accessible via: + + /sys/firmware/dmi/tables/smbios_entry_point + /sys/firmware/dmi/tables/DMI + + The complete DMI information can be obtained using these two + tables. diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index e0f1cb3..ef76e5e 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = { .default_attrs = dmi_sysfs_entry_attrs, }; -static struct kobject *dmi_kobj; static struct kset *dmi_kset; /* Global count of all instances seen. Only for setup */ @@ -648,17 +647,20 @@ static void cleanup_entry_list(void) static int __init dmi_sysfs_init(void) { - int error = -ENOMEM; + int error; int val; - /* Set up our directory */ - dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); - if (!dmi_kobj) + if (!dmi_kobj) { + pr_err("dmi-sysfs: dmi entry is absent.\n"); + error = -ENODATA; goto err; + } dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); - if (!dmi_kset) + if (!dmi_kset) { + error = -ENOMEM; goto err; + } val = 0; error = dmi_walk(dmi_sysfs_register_handle, &val); @@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void) err: cleanup_entry_list(); kset_unregister(dmi_kset); - kobject_put(dmi_kobj); return error; } @@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void) pr_debug("dmi-sysfs: unloading.\n"); cleanup_entry_list(); kset_unregister(dmi_kset); - kobject_del(dmi_kobj); - kobject_put(dmi_kobj); } module_init(dmi_sysfs_init); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index a864a6b..389f8e9 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -10,6 +10,9 @@ #include #include +struct kobject *dmi_kobj; +EXPORT_SYMBOL_GPL(dmi_kobj); + /* * DMI stands for "Desktop Management Interface". It is part * of and an antecedent to, SMBIOS, which stands for System @@ -20,6 +23,9 @@ static const char dmi_empty_string[] = ""; static u32 dmi_ver __initdata; static u32 dmi_len; static u16 dmi_num; +static u8 smbios_entry_point[32]; +static int smbios_entry_point_size; + /* * Catch too early calls to dmi_check_system(): */ @@ -478,6 +484,8 @@ static int __init dmi_present(const u8 *buf) if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { smbios_ver = get_unaligned_be1
[Patch v3 0/3] firmware: dmi_scan: add SBMIOS entry point and DMI tables
This series adds SMBIOS entry point table and DMI table under /sys/firmware/dmi/tables in order to use as an alternative to utilities reading them from /dev/mem. Based on linux next + 2 patches from Jean Delvare. The patches can be obtained with links: http://jdelvare.nerim.net/devel/linux-3/jdelvare-dmi/firmware-dmi-03-simplify-displayed-version.patch http://jdelvare.nerim.net/devel/linux-3/jdelvare-dmi/firmware-dmi-04-fix-product-uuid.patch Link on V2: https://lkml.org/lkml/2015/4/20/218 Changes since V2 - use more labels in error path Changes since V1 - correct error path in dmi-sysfs - don't use globally dmi_table var - use "DMI" in attribute name - correct error path in dmi_init - leave dmi_kobj even in case of error - include linux/kobject.h in header Ivan Khoronzhuk (3): firmware: dmi_scan: rename dmi_table to dmi_decode_table firmware: dmi_scan: add SBMIOS entry and DMI tables Documentation: ABI: sysfs-firmware-dmi: add -entries suffix to file name ...sfs-firmware-dmi => sysfs-firmware-dmi-entries} | 2 +- .../ABI/testing/sysfs-firmware-dmi-tables | 22 ++ drivers/firmware/dmi-sysfs.c | 17 ++--- drivers/firmware/dmi_scan.c| 88 -- include/linux/dmi.h| 2 + 5 files changed, 116 insertions(+), 15 deletions(-) rename Documentation/ABI/testing/{sysfs-firmware-dmi => sysfs-firmware-dmi-entries} (99%) create mode 100644 Documentation/ABI/testing/sysfs-firmware-dmi-tables -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] net: ethernet: ti: cpsw: disable mq feature for "AM33xx ES1.0" devices
The early versions of am33xx devices, related to ES1.0 SoC revision have errata limiting mq support. That's the same errata as commit 7da1160002f1 ("drivers: net: cpsw: add am335x errata workarround for interrutps") AM33xx Errata [1] Advisory 1.0.9 http://www.ti.com/lit/er/sprz360f/sprz360f.pdf After additional investigation were found that drivers w/a is propagated on all AM33xx SoCs and on DM814x. But the errata exists only for ES1.0 of AM33xx family, limiting mq support for revisions after ES1.0. So, disable mq support only for related SoCs and use separate polls for revisions allowing mq. Signed-off-by: Ivan Khoronzhuk --- Based on net-next/master drivers/net/ethernet/ti/cpsw.c | 109 ++--- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 28d893b93d30..a7285dddfd29 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -957,7 +958,7 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) +static int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget) { u32 ch_map; int num_tx, cur_budget, ch; @@ -984,7 +985,21 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) if (num_tx < budget) { napi_complete(napi_tx); writel(0xff, &cpsw->wr_regs->tx_en); - if (cpsw->quirk_irq && cpsw->tx_irq_disabled) { + } + + return num_tx; +} + +static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) +{ + struct cpsw_common *cpsw = napi_to_cpsw(napi_tx); + int num_tx; + + num_tx = cpdma_chan_process(cpsw->txv[0].ch, budget); + if (num_tx < budget) { + napi_complete(napi_tx); + writel(0xff, &cpsw->wr_regs->tx_en); + if (cpsw->tx_irq_disabled) { cpsw->tx_irq_disabled = false; enable_irq(cpsw->irqs_table[1]); } @@ -993,7 +1008,7 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) return num_tx; } -static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) +static int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget) { u32 ch_map; int num_rx, cur_budget, ch; @@ -1020,7 +1035,21 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) if (num_rx < budget) { napi_complete_done(napi_rx, num_rx); writel(0xff, &cpsw->wr_regs->rx_en); - if (cpsw->quirk_irq && cpsw->rx_irq_disabled) { + } + + return num_rx; +} + +static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) +{ + struct cpsw_common *cpsw = napi_to_cpsw(napi_rx); + int num_rx; + + num_rx = cpdma_chan_process(cpsw->rxv[0].ch, budget); + if (num_rx < budget) { + napi_complete_done(napi_rx, num_rx); + writel(0xff, &cpsw->wr_regs->rx_en); + if (cpsw->rx_irq_disabled) { cpsw->rx_irq_disabled = false; enable_irq(cpsw->irqs_table[0]); } @@ -2364,9 +2393,9 @@ static void cpsw_get_channels(struct net_device *ndev, { struct cpsw_common *cpsw = ndev_to_cpsw(ndev); + ch->max_rx = cpsw->quirk_irq ? 1 : CPSW_MAX_QUEUES; + ch->max_tx = cpsw->quirk_irq ? 1 : CPSW_MAX_QUEUES; ch->max_combined = 0; - ch->max_rx = CPSW_MAX_QUEUES; - ch->max_tx = CPSW_MAX_QUEUES; ch->max_other = 0; ch->other_count = 0; ch->rx_count = cpsw->rx_ch_num; @@ -2377,6 +2406,11 @@ static void cpsw_get_channels(struct net_device *ndev, static int cpsw_check_ch_settings(struct cpsw_common *cpsw, struct ethtool_channels *ch) { + if (cpsw->quirk_irq) { + dev_err(cpsw->dev, "Maximum one tx/rx queue is allowed"); + return -EOPNOTSUPP; + } + if (ch->combined_count) return -EINVAL; @@ -2917,44 +2951,20 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv) return ret; } -#define CPSW_QUIRK_IRQ BIT(0) - -static const struct platform_device_id cpsw_devtype[] = { - { - /* keep it for existing comaptibles */ - .name = "cpsw", - .driver_data = CPSW_QUIRK_IRQ, - }, { - .name = "am335x-cpsw", - .driver_data = CPSW_QUIRK_IRQ, - }, { -
Re: [PATCH net-next 1/6] net: core: dev_addr_lists: add VID to device address
On Wed, Feb 27, 2019 at 08:24:00PM -0800, Florian Fainelli wrote: On 2/26/2019 10:45 AM, Ivan Khoronzhuk wrote: Despite this is supposed to be used for Ethernet VLANs, not Ethernet addresses with space for VID also can reuse this, so VID is considered as virtual ID extension, not belonging strictly to Ethernet VLAN VIDs, and overall change can be named individual virtual device filtering (IVDF). This patch adds VID tag at the end of each address. The actual reserved address size is 32 bytes. For Ethernet addresses with 6 bytes long that's possible to add tag w/o increasing address size. Thus, each address for the case has 32 - 6 = 26 bytes to hold additional info, say VID for virtual device addresses. Therefore, when addresses are synced to the address list of parent device the address list of latter can contain separate addresses for virtual devices. It allows to track separate address tables for virtual devices if they present and the device can be placed on any place of device tree as the address is propagated to to the end real device thru *_sync()/ndo_set_rx_mode() APIs. Also it simplifies handling VID addresses at real device when it supports IVDF. If parent device doesn't want to have virtual addresses in its address space the vid_len has to be 0, thus its address space is "shrunk" to the state as before this patch. For now it's 0 for every device. It allows two devices with and w/o IVDF to be part of same bond device for instance. The end real device supporting IVDF can retrieve VID tag from an address and set it for a given virtual device only. By default, vid 0 is used for real devices to distinguish it from virtual addresses. See next patches to see how it's used. Signed-off-by: Ivan Khoronzhuk --- [snip] @@ -1889,6 +1890,7 @@ struct net_device { unsigned char perm_addr[MAX_ADDR_LEN]; unsigned char addr_assign_type; unsigned char addr_len; + unsigned char vid_len; Have not compiled or tested this patch series yet, but did you check that adding this member does not change the structure layout (you can use pahole for that purpose). For ARM 32, on 1 hole less: --- before (https://pastebin.com/DG1SVpFR): /* size: 1344, cachelines: 21, members: 123 */ /* sum members: 1304, holes: 5, sum holes: 28 */ /* padding: 12 */ /* bit_padding: 31 bits */ after (https://pastebin.com/ZUMhxGkA): /* size: 1344, cachelines: 21, members: 124 */ /* sum members: 1305, holes: 5, sum holes: 27 */ /* padding: 12 */ /* bit_padding: 31 bits */ For ARM 64, on 1 hole less: --- before (https://pastebin.com/5CdTQWkc): /* size: 2048, cachelines: 32, members: 120 */ /* sum members: 1972, holes: 7, sum holes: 48 */ /* padding: 28 */ /* bit_padding: 31 bits */ after (https://pastebin.com/32ktb1iV): /* size: 2048, cachelines: 32, members: 121 */ /* sum members: 1973, holes: 7, sum holes: 47 */ /* padding: 28 */ /* bit_padding: 31 bits */ Looks Ok, but it depends on configuration ... unsigned short neigh_priv_len; unsigned short dev_id; unsigned short dev_port; @@ -4141,8 +4143,10 @@ int dev_addr_init(struct net_device *dev); /* Functions used for unicast addresses handling */ int dev_uc_add(struct net_device *dev, const unsigned char *addr); +int dev_vid_uc_add(struct net_device *dev, const unsigned char *addr); int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); int dev_uc_del(struct net_device *dev, const unsigned char *addr); +int dev_vid_uc_del(struct net_device *dev, const unsigned char *addr); int dev_uc_sync(struct net_device *to, struct net_device *from); int dev_uc_sync_multiple(struct net_device *to, struct net_device *from); void dev_uc_unsync(struct net_device *to, struct net_device *from); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index a6723b306717..e3c80e044b8c 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -545,6 +545,26 @@ int dev_addr_del(struct net_device *dev, const unsigned char *addr, } EXPORT_SYMBOL(dev_addr_del); +static int get_addr_len(struct net_device *dev) +{ + return dev->addr_len + dev->vid_len; +} + +static int set_vid_addr(struct net_device *dev, const unsigned char *addr, + unsigned char *naddr) Having some kernel doc comments here would be nice to indicate that the return value is dev->addr_len, it was not obvious until I saw in the next function how you used it. Agree +{ + int i; + + if (!dev->vid_len) + return dev->addr_len; + + memcpy(naddr, addr, dev->addr_len); + for (i = 0; i < dev->vid_len; i++) + naddr[dev->addr_len + i] = 0; memset(naddr + dev->addr_len, 0, dev->vid_len) would be more compact and maybe a little less error prone too? Yes, would be -- Regards, Ivan Khoronzhuk
Re: [PATCH net-next 2/6] net: 8021q: vlan_dev: add vid tag to addresses of uc and mc lists
On Wed, Feb 27, 2019 at 08:09:44PM -0800, Florian Fainelli wrote: On 2/26/2019 10:45 AM, Ivan Khoronzhuk wrote: Update vlan mc and uc addresses with VID tag while propagating addresses to lower devices, do this only if address is not synced. It allows at end driver level to distinguish addresses belonging to vlan devices. Signed-off-by: Ivan Khoronzhuk --- [snip] +u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr) Having some kernel doc comment here would also be nice. yes can be: vlan_dev_get_addr_vid - get vlan id the address belongs to +{ + u16 vid = 0; + + if (dev->vid_len != NET_8021Q_VID_TSIZE) + return vid; + + vid = addr[dev->addr_len]; + vid |= (addr[dev->addr_len + 1] & 0xf) << 8; This uses knowledge of the maximum VLAN ID is 4095, which is fine, might be a good idea to add a check on VID not exceeding the maximum VLAN ID number instead of doing a silent truncation? and then return -1, not sure, just because it's 0 or directly set by vlan layer and is verified anyway. But no harm to verify even it looks like redundancy. [snip] +static void vlan_dev_align_addr_vid(struct net_device *vlan_dev) +{ + struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); + struct netdev_hw_addr *ha; + + if (!real_dev->vid_len) + return; Should not this check be moved to dev_{mc,uc}_sync? It does not seem to me like this would scale really well across different stacked devices (VLAN, bond, macvlan) as well as underlying drivers (cpsw, dsa, etc.). Or maybe the check should be if vlan_dev->vid_len > real_dev->vid_len -> error, right? It shouldn't be part of netdev addr module, no any vlan_dev_vlan_id(vlan_dev) should be there. It's scaled becouse bond/team ...etc, are ethernet devices and have IVDF enabled while configuration. Address propagation always is from leafs to real root device, every underneeth device knows nothing about above, so check is only in one direction. -- Regards, Ivan Khoronzhuk
Re: [PATCH net-next 3/6] net: 8021q: vlan_dev: add vid tag for vlan device own address
On Wed, Feb 27, 2019 at 08:13:34PM -0800, Florian Fainelli wrote: On 2/26/2019 10:45 AM, Ivan Khoronzhuk wrote: The vlan device address is held separately from uc/mc lists and handled differently. The vlan dev address is bound with real device address only if it's inherited from init, in all other cases it's separate address entry in uc list. With vid set, the address becomes not inherited from real device after it's set manually as before, but is part of uc list any way, with appropriate vid tag set. If vid_len for real device is 0, the behaviour is the same as before this change, so shouldn't be any impact on systems w/o individual virtual device filtering (IVDF) enabled. This allows to control and sync vlan device address and disable concrete vlan packet income when vlan interface is down. Signed-off-by: Ivan Khoronzhuk --- [snip] +static int vlan_dev_add_addr(struct net_device *dev, u8 *addr) +{ + struct net_device *real_dev = vlan_dev_real_dev(dev); + unsigned char naddr[ETH_ALEN + NET_8021Q_VID_TSIZE]; + + if (real_dev->vid_len) { Don't you need to check that real_dev->vid_len is >= NET_8021Q_VID_TSIZE here? vid_len for all eth devices or 0 or NET_8021Q_VID_TSIZE and used here only as a flag that different addressing scheme is used. vlan_dev_set_addr_vid() do copy only < NET_8021Q_VID_TSIZE anyway. Can add the following to be sure: if (real_dev->vid_len) { if (real_dev->vid_len != NET_8021Q_VID_TSIZE) return -1; } But frankly, if this happens the system is ill and this check can't help it. + memcpy(naddr, addr, dev->addr_len); + vlan_dev_set_addr_vid(dev, naddr); + return dev_vid_uc_add(real_dev, naddr); + } + + if (ether_addr_equal(addr, real_dev->dev_addr)) + return 0; + + return dev_uc_add(real_dev, addr); +} + +static void vlan_dev_del_addr(struct net_device *dev, u8 *addr) +{ + struct net_device *real_dev = vlan_dev_real_dev(dev); + unsigned char naddr[ETH_ALEN + NET_8021Q_VID_TSIZE]; + + if (real_dev->vid_len) { Same here. Not same, it's void routine. And del can't happen w/o add, no reason. + memcpy(naddr, addr, dev->addr_len); + vlan_dev_set_addr_vid(dev, naddr); + dev_vid_uc_del(real_dev, naddr); + return; + } + + if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) + dev_uc_del(real_dev, addr); +} + +static int vlan_dev_subs_addr(struct net_device *dev, u8 *addr) +{ + int err; + + err = vlan_dev_add_addr(dev, addr); + if (err < 0) + return err; + + vlan_dev_del_addr(dev, dev->dev_addr); + return err; +} + bool vlan_dev_inherit_address(struct net_device *dev, struct net_device *real_dev) { if (dev->addr_assign_type != NET_ADDR_STOLEN) return false; + if (real_dev->vid_len) + if (vlan_dev_subs_addr(dev, real_dev->dev_addr)) + return false; The check on real_dev->vid_len can be absorbed into vlan_dev_subs_addr()? No, I'd tried. vlan_dev_subs_addr() is used not only here and move it under makes more not combined code. + ether_addr_copy(dev->dev_addr, real_dev->dev_addr); call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); return true; @@ -278,9 +327,10 @@ static int vlan_dev_open(struct net_device *dev) !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) return -ENETDOWN; - if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) && - !vlan_dev_inherit_address(dev, real_dev)) { - err = dev_uc_add(real_dev, dev->dev_addr); + if (ether_addr_equal(dev->dev_addr, real_dev->dev_addr) || + (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) && +!vlan_dev_inherit_address(dev, real_dev))) { Should this condition simply become if !vlan_dev_inherit_address() now? It can't, I'd tried. vlan_dev_inherit_address() is used in (vlan.c): vlan_sync_address(struct net_device *dev, struct net_device *vlandev); -- Regards, Ivan Khoronzhuk
Re: [PATCH net-next 4/6] ethernet: eth: add default vid len for all ehternet kind devices
On Wed, Feb 27, 2019 at 08:29:20PM -0800, Florian Fainelli wrote: On 2/26/2019 10:45 AM, Ivan Khoronzhuk wrote: IVDF - individual virtual device filtering. Allows to set per vlan l2 address filters on end real network device (for unicast and for multicast) and drop redundant not expected packet income. If CONFIG_VLAN_8021Q_IVDF is enabled the following changes are applied, and only for ethernet network devices. By default every ethernet netdev needs vid len = 2 bytes to be able to hold up to 4096 vids. So set it for every eth device to be correct, except vlan devs. In order to shrink all addresses of devices above vlan, the vid_len for vlan dev = 0, as result all suckers sync their addresses to common base not taking in to account vid part (vid_len of "to" devices is important only). And only vlan device is the source of addresses with actual its vid set, propagating it to parent devices while rx_mode(). Also, don't bother those ethernet devices that at this moment are not moved to vlan addressing scheme, so while end ethernet device is created - set vid_len to 0, thus, while syncing, its address space is concatenated to one dimensional like usual, and who needs IVDF - set it to NET_8021Q_VID_TSIZE. There is another decision - is to inherit vid_len or some feature flag from end root device in order to all upper devices have vlan extended address space only if exact end real device have such capability. But I didn't, because it requires more changes and probably I'm not familiar with all places where it should be inherited, I would appreciate if someone can guid where it's applicable, then it could become a little bit more limited. I would think that a call to vlan_dev_ivdf_set() would be enough to indicate that the underlying network device driver supports IVDF and wants to make use of it. The infrastructure itself that you added costs little memory, it is once the call to vlan_dev_ivdf_set() is made that the memory consumption increases which is fine, since we want to make use of that feature. While I appreciate the thoughts given to making this a configurable option, I feel that enabling it unconditionally and having the underlying driver decide would be more manageable. Not exactly. Even system has no driver calling vlan_dev_ivdf_set() I still has this "mem consumption" from the very beginning. That's why I made this depended on the driver and CONF. For embedded world it looks fine. The issues is that I can't change addressing scheme dynamically since some drivers and infrastructure that exists before I called vlan_dev_ivdf_set() can already have some synced addresses using old scheme. To do this dynamically it needs unsync vlans from old scheme and make sync again. Probably that is topic to "sync" :-| about I considered idea making "above infrastructure" IVDF to be dependent on underneath end device IVDF and remove this config at all, but here several issues with this, the infrastructure has to be "resynced" and some signal has to inform each vlan to do this, and while this happens, all end devices already configured and not supported IVDF shouldn't suffer. I can try but it looks not doable in normal way, and appreciate any thoughts about this. Meanwhile, this option looks fine for small embedded paltforms. We have had that conversation before, but let me ask again when we call dev_{uc,mc}_sync() and ultimately the network device's ndo_set_rx_mode(), by the time the ndo_set_rx_mode() function is called, we lost track of the call chain, like which virtual device was it originating from. If we somehow added a notification information about the network device stack (and we could use netdevice notifiers for that), then maybe we don't really need to add all of this code and we can just derive the necessary bits of information we want by checking: is this a VLAN network device? It is, okay what's your VLAN ID, etc.? Either approach would get us our cookie anyway :) Postulate here is that address of vlan device is separate from netdevice entity with it's own context. Several cases talking about this: - bound device having 2 slaves can have added vid to both slave devices but synced addresses for only one of them. So, if vid is set in real device it doesn't mean it needs addresses of vlan device. - I know that's crazy, but net device tree can contain 2 same vlan devices ). The scheme doesn't prevent this case. So one vid address can be counted by two vlan network devices. - Any of the devices in _sync/rx_mode() chain is legal to do with addresses what ever it's allowed to do, drop some of them, combine with others and more, even add it's own vid addresses w/o actual vlan network device. These made me to look in side of building rx_sync netdevice tree holding links on nodes per each address. And I've did this mostly...then after short loo
[PATCH net-next 4/6] ethernet: eth: add default vid len for all ehternet kind devices
IVDF - individual virtual device filtering. Allows to set per vlan l2 address filters on end real network device (for unicast and for multicast) and drop redundant not expected packet income. If CONFIG_VLAN_8021Q_IVDF is enabled the following changes are applied, and only for ethernet network devices. By default every ethernet netdev needs vid len = 2 bytes to be able to hold up to 4096 vids. So set it for every eth device to be correct, except vlan devs. In order to shrink all addresses of devices above vlan, the vid_len for vlan dev = 0, as result all suckers sync their addresses to common base not taking in to account vid part (vid_len of "to" devices is important only). And only vlan device is the source of addresses with actual its vid set, propagating it to parent devices while rx_mode(). Also, don't bother those ethernet devices that at this moment are not moved to vlan addressing scheme, so while end ethernet device is created - set vid_len to 0, thus, while syncing, its address space is concatenated to one dimensional like usual, and who needs IVDF - set it to NET_8021Q_VID_TSIZE. There is another decision - is to inherit vid_len or some feature flag from end root device in order to all upper devices have vlan extended address space only if exact end real device have such capability. But I didn't, because it requires more changes and probably I'm not familiar with all places where it should be inherited, I would appreciate if someone can guid where it's applicable, then it could become a little bit more limited. Signed-off-by: Ivan Khoronzhuk --- include/linux/if_vlan.h | 1 + net/8021q/Kconfig | 12 net/8021q/vlan_core.c | 12 net/8021q/vlan_dev.c| 1 + net/ethernet/eth.c | 10 -- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 94657f3c483a..9c914b31d208 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -137,6 +137,7 @@ extern int vlan_for_each(struct net_device *dev, int (*action)(struct net_device *dev, int vid, void *arg), void *arg); extern u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr); +extern void vlan_dev_ivdf_set(struct net_device *dev, int enable); extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); extern u16 vlan_dev_vlan_id(const struct net_device *dev); extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); diff --git a/net/8021q/Kconfig b/net/8021q/Kconfig index 42320180967f..3e843045739c 100644 --- a/net/8021q/Kconfig +++ b/net/8021q/Kconfig @@ -38,3 +38,15 @@ config VLAN_8021Q_MVRP supersedes GVRP and is not backwards-compatible. If unsure, say N. + +config VLAN_8021Q_IVDF + bool "IVDF (Individual Virtual Device Filtering) support" + depends on VLAN_8021Q + help + Select this to enable IVDF addressing scheme support. IVDF is used + for automatic propagation of registered VLANs addresses to real end + devices. If no device supporting IVDF then disable this as it can + consume some memory in configuration with complex network device + structures to hold vlan addresses. + + If unsure, say N. diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index fe2ac64c13f8..310b6cd39f22 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -454,6 +454,18 @@ bool vlan_uses_dev(const struct net_device *dev) } EXPORT_SYMBOL(vlan_uses_dev); +void vlan_dev_ivdf_set(struct net_device *dev, int enable) +{ +#ifdef CONFIG_VLAN_8021Q_IVDF + if (enable) { + dev->vid_len = NET_8021Q_VID_TSIZE; + return; + } +#endif + dev->vid_len = 0; +} +EXPORT_SYMBOL(vlan_dev_ivdf_set); + u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr) { u16 vid = 0; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 634436e780f1..e4120aca4b9b 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -896,5 +896,6 @@ void vlan_setup(struct net_device *dev) dev->min_mtu= 0; dev->max_mtu= ETH_MAX_MTU; + vlan_dev_ivdf_set(dev, 0); eth_zero_addr(dev->broadcast); } diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index f7a3d7a171c7..95497cac24eb 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -381,6 +381,7 @@ void ether_setup(struct net_device *dev) dev->flags = IFF_BROADCAST|IFF_MULTICAST; dev->priv_flags |= IFF_TX_SKB_SHARING; + vlan_dev_ivdf_set(dev, 1); eth_broadcast_addr(dev->broadcast); } @@ -404,8 +405,13 @@ EXPORT_SYMBOL(ether_setup); struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, unsigned int rxqs) { -
[PATCH net-next 6/6] net: ethernet: ti: cpsw: add macvlan and ucast/vlan filtering support
The cpsw supports unicast filtering as for real as for vlan devices now, but has no flag set for that. As result, once macvlan or vlan adds new ucast address the cpsw is silently toggled to promiscuous mode. That's smth not expected, so patch fixes it. A unicast address for vlan has to be presented by vlan/unicast entry in ALE table. At this moment, while vlan address change, entry is not created in any form, even just like real device unicast used for macvlan, leaving only address inherited from real device created while vlan addition. Therefore, program unicast entries for vlans by using IVDF, it allows to add only vlan/unicast entries for vlans, omitting real device ucast entries unless they are added for macvans or so, as they are redundant for vlans and just consume forwarding table and in case of matching packet income - CPU time. So, after this patch, cpsw has ability to handle macvlan and vlan ucasts, synchronizing ucast tables for these devices with cpsw ALE table exclusively. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/cpsw.c | 62 ++ 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index fd76d1f12911..c6d5ddc05299 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -693,6 +693,31 @@ static int cpsw_set_mc(struct net_device *ndev, const u8 *addr, return ret; } +static int cpsw_set_uc(struct net_device *ndev, const u8 *addr, + int vid, int add) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + int flags, port, ret; + + if (vid < 0) { + if (cpsw->data.dual_emac) + vid = cpsw->slaves[priv->emac_port].port_vlan; + else + vid = 0; + } + + port = HOST_PORT_NUM; + flags = vid ? ALE_VLAN : 0; + + if (add) + ret = cpsw_ale_add_ucast(cpsw->ale, addr, port, flags, vid); + else + ret = cpsw_ale_del_ucast(cpsw->ale, addr, port, flags, vid); + + return ret; +} + static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr) { u16 vid; @@ -711,6 +736,24 @@ static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr) return 0; } +static int cpsw_add_uc_addr(struct net_device *ndev, const u8 *addr) +{ + u16 vid; + + vid = vlan_dev_get_addr_vid(ndev, addr); + cpsw_set_uc(ndev, addr, vid ? vid : -1, 1); + return 0; +} + +static int cpsw_del_uc_addr(struct net_device *ndev, const u8 *addr) +{ + u16 vid; + + vid = vlan_dev_get_addr_vid(ndev, addr); + cpsw_set_uc(ndev, addr, vid ? vid : -1, 0); + return 0; +} + static void cpsw_ndo_set_rx_mode(struct net_device *ndev) { struct cpsw_common *cpsw = ndev_to_cpsw(ndev); @@ -730,6 +773,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) /* add/remove mcast address either for real netdev or for vlan */ __dev_mc_sync(ndev, cpsw_add_mc_addr, cpsw_del_mc_addr); + __dev_uc_sync(ndev, cpsw_add_uc_addr, cpsw_del_uc_addr); } static void cpsw_intr_enable(struct cpsw_common *cpsw) @@ -2009,6 +2053,7 @@ static int cpsw_ndo_stop(struct net_device *ndev) cpsw_info(priv, ifdown, "shutting down cpsw device\n"); __dev_mc_unsync(ndev, cpsw_del_mc_addr); + __dev_uc_unsync(ndev, cpsw_del_uc_addr); netif_tx_stop_all_queues(priv->ndev); netif_carrier_off(priv->ndev); @@ -2369,10 +2414,12 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, if (ret != 0) return ret; - ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, -HOST_PORT_NUM, ALE_VLAN, vid); - if (ret != 0) - goto clean_vid; + if (!priv->ndev->vid_len) { + ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, +HOST_PORT_NUM, ALE_VLAN, vid); + if (ret != 0) + goto clean_vid; + } ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, mcast_mask, ALE_VLAN, vid, 0); @@ -2381,8 +2428,9 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, return 0; clean_vlan_ucast: - cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, - HOST_PORT_NUM, ALE_VLAN, vid); + if (!priv->ndev->vid_len) + cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, + HOST_PORT_NUM, ALE_VLAN, vid); clean_vid: cpsw_ale_del_vlan(cpsw->ale, vid, 0); return ret; @@ -3344,6 +3392,7 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
[PATCH net-next 5/6] net: ethernet: ti: cpsw: update mc filtering to use IVDF
The cpsw can filter multicast addresses only per vlan. Thus if mcast address is set for one of them or only for real device it must be added for every created vlan consuming ALE table w/o reason. In order to simplify dispatching vlan filters, the IVDF recently added is resused. In case IVDF is disabled - mc is updated only for real device as before. The previous method is harder to reuse and vlan filtering is limited only for vlans directly connected to real netdev, so drop it in flavor of IVDF decision. Signed-off-by: Ivan Khoronzhuk --- drivers/net/ethernet/ti/Kconfig | 1 + drivers/net/ethernet/ti/cpsw.c | 113 2 files changed, 13 insertions(+), 101 deletions(-) diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index bb126be1eb72..c99c08ece9a1 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -65,6 +65,7 @@ config TI_CPSW select TI_DAVINCI_CPDMA select TI_DAVINCI_MDIO select TI_CPSW_PHY_SEL + select VLAN_8021Q_IVDF select TI_CPSW_ALE select MFD_SYSCON select REGMAP diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a591583d120e..fd76d1f12911 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -693,108 +693,21 @@ static int cpsw_set_mc(struct net_device *ndev, const u8 *addr, return ret; } -static int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx) +static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr) { - struct addr_sync_ctx *sync_ctx = ctx; - struct netdev_hw_addr *ha; - int found = 0, ret = 0; - - if (!vdev || !(vdev->flags & IFF_UP)) - return 0; - - /* vlan address is relevant if its sync_cnt != 0 */ - netdev_for_each_mc_addr(ha, vdev) { - if (ether_addr_equal(ha->addr, sync_ctx->addr)) { - found = ha->sync_cnt; - break; - } - } - - if (found) - sync_ctx->consumed++; - - if (sync_ctx->flush) { - if (!found) - cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0); - return 0; - } - - if (found) - ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1); - - return ret; -} - -static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num) -{ - struct addr_sync_ctx sync_ctx; - int ret; - - sync_ctx.consumed = 0; - sync_ctx.addr = addr; - sync_ctx.ndev = ndev; - sync_ctx.flush = 0; - - ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx); - if (sync_ctx.consumed < num && !ret) - ret = cpsw_set_mc(ndev, addr, -1, 1); - - return ret; -} - -static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num) -{ - struct addr_sync_ctx sync_ctx; - - sync_ctx.consumed = 0; - sync_ctx.addr = addr; - sync_ctx.ndev = ndev; - sync_ctx.flush = 1; - - vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx); - if (sync_ctx.consumed == num) - cpsw_set_mc(ndev, addr, -1, 0); + u16 vid; + vid = vlan_dev_get_addr_vid(ndev, addr); + cpsw_set_mc(ndev, addr, vid ? vid : -1, 1); return 0; } -static int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx) +static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr) { - struct addr_sync_ctx *sync_ctx = ctx; - struct netdev_hw_addr *ha; - int found = 0; - - if (!vdev || !(vdev->flags & IFF_UP)) - return 0; - - /* vlan address is relevant if its sync_cnt != 0 */ - netdev_for_each_mc_addr(ha, vdev) { - if (ether_addr_equal(ha->addr, sync_ctx->addr)) { - found = ha->sync_cnt; - break; - } - } - - if (!found) - return 0; - - sync_ctx->consumed++; - cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0); - return 0; -} - -static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num) -{ - struct addr_sync_ctx sync_ctx; - - sync_ctx.addr = addr; - sync_ctx.ndev = ndev; - sync_ctx.consumed = 0; - - vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx); - if (sync_ctx.consumed < num) - cpsw_set_mc(ndev, addr, -1, 0); + u16 vid; + vid = vlan_dev_get_addr_vid(ndev, addr); + cpsw_set_mc(ndev, addr, vid ? vid : -1, 0); return 0; } @@ -816,8 +729,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) cpsw_ale_set_allmulti(cpsw->ale, ndev->flags & IFF_ALLMULTI); /* add/remove mcast address either
[PATCH net-next 1/6] net: core: dev_addr_lists: add VID to device address
Despite this is supposed to be used for Ethernet VLANs, not Ethernet addresses with space for VID also can reuse this, so VID is considered as virtual ID extension, not belonging strictly to Ethernet VLAN VIDs, and overall change can be named individual virtual device filtering (IVDF). This patch adds VID tag at the end of each address. The actual reserved address size is 32 bytes. For Ethernet addresses with 6 bytes long that's possible to add tag w/o increasing address size. Thus, each address for the case has 32 - 6 = 26 bytes to hold additional info, say VID for virtual device addresses. Therefore, when addresses are synced to the address list of parent device the address list of latter can contain separate addresses for virtual devices. It allows to track separate address tables for virtual devices if they present and the device can be placed on any place of device tree as the address is propagated to to the end real device thru *_sync()/ndo_set_rx_mode() APIs. Also it simplifies handling VID addresses at real device when it supports IVDF. If parent device doesn't want to have virtual addresses in its address space the vid_len has to be 0, thus its address space is "shrunk" to the state as before this patch. For now it's 0 for every device. It allows two devices with and w/o IVDF to be part of same bond device for instance. The end real device supporting IVDF can retrieve VID tag from an address and set it for a given virtual device only. By default, vid 0 is used for real devices to distinguish it from virtual addresses. See next patches to see how it's used. Signed-off-by: Ivan Khoronzhuk --- include/linux/netdevice.h | 4 ++ net/core/dev_addr_lists.c | 124 +++--- 2 files changed, 105 insertions(+), 23 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 58e83bd7a861..74fef35b6bec 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1660,6 +1660,7 @@ enum netdev_priv_flags { * @perm_addr: Permanent hw address * @addr_assign_type: Hw address assignment type * @addr_len: Hardware address length + * @vid_len: Virtual ID length, set in case of IVDF * @neigh_priv_len:Used in neigh_alloc() * @dev_id:Used to differentiate devices that share * the same link layer address @@ -1889,6 +1890,7 @@ struct net_device { unsigned char perm_addr[MAX_ADDR_LEN]; unsigned char addr_assign_type; unsigned char addr_len; + unsigned char vid_len; unsigned short neigh_priv_len; unsigned short dev_id; unsigned short dev_port; @@ -4141,8 +4143,10 @@ int dev_addr_init(struct net_device *dev); /* Functions used for unicast addresses handling */ int dev_uc_add(struct net_device *dev, const unsigned char *addr); +int dev_vid_uc_add(struct net_device *dev, const unsigned char *addr); int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); int dev_uc_del(struct net_device *dev, const unsigned char *addr); +int dev_vid_uc_del(struct net_device *dev, const unsigned char *addr); int dev_uc_sync(struct net_device *to, struct net_device *from); int dev_uc_sync_multiple(struct net_device *to, struct net_device *from); void dev_uc_unsync(struct net_device *to, struct net_device *from); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index a6723b306717..e3c80e044b8c 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -545,6 +545,26 @@ int dev_addr_del(struct net_device *dev, const unsigned char *addr, } EXPORT_SYMBOL(dev_addr_del); +static int get_addr_len(struct net_device *dev) +{ + return dev->addr_len + dev->vid_len; +} + +static int set_vid_addr(struct net_device *dev, const unsigned char *addr, + unsigned char *naddr) +{ + int i; + + if (!dev->vid_len) + return dev->addr_len; + + memcpy(naddr, addr, dev->addr_len); + for (i = 0; i < dev->vid_len; i++) + naddr[dev->addr_len + i] = 0; + + return get_addr_len(dev); +} + /* * Unicast list handling functions */ @@ -556,18 +576,22 @@ EXPORT_SYMBOL(dev_addr_del); */ int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr) { + unsigned char naddr[MAX_ADDR_LEN]; struct netdev_hw_addr *ha; - int err; + int addr_len, err; + + addr_len = set_vid_addr(dev, addr, naddr); + addr = dev->vid_len ? naddr : addr; netif_addr_lock_bh(dev); list_for_each_entry(ha, &dev->uc.list, list) { - if (!memcmp(ha->addr, addr, dev->addr_len) && + if (!memcmp(ha->addr, addr, addr_len) && ha->type == NET
[PATCH net-next 0/6] net: add individual virtual device filtering
One of the reasons of this proposition is safety and performance - host should not receive traffic which is not designated for it. Some network devices can hold separate address tables for vlans and real device, but for some reason there is no possibility to apply it with generic net addressing scheme easily. At this moment the fastest solution is to add mcast/ucast entries for every created vlan including real device. But it not only consumes forwarding table but also adds holes in the filtering and thus wastes cpus cycles. This patchseries tries to correct core to assign mcast and ucast addresses only for vlans that really require it and as result an end driver can exclusively and simply set its rx filters. As an example it's implemented on cpsw TI driver, but generic changes provided by this series can be reused by other ethernet drivers having similar rx filter address possibilities. An address+vid is considered as separate address. The reserved device address length is 32 Bytes, for ethernet devices it's additional opportunity to pass auxiliary address info, like virtual ID identifying a device the address belongs to. This series makes it possible at least for ETH_P_8021Q. Thus end real device can setup separate tables for virtual devices just retrieving VID from the address. A device address space can maintain addresses and references on them separately for each virtual device if it needs so, or only addresses for real device (and all its vlans) it holds usually. A vlan device can be in any place of device chain upper real device, say smth like rdevice/bonding/vlan or even rdevice/macvlan/vlan. This series is verified on TI am572x EVM that can hold separate tables for vlans. Potentially it can be easily extended to netcp driver for keystone 2 boards (including k2g) and also new am6 chipsets. As a simple test case, different combinations of vlan+macvlan, macvlan+vlan were used and tested as with unicast as multicast addresses. Based on net-next/master It's continuation of RFC: [RFC PATCH net-next 0/5] net: allow hw addresses for virtual device https://lkml.org/lkml/2018/12/3/817 Ivan Khoronzhuk (6): net: core: dev_addr_lists: add VID to device address net: 8021q: vlan_dev: add vid tag to addresses of uc and mc lists net: 8021q: vlan_dev: add vid tag for vlan device own address ethernet: eth: add default vid len for all ehternet kind devices net: ethernet: ti: cpsw: update mc filtering to use IVDF net: ethernet: ti: cpsw: add macvlan and ucast/vlan filtering support drivers/net/ethernet/ti/Kconfig | 1 + drivers/net/ethernet/ti/cpsw.c | 139 include/linux/if_vlan.h | 2 + include/linux/netdevice.h | 4 + net/8021q/Kconfig | 12 +++ net/8021q/vlan.c| 3 + net/8021q/vlan.h| 2 + net/8021q/vlan_core.c | 25 ++ net/8021q/vlan_dev.c| 103 ++- net/core/dev_addr_lists.c | 124 ++-- net/ethernet/eth.c | 10 ++- 11 files changed, 292 insertions(+), 133 deletions(-) -- 2.17.1
[PATCH net-next 2/6] net: 8021q: vlan_dev: add vid tag to addresses of uc and mc lists
Update vlan mc and uc addresses with VID tag while propagating addresses to lower devices, do this only if address is not synced. It allows at end driver level to distinguish addresses belonging to vlan devices. Signed-off-by: Ivan Khoronzhuk --- include/linux/if_vlan.h | 1 + net/8021q/vlan.h| 2 ++ net/8021q/vlan_core.c | 13 + net/8021q/vlan_dev.c| 26 ++ 4 files changed, 42 insertions(+) diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 4cca4da7a6de..94657f3c483a 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -136,6 +136,7 @@ extern struct net_device *__vlan_find_dev_deep_rcu(struct net_device *real_dev, extern int vlan_for_each(struct net_device *dev, int (*action)(struct net_device *dev, int vid, void *arg), void *arg); +extern u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr); extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); extern u16 vlan_dev_vlan_id(const struct net_device *dev); extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index c46daf09a501..f083c43c508f 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -6,6 +6,8 @@ #include #include +#define NET_8021Q_VID_TSIZE2 + /* if this changes, algorithm will have to be reworked because this * depends on completely exhausting the VLAN identifier space. Thus * it gives constant time look-up, but in many cases it wastes memory. diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index a313165e7a67..fe2ac64c13f8 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -454,6 +454,19 @@ bool vlan_uses_dev(const struct net_device *dev) } EXPORT_SYMBOL(vlan_uses_dev); +u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr) +{ + u16 vid = 0; + + if (dev->vid_len != NET_8021Q_VID_TSIZE) + return vid; + + vid = addr[dev->addr_len]; + vid |= (addr[dev->addr_len + 1] & 0xf) << 8; + return vid; +} +EXPORT_SYMBOL(vlan_dev_get_addr_vid); + static struct sk_buff *vlan_gro_receive(struct list_head *head, struct sk_buff *skb) { diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 15293c2a5dd8..93d20b1f4916 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -249,6 +249,14 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23); } +static void vlan_dev_set_addr_vid(struct net_device *vlan_dev, u8 *addr) +{ + u16 vid = vlan_dev_vlan_id(vlan_dev); + + addr[vlan_dev->addr_len] = vid & 0xff; + addr[vlan_dev->addr_len + 1] = (vid >> 8) & 0xf; +} + bool vlan_dev_inherit_address(struct net_device *dev, struct net_device *real_dev) { @@ -480,8 +488,26 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change) } } +static void vlan_dev_align_addr_vid(struct net_device *vlan_dev) +{ + struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); + struct netdev_hw_addr *ha; + + if (!real_dev->vid_len) + return; + + netdev_for_each_mc_addr(ha, vlan_dev) + if (!ha->sync_cnt) + vlan_dev_set_addr_vid(vlan_dev, ha->addr); + + netdev_for_each_uc_addr(ha, vlan_dev) + if (!ha->sync_cnt) + vlan_dev_set_addr_vid(vlan_dev, ha->addr); +} + static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) { + vlan_dev_align_addr_vid(vlan_dev); dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); } -- 2.17.1
[PATCH net-next 3/6] net: 8021q: vlan_dev: add vid tag for vlan device own address
The vlan device address is held separately from uc/mc lists and handled differently. The vlan dev address is bound with real device address only if it's inherited from init, in all other cases it's separate address entry in uc list. With vid set, the address becomes not inherited from real device after it's set manually as before, but is part of uc list any way, with appropriate vid tag set. If vid_len for real device is 0, the behaviour is the same as before this change, so shouldn't be any impact on systems w/o individual virtual device filtering (IVDF) enabled. This allows to control and sync vlan device address and disable concrete vlan packet income when vlan interface is down. Signed-off-by: Ivan Khoronzhuk --- net/8021q/vlan.c | 3 ++ net/8021q/vlan_dev.c | 76 +--- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index dc4411165e43..9c72551a9a1e 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -295,6 +295,9 @@ static void vlan_sync_address(struct net_device *dev, if (vlan_dev_inherit_address(vlandev, dev)) goto out; + if (dev->vid_len) + goto out; + /* vlan address was different from the old address and is equal to * the new address */ if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 93d20b1f4916..634436e780f1 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -257,12 +257,61 @@ static void vlan_dev_set_addr_vid(struct net_device *vlan_dev, u8 *addr) addr[vlan_dev->addr_len + 1] = (vid >> 8) & 0xf; } +static int vlan_dev_add_addr(struct net_device *dev, u8 *addr) +{ + struct net_device *real_dev = vlan_dev_real_dev(dev); + unsigned char naddr[ETH_ALEN + NET_8021Q_VID_TSIZE]; + + if (real_dev->vid_len) { + memcpy(naddr, addr, dev->addr_len); + vlan_dev_set_addr_vid(dev, naddr); + return dev_vid_uc_add(real_dev, naddr); + } + + if (ether_addr_equal(addr, real_dev->dev_addr)) + return 0; + + return dev_uc_add(real_dev, addr); +} + +static void vlan_dev_del_addr(struct net_device *dev, u8 *addr) +{ + struct net_device *real_dev = vlan_dev_real_dev(dev); + unsigned char naddr[ETH_ALEN + NET_8021Q_VID_TSIZE]; + + if (real_dev->vid_len) { + memcpy(naddr, addr, dev->addr_len); + vlan_dev_set_addr_vid(dev, naddr); + dev_vid_uc_del(real_dev, naddr); + return; + } + + if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) + dev_uc_del(real_dev, addr); +} + +static int vlan_dev_subs_addr(struct net_device *dev, u8 *addr) +{ + int err; + + err = vlan_dev_add_addr(dev, addr); + if (err < 0) + return err; + + vlan_dev_del_addr(dev, dev->dev_addr); + return err; +} + bool vlan_dev_inherit_address(struct net_device *dev, struct net_device *real_dev) { if (dev->addr_assign_type != NET_ADDR_STOLEN) return false; + if (real_dev->vid_len) + if (vlan_dev_subs_addr(dev, real_dev->dev_addr)) + return false; + ether_addr_copy(dev->dev_addr, real_dev->dev_addr); call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); return true; @@ -278,9 +327,10 @@ static int vlan_dev_open(struct net_device *dev) !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) return -ENETDOWN; - if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) && - !vlan_dev_inherit_address(dev, real_dev)) { - err = dev_uc_add(real_dev, dev->dev_addr); + if (ether_addr_equal(dev->dev_addr, real_dev->dev_addr) || + (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) && +!vlan_dev_inherit_address(dev, real_dev))) { + err = vlan_dev_add_addr(dev, dev->dev_addr); if (err < 0) goto out; } @@ -312,8 +362,7 @@ static int vlan_dev_open(struct net_device *dev) if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, -1); del_unicast: - if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) - dev_uc_del(real_dev, dev->dev_addr); + vlan_dev_del_addr(dev, dev->dev_addr); out: netif_carrier_off(dev); return err; @@ -331,18 +380,14 @@ static int vlan_dev_stop(struct net_device *dev) if (dev->flags & IFF_PROMISC) dev_set_promiscuity(real_dev, -1); - if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) - dev_uc_del
[PATCH net] net: octeon: mgmt: fix xmit hang as busy
The issue happens only at appropriate circumstances, in my case I faced it only while running crash kernel, when basic kernel worked fine. The code inspection has shown tx_current_fill counter overflow, after one packet or couple packets were sent. That's because tx cleanup tasklet dequeued bunch of not correct packets afterwards when it should only one. As result xmit queue counter becomes more than tx ring size and xmit always returns NETDEV_TX_BUSY. The reason is in some trash got by dma after ringing the bell. The wmb() in correct place solved the issue, so reason likely in removal of mips_swiotlb_ops which had an mb() after most of the operations and the removal of the ops had broken the tx functionality of the driver implicitly. The patch has been tested on Octeon II. Fixes: a33db9ed ("MIPS: remove mips_swiotlb_ops") Change-Id: I947c359d9451c75a693bc4a3f2958489503fc0ab Signed-off-by: Ivan Khoronzhuk --- Based on net/master drivers/net/ethernet/cavium/octeon/octeon_mgmt.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c index ecffebd513be..be1c353b961c 100644 --- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c @@ -1315,6 +1315,10 @@ octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) spin_unlock_irqrestore(&p->tx_list.lock, flags); + /* Make sure there is no reorder of filling the ring and ringing +* the bell +*/ + wmb(); dma_sync_single_for_device(p->dev, p->tx_ring_handle, ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), DMA_BIDIRECTIONAL); -- 2.18.2
[PATCH] mips: kernel: setup: fix crash kernel resource allocation
In order to avoid crash kernel corruption, its memory is reserved early in memblock and as result, in time when resources are inited it's not present in memblock.memory, so crash kernel memory is out of ranges listed with for_each_mem_range(). To avoid it and still keep memory reserved lets reseve it out of loop by inserting it in iomem_resource. Fixes: a94e4f24ec83 ("MIPS: init: Drop boot_mem_map") Signed-off-by: Ivan Khoronzhuk --- Based on linux-next/master arch/mips/kernel/setup.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 3785c72bc3bc..25e376ef2f2a 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -473,14 +473,15 @@ static void __init mips_parse_crashkernel(void) crashk_res.end = crash_base + crash_size - 1; } -static void __init request_crashkernel(struct resource *res) +static void __init request_crashkernel(void) { int ret; if (crashk_res.start == crashk_res.end) return; - ret = request_resource(res, &crashk_res); + /* The crashk resource shoud be located in normal mem */ + ret = insert_resource(&iomem_resource, &crashk_res); if (!ret) pr_info("Reserving %ldMB of memory at %ldMB for crashkernel\n", (unsigned long)(resource_size(&crashk_res) >> 20), @@ -734,8 +735,9 @@ static void __init resource_init(void) request_resource(res, &code_resource); request_resource(res, &data_resource); request_resource(res, &bss_resource); - request_crashkernel(res); } + + request_crashkernel(); } #ifdef CONFIG_SMP -- 2.23.1
Re: [PATCH] mips: kernel: setup: fix crash kernel resource allocation
On Sun, Feb 07, 2021 at 11:19:03AM +0800, Jinyang He wrote: On 02/06/2021 08:59 PM, Ivan Khoronzhuk wrote: In order to avoid crash kernel corruption, its memory is reserved early in memblock and as result, in time when resources are inited it's not present in memblock.memory, so crash kernel memory is out of ranges listed with for_each_mem_range(). To avoid it and still keep memory reserved lets reseve it out of loop by inserting it in iomem_resource. Hi, Ivan, I'm not familiar with memblock. If the following my ideas show my ignorance, please forgive me. First, not only the crash kernel is reserved early in memblock, but also code, data, and bss are also reserved in bootmem_init(): /* Reserve memory occupied by kernel. */ memblock_reserve(__pa_symbol(&_text), __pa_symbol(&_end) - __pa_symbol(&_text)); (CONFIG_NUMA is not enabled. NUMA platform reserved them is earlier.) If there is something unsuitable with the crash kernel, is there something unsuitable with the kernel memory? Then, for_each_mem_range() is normal memory. Although memblock_reserve() has used before that, it just adds memory to memblock.reserved. That means it will still appear in memblock.memory. Thus, here I have a question, do we need to use replace for_each_mem_range with for_each_mem_range_rev? Reserve doesn't mean it's present in memblock.memory, if it memory was not added before, like it's supposed. In my canse, seems like to local issue it wasn't, that's why it was not present. So, traverse direction won't solve this obviously. Finally, thank you for the patch, it makes me think a lot. Thanks, Jinyang Fixes: a94e4f24ec83 ("MIPS: init: Drop boot_mem_map") Signed-off-by: Ivan Khoronzhuk --- Based on linux-next/master arch/mips/kernel/setup.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 3785c72bc3bc..25e376ef2f2a 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -473,14 +473,15 @@ static void __init mips_parse_crashkernel(void) crashk_res.end = crash_base + crash_size - 1; } -static void __init request_crashkernel(struct resource *res) +static void __init request_crashkernel(void) { int ret; if (crashk_res.start == crashk_res.end) return; - ret = request_resource(res, &crashk_res); + /* The crashk resource shoud be located in normal mem */ + ret = insert_resource(&iomem_resource, &crashk_res); if (!ret) pr_info("Reserving %ldMB of memory at %ldMB for crashkernel\n", (unsigned long)(resource_size(&crashk_res) >> 20), @@ -734,8 +735,9 @@ static void __init resource_init(void) request_resource(res, &code_resource); request_resource(res, &data_resource); request_resource(res, &bss_resource); - request_crashkernel(res); } + + request_crashkernel(); } #ifdef CONFIG_SMP -- Regards, Ivan Khoronzhuk
Re: [PATCH] mips: kernel: setup: fix crash kernel resource allocation
On Sun, Feb 07, 2021 at 11:18:42AM +0200, Mike Rapoport wrote: On Sat, Feb 06, 2021 at 12:59:40PM +, Ivan Khoronzhuk wrote: In order to avoid crash kernel corruption, its memory is reserved early in memblock and as result, in time when resources are inited it's not present in memblock.memory, so crash kernel memory is out of ranges listed with for_each_mem_range(). To avoid it and still keep memory reserved lets reseve it out of loop by inserting it in iomem_resource. Unless I misread the code, the crash kernel memory is actually allocated from memblock (memblock_find_in_range + memblock_reserve), but for some reason memblock_reserve() is called outside mips_parse_crashkernel(). So the crash kernel memory is surely in both memblock.memory and memblock.reserved and it will be covered by for_each_mem_range(). The mips_parse_crashkernel() function and the following reservation of crash kernel memory should be merged, IMO, and this can be further simplified with memblock_alloc() helpers. Is there a particular issue you are trying to fix? Yes, sorry, according to local code, seems like memory was not added (was reverted memblock_find_in_range for some other reson while porting), so I had only reserve. The issue is in another place, so ignoe this patch, for now. Fixes: a94e4f24ec83 ("MIPS: init: Drop boot_mem_map") Signed-off-by: Ivan Khoronzhuk --- Based on linux-next/master arch/mips/kernel/setup.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 3785c72bc3bc..25e376ef2f2a 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -473,14 +473,15 @@ static void __init mips_parse_crashkernel(void) crashk_res.end = crash_base + crash_size - 1; } -static void __init request_crashkernel(struct resource *res) +static void __init request_crashkernel(void) { int ret; if (crashk_res.start == crashk_res.end) return; - ret = request_resource(res, &crashk_res); + /* The crashk resource shoud be located in normal mem */ + ret = insert_resource(&iomem_resource, &crashk_res); if (!ret) pr_info("Reserving %ldMB of memory at %ldMB for crashkernel\n", (unsigned long)(resource_size(&crashk_res) >> 20), @@ -734,8 +735,9 @@ static void __init resource_init(void) request_resource(res, &code_resource); request_resource(res, &data_resource); request_resource(res, &bss_resource); - request_crashkernel(res); } + + request_crashkernel(); } #ifdef CONFIG_SMP -- 2.23.1 -- Sincerely yours, Mike. -- Regards, Ivan Khoronzhuk