[RFC PATCH] ARM: OMAP4: ID: Improve features detection and check

2012-11-01 Thread Ivan Khoronzhuk
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

2012-11-05 Thread Ivan Khoronzhuk
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

2012-12-19 Thread Ivan Khoronzhuk
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

2012-12-19 Thread Ivan Khoronzhuk
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

2012-12-19 Thread Ivan Khoronzhuk
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

2013-01-21 Thread Ivan Khoronzhuk
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

2012-12-17 Thread Ivan Khoronzhuk
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

2012-12-17 Thread Ivan Khoronzhuk
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

2012-12-17 Thread Ivan Khoronzhuk
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

2012-12-17 Thread Ivan Khoronzhuk
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

2012-12-17 Thread Ivan Khoronzhuk
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

2012-12-17 Thread Ivan Khoronzhuk
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

2013-01-18 Thread Ivan Khoronzhuk
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()

2016-07-22 Thread Ivan Khoronzhuk



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

2016-08-04 Thread Ivan Khoronzhuk



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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk

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

2016-08-04 Thread Ivan Khoronzhuk

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

2016-08-04 Thread Ivan Khoronzhuk
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

2016-08-04 Thread Ivan Khoronzhuk

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

2016-08-04 Thread Ivan Khoronzhuk


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

2016-08-04 Thread Ivan Khoronzhuk

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

2016-08-05 Thread Ivan Khoronzhuk



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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2016-08-06 Thread Ivan Khoronzhuk
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

2018-04-12 Thread Ivan Khoronzhuk
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

2018-11-26 Thread Ivan Khoronzhuk

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

2018-06-14 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-05-18 Thread Ivan Khoronzhuk
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

2018-10-16 Thread Ivan Khoronzhuk
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

2018-10-12 Thread Ivan Khoronzhuk
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

2018-10-12 Thread Ivan Khoronzhuk
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

2018-10-12 Thread Ivan Khoronzhuk
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

2018-10-12 Thread Ivan Khoronzhuk
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

2018-10-18 Thread Ivan Khoronzhuk
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

2018-10-19 Thread Ivan Khoronzhuk

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

2018-10-19 Thread Ivan Khoronzhuk
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

2015-01-23 Thread Ivan Khoronzhuk
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

2015-01-23 Thread Ivan Khoronzhuk
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

2015-01-23 Thread Ivan Khoronzhuk
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

2015-01-26 Thread Ivan Khoronzhuk


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

2015-01-26 Thread Ivan Khoronzhuk
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

2015-01-26 Thread Ivan Khoronzhuk
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

2015-01-26 Thread Ivan Khoronzhuk
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

2015-01-27 Thread Ivan Khoronzhuk


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

2015-01-28 Thread Ivan Khoronzhuk
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

2015-04-02 Thread Ivan Khoronzhuk
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

2015-04-02 Thread Ivan Khoronzhuk
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

2015-04-02 Thread Ivan Khoronzhuk
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

2015-04-02 Thread Ivan Khoronzhuk
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

2015-04-20 Thread Ivan Khoronzhuk
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

2015-04-20 Thread Ivan Khoronzhuk
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

2015-04-20 Thread Ivan Khoronzhuk
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

2015-04-20 Thread Ivan Khoronzhuk
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

2015-04-21 Thread Ivan Khoronzhuk
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

2015-04-21 Thread Ivan Khoronzhuk
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

2015-04-21 Thread Ivan Khoronzhuk
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

2015-04-21 Thread Ivan Khoronzhuk
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

2018-05-16 Thread Ivan Khoronzhuk
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

2019-03-01 Thread Ivan Khoronzhuk

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

2019-03-01 Thread Ivan Khoronzhuk

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

2019-03-01 Thread Ivan Khoronzhuk

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

2019-03-01 Thread Ivan Khoronzhuk

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

2019-02-26 Thread Ivan Khoronzhuk
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

2019-02-26 Thread Ivan Khoronzhuk
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

2019-02-26 Thread Ivan Khoronzhuk
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

2019-02-26 Thread Ivan Khoronzhuk
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

2019-02-26 Thread Ivan Khoronzhuk
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

2019-02-26 Thread Ivan Khoronzhuk
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

2019-02-26 Thread Ivan Khoronzhuk
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

2021-04-02 Thread Ivan Khoronzhuk
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

2021-02-06 Thread Ivan Khoronzhuk
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

2021-02-08 Thread Ivan Khoronzhuk

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

2021-02-08 Thread Ivan Khoronzhuk

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


  1   2   3   4   5   6   7   8   >