From bb4997a1afbff61084b243d62aaaf23ea38a290e Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Mon, 15 Jul 2013 13:15:04 +0200 Subject: bcma: add some more core names These cores were found on a BCM4708 (chipid 53010), this is a ARM SoC with two Cortex A9 cores. bcma: bus0: Found chip with id 0xCF12, rev 0x00 and package 0x02 bcma: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x2A, class 0x0) bcma: bus0: Core 1 found: DMA (manuf 0x4BF, id 0x502, rev 0x01, class 0x0) bcma: bus0: Core 2 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 3 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 4 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 5 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 6 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0) bcma: bus0: Core 7 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0) bcma: bus0: Core 8 found: ARM Cortex A9 core (ihost) (manuf 0x4BF, id 0x510, rev 0x01, class 0x0) bcma: bus0: Core 9 found: USB 2.0 (manuf 0x4BF, id 0x504, rev 0x01, class 0x0) bcma: bus0: Core 10 found: USB 3.0 (manuf 0x4BF, id 0x505, rev 0x01, class 0x0) bcma: bus0: Core 11 found: SDIO3 (manuf 0x4BF, id 0x503, rev 0x01, class 0x0) bcma: bus0: Core 12 found: ARM Cortex A9 JTAG (manuf 0x4BF, id 0x506, rev 0x01, class 0x0) bcma: bus0: Core 13 found: Denali DDR2/DDR3 memory controller (manuf 0x4BF, id 0x507, rev 0x01, class 0x0) bcma: bus0: Core 14 found: ROM (manuf 0x4BF, id 0x508, rev 0x01, class 0x0) bcma: bus0: Core 15 found: NAND flash controller (manuf 0x4BF, id 0x509, rev 0x01, class 0x0) bcma: bus0: Core 16 found: SPI flash controller (manuf 0x4BF, id 0x50A, rev 0x01, class 0x0) Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/bcma/bcma.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 622fc505d3e1..8fee02807204 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -72,7 +72,19 @@ struct bcma_host_ops { /* Core-ID values. */ #define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */ #define BCMA_CORE_4706_CHIPCOMMON 0x500 +#define BCMA_CORE_PCIEG2 0x501 +#define BCMA_CORE_DMA 0x502 +#define BCMA_CORE_SDIO3 0x503 +#define BCMA_CORE_USB20 0x504 +#define BCMA_CORE_USB30 0x505 +#define BCMA_CORE_A9JTAG 0x506 +#define BCMA_CORE_DDR23 0x507 +#define BCMA_CORE_ROM 0x508 +#define BCMA_CORE_NAND 0x509 +#define BCMA_CORE_QSPI 0x50A +#define BCMA_CORE_CHIPCOMMON_B 0x50B #define BCMA_CORE_4706_SOC_RAM 0x50E +#define BCMA_CORE_ARMCA9 0x510 #define BCMA_CORE_4706_MAC_GBIT 0x52D #define BCMA_CORE_AMEMC 0x52E /* DDR1/2 memory controller core */ #define BCMA_CORE_ALTA 0x534 /* I2S core */ -- cgit v1.2.3 From 16041990d1c75efb4408d19413cf4fd27aa148dd Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Mon, 15 Jul 2013 13:15:06 +0200 Subject: bcma: add constants for new ARM based SoCs These are the chipIDs of some ARM based SoCs from the BCM47xx line. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/bcma/bcma.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 8fee02807204..4d043c30216f 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -189,6 +189,11 @@ struct bcma_host_ops { #define BCMA_PKG_ID_BCM5357 11 #define BCMA_CHIP_ID_BCM53572 53572 #define BCMA_PKG_ID_BCM47188 9 +#define BCMA_CHIP_ID_BCM4707 53010 +#define BCMA_PKG_ID_BCM4707 1 +#define BCMA_PKG_ID_BCM4708 2 +#define BCMA_PKG_ID_BCM4709 0 +#define BCMA_CHIP_ID_BCM53018 53018 /* Board types (on PCI usually equals to the subsystem dev id) */ /* BCM4313 */ -- cgit v1.2.3 From fc423ff00df3a19554414eed80aef9de9b50313e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 20 Jul 2013 12:13:52 +0200 Subject: team: add peer notification When port is enabled or disabled, allow to notify peers by unsolicitated NAs or gratuitous ARPs. Disabled by default. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_team.h | 8 ++++- 2 files changed, 94 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index bff7e0b0b4e7..0433ee994f8c 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -622,6 +622,46 @@ static int team_change_mode(struct team *team, const char *kind) } +/********************* + * Peers notification + *********************/ + +static void team_notify_peers_work(struct work_struct *work) +{ + struct team *team; + + team = container_of(work, struct team, notify_peers.dw.work); + + if (!rtnl_trylock()) { + schedule_delayed_work(&team->notify_peers.dw, 0); + return; + } + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev); + rtnl_unlock(); + if (!atomic_dec_and_test(&team->notify_peers.count_pending)) + schedule_delayed_work(&team->notify_peers.dw, + msecs_to_jiffies(team->notify_peers.interval)); +} + +static void team_notify_peers(struct team *team) +{ + if (!team->notify_peers.count || !netif_running(team->dev)) + return; + atomic_set(&team->notify_peers.count_pending, team->notify_peers.count); + schedule_delayed_work(&team->notify_peers.dw, 0); +} + +static void team_notify_peers_init(struct team *team) +{ + INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work); +} + +static void team_notify_peers_fini(struct team *team) +{ + cancel_delayed_work_sync(&team->notify_peers.dw); +} + + /************************ * Rx path frame handler ************************/ @@ -846,6 +886,7 @@ static void team_port_enable(struct team *team, team_queue_override_port_add(team, port); if (team->ops.port_enabled) team->ops.port_enabled(team, port); + team_notify_peers(team); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -875,6 +916,7 @@ static void team_port_disable(struct team *team, team->en_port_count--; team_queue_override_port_del(team, port); team_adjust_ops(team); + team_notify_peers(team); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -1205,6 +1247,34 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) return team_change_mode(team, ctx->data.str_val); } +static int team_notify_peers_count_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->notify_peers.count; + return 0; +} + +static int team_notify_peers_count_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->notify_peers.count = ctx->data.u32_val; + return 0; +} + +static int team_notify_peers_interval_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->notify_peers.interval; + return 0; +} + +static int team_notify_peers_interval_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->notify_peers.interval = ctx->data.u32_val; + return 0; +} + static int team_port_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { @@ -1316,6 +1386,18 @@ static const struct team_option team_options[] = { .getter = team_mode_option_get, .setter = team_mode_option_set, }, + { + .name = "notify_peers_count", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_notify_peers_count_get, + .setter = team_notify_peers_count_set, + }, + { + .name = "notify_peers_interval", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_notify_peers_interval_get, + .setter = team_notify_peers_interval_set, + }, { .name = "enabled", .type = TEAM_OPTION_TYPE_BOOL, @@ -1396,6 +1478,9 @@ static int team_init(struct net_device *dev) INIT_LIST_HEAD(&team->option_list); INIT_LIST_HEAD(&team->option_inst_list); + + team_notify_peers_init(team); + err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); if (err) goto err_options_register; @@ -1406,6 +1491,7 @@ static int team_init(struct net_device *dev) return 0; err_options_register: + team_notify_peers_fini(team); team_queue_override_fini(team); err_team_queue_override_init: free_percpu(team->pcpu_stats); @@ -1425,6 +1511,7 @@ static void team_uninit(struct net_device *dev) __team_change_mode(team, NULL); /* cleanup */ __team_options_unregister(team, team_options, ARRAY_SIZE(team_options)); + team_notify_peers_fini(team); team_queue_override_fini(team); mutex_unlock(&team->lock); } diff --git a/include/linux/if_team.h b/include/linux/if_team.h index f6156f91eb1c..b0b83683461e 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -10,9 +10,9 @@ #ifndef _LINUX_IF_TEAM_H_ #define _LINUX_IF_TEAM_H_ - #include #include +#include #include struct team_pcpu_stats { @@ -194,6 +194,12 @@ struct team { bool user_carrier_enabled; bool queue_override_enabled; struct list_head *qom_lists; /* array of queue override mapping lists */ + struct { + unsigned int count; + unsigned int interval; /* in ms */ + atomic_t count_pending; + struct delayed_work dw; + } notify_peers; long mode_priv[TEAM_MODE_PRIV_LONGS]; }; -- cgit v1.2.3 From 4aa5dee4d9997879adff858514844efab5a15a01 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 20 Jul 2013 12:13:53 +0200 Subject: net: convert resend IGMP to notifier event Until now, bond_resend_igmp_join_requests() looks for vlans attached to bonding device, bridge where bonding act as port manually. It does not care of other scenarios, like stacked bonds or team device above. Make this more generic and use netdev notifier to propagate the event to upper devices and to actually call ip_mc_rejoin_groups(). Signed-off-by: Jiri Pirko Acked-by: Veaceslav Falico Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 44 ++++++++------------------------------- drivers/net/team/team.c | 4 ++++ include/linux/igmp.h | 1 - include/linux/netdevice.h | 1 + net/8021q/vlan.c | 1 + net/bridge/br_notify.c | 5 +++++ net/ipv4/igmp.c | 46 +++++++++++++++++++++++++++++++++++------ 7 files changed, 60 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 07f257d44a1e..ae9864c9fa38 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -715,15 +715,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc) return err; } -static void __bond_resend_igmp_join_requests(struct net_device *dev) -{ - struct in_device *in_dev; - - in_dev = __in_dev_get_rcu(dev); - if (in_dev) - ip_mc_rejoin_groups(in_dev); -} - /* * Retrieve the list of registered multicast addresses for the bonding * device and retransmit an IGMP JOIN request to the current active @@ -731,33 +722,12 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) */ static void bond_resend_igmp_join_requests(struct bonding *bond) { - struct net_device *bond_dev, *vlan_dev, *upper_dev; - struct vlan_entry *vlan; - - read_lock(&bond->lock); - rcu_read_lock(); - - bond_dev = bond->dev; - - /* rejoin all groups on bond device */ - __bond_resend_igmp_join_requests(bond_dev); - - /* - * if bond is enslaved to a bridge, - * then rejoin all groups on its master - */ - upper_dev = netdev_master_upper_dev_get_rcu(bond_dev); - if (upper_dev && upper_dev->priv_flags & IFF_EBRIDGE) - __bond_resend_igmp_join_requests(upper_dev); - - /* rejoin all groups on vlan devices */ - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - vlan_dev = __vlan_find_dev_deep(bond_dev, htons(ETH_P_8021Q), - vlan->vlan_id); - if (vlan_dev) - __bond_resend_igmp_join_requests(vlan_dev); + if (!rtnl_trylock()) { + queue_delayed_work(bond->wq, &bond->mcast_work, 0); + return; } - rcu_read_unlock(); + call_netdevice_notifiers(NETDEV_RESEND_IGMP, bond->dev); + rtnl_unlock(); /* We use curr_slave_lock to protect against concurrent access to * igmp_retrans from multiple running instances of this function and @@ -3234,6 +3204,10 @@ static int bond_slave_netdev_event(unsigned long event, case NETDEV_FEAT_CHANGE: bond_compute_features(bond); break; + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, slave->bond->dev); + break; default: break; } diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 0433ee994f8c..2587dc86d533 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2785,6 +2785,10 @@ static int team_device_event(struct notifier_block *unused, case NETDEV_PRE_TYPE_CHANGE: /* Forbid to change type of underlaying device */ return NOTIFY_BAD; + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, port->team->dev); + break; } return NOTIFY_DONE; } diff --git a/include/linux/igmp.h b/include/linux/igmp.h index e3362b5f13e8..f47550d75f85 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -129,6 +129,5 @@ extern void ip_mc_unmap(struct in_device *); extern void ip_mc_remap(struct in_device *); extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); -extern void ip_mc_rejoin_groups(struct in_device *in_dev); #endif diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0741a1e919a5..2bb2357d83bb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1633,6 +1633,7 @@ struct packet_offload { #define NETDEV_NOTIFY_PEERS 0x0013 #define NETDEV_JOIN 0x0014 #define NETDEV_CHANGEUPPER 0x0015 +#define NETDEV_RESEND_IGMP 0x0016 extern int register_netdevice_notifier(struct notifier_block *nb); extern int unregister_netdevice_notifier(struct notifier_block *nb); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 2fb2d88e8c2e..03a92e117657 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -459,6 +459,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, case NETDEV_NOTIFY_PEERS: case NETDEV_BONDING_FAILOVER: + case NETDEV_RESEND_IGMP: /* Propagate to vlan devices */ vlan_group_for_each_dev(grp, i, vlandev) call_netdevice_notifiers(event, vlandev); diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 3a3f371b2841..2998dd1769a0 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -102,6 +102,11 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v case NETDEV_PRE_TYPE_CHANGE: /* Forbid underlaying device to change its type. */ return NOTIFY_BAD; + + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, br->dev); + break; } /* Events that may cause spanning tree to refresh */ diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index cd71190d2962..375aca372250 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1323,16 +1323,17 @@ out: EXPORT_SYMBOL(ip_mc_inc_group); /* - * Resend IGMP JOIN report; used for bonding. - * Called with rcu_read_lock() + * Resend IGMP JOIN report; used by netdev notifier. */ -void ip_mc_rejoin_groups(struct in_device *in_dev) +static void ip_mc_rejoin_groups(struct in_device *in_dev) { #ifdef CONFIG_IP_MULTICAST struct ip_mc_list *im; int type; - for_each_pmc_rcu(in_dev, im) { + ASSERT_RTNL(); + + for_each_pmc_rtnl(in_dev, im) { if (im->multiaddr == IGMP_ALL_HOSTS) continue; @@ -1349,7 +1350,6 @@ void ip_mc_rejoin_groups(struct in_device *in_dev) } #endif } -EXPORT_SYMBOL(ip_mc_rejoin_groups); /* * A socket has left a multicast group on device dev @@ -2735,8 +2735,42 @@ static struct pernet_operations igmp_net_ops = { .exit = igmp_net_exit, }; +static int igmp_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct in_device *in_dev; + + switch (event) { + case NETDEV_RESEND_IGMP: + in_dev = __in_dev_get_rtnl(dev); + if (in_dev) + ip_mc_rejoin_groups(in_dev); + break; + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block igmp_notifier = { + .notifier_call = igmp_netdev_event, +}; + int __init igmp_mc_proc_init(void) { - return register_pernet_subsys(&igmp_net_ops); + int err; + + err = register_pernet_subsys(&igmp_net_ops); + if (err) + return err; + err = register_netdevice_notifier(&igmp_notifier); + if (err) + goto reg_notif_fail; + return 0; + +reg_notif_fail: + unregister_pernet_subsys(&igmp_net_ops); + return err; } #endif -- cgit v1.2.3 From 492b200efdd20b8fcfdac873f3cd8d4902386581 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 20 Jul 2013 12:13:54 +0200 Subject: team: add support for sending multicast rejoins Similar to what is implemented in bonding. User is able to ask team driver to send IGMP rejoins in case port is enabled or disabled. Using previously introduced netdev notifier. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_team.h | 6 ++++ 2 files changed, 91 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 2587dc86d533..75159e4184fd 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -662,6 +662,46 @@ static void team_notify_peers_fini(struct team *team) } +/******************************* + * Send multicast group rejoins + *******************************/ + +static void team_mcast_rejoin_work(struct work_struct *work) +{ + struct team *team; + + team = container_of(work, struct team, mcast_rejoin.dw.work); + + if (!rtnl_trylock()) { + schedule_delayed_work(&team->mcast_rejoin.dw, 0); + return; + } + call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev); + rtnl_unlock(); + if (!atomic_dec_and_test(&team->mcast_rejoin.count_pending)) + schedule_delayed_work(&team->mcast_rejoin.dw, + msecs_to_jiffies(team->mcast_rejoin.interval)); +} + +static void team_mcast_rejoin(struct team *team) +{ + if (!team->mcast_rejoin.count || !netif_running(team->dev)) + return; + atomic_set(&team->mcast_rejoin.count_pending, team->mcast_rejoin.count); + schedule_delayed_work(&team->mcast_rejoin.dw, 0); +} + +static void team_mcast_rejoin_init(struct team *team) +{ + INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work); +} + +static void team_mcast_rejoin_fini(struct team *team) +{ + cancel_delayed_work_sync(&team->mcast_rejoin.dw); +} + + /************************ * Rx path frame handler ************************/ @@ -887,6 +927,7 @@ static void team_port_enable(struct team *team, if (team->ops.port_enabled) team->ops.port_enabled(team, port); team_notify_peers(team); + team_mcast_rejoin(team); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -917,6 +958,7 @@ static void team_port_disable(struct team *team, team_queue_override_port_del(team, port); team_adjust_ops(team); team_notify_peers(team); + team_mcast_rejoin(team); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -1275,6 +1317,34 @@ static int team_notify_peers_interval_set(struct team *team, return 0; } +static int team_mcast_rejoin_count_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->mcast_rejoin.count; + return 0; +} + +static int team_mcast_rejoin_count_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->mcast_rejoin.count = ctx->data.u32_val; + return 0; +} + +static int team_mcast_rejoin_interval_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->mcast_rejoin.interval; + return 0; +} + +static int team_mcast_rejoin_interval_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->mcast_rejoin.interval = ctx->data.u32_val; + return 0; +} + static int team_port_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { @@ -1398,6 +1468,18 @@ static const struct team_option team_options[] = { .getter = team_notify_peers_interval_get, .setter = team_notify_peers_interval_set, }, + { + .name = "mcast_rejoin_count", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_mcast_rejoin_count_get, + .setter = team_mcast_rejoin_count_set, + }, + { + .name = "mcast_rejoin_interval", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_mcast_rejoin_interval_get, + .setter = team_mcast_rejoin_interval_set, + }, { .name = "enabled", .type = TEAM_OPTION_TYPE_BOOL, @@ -1480,6 +1562,7 @@ static int team_init(struct net_device *dev) INIT_LIST_HEAD(&team->option_inst_list); team_notify_peers_init(team); + team_mcast_rejoin_init(team); err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); if (err) @@ -1491,6 +1574,7 @@ static int team_init(struct net_device *dev) return 0; err_options_register: + team_mcast_rejoin_fini(team); team_notify_peers_fini(team); team_queue_override_fini(team); err_team_queue_override_init: @@ -1511,6 +1595,7 @@ static void team_uninit(struct net_device *dev) __team_change_mode(team, NULL); /* cleanup */ __team_options_unregister(team, team_options, ARRAY_SIZE(team_options)); + team_mcast_rejoin_fini(team); team_notify_peers_fini(team); team_queue_override_fini(team); mutex_unlock(&team->lock); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index b0b83683461e..a899dc24be15 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -200,6 +200,12 @@ struct team { atomic_t count_pending; struct delayed_work dw; } notify_peers; + struct { + unsigned int count; + unsigned int interval; /* in ms */ + atomic_t count_pending; + struct delayed_work dw; + } mcast_rejoin; long mode_priv[TEAM_MODE_PRIV_LONGS]; }; -- cgit v1.2.3 From c9bee3b7fdecb0c1d070c7b54113b3bdfb9a3d36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jul 2013 20:27:07 -0700 Subject: tcp: TCP_NOTSENT_LOWAT socket option Idea of this patch is to add optional limitation of number of unsent bytes in TCP sockets, to reduce usage of kernel memory. TCP receiver might announce a big window, and TCP sender autotuning might allow a large amount of bytes in write queue, but this has little performance impact if a large part of this buffering is wasted : Write queue needs to be large only to deal with large BDP, not necessarily to cope with scheduling delays (incoming ACKS make room for the application to queue more bytes) For most workloads, using a value of 128 KB or less is OK to give applications enough time to react to POLLOUT events in time (or being awaken in a blocking sendmsg()) This patch adds two ways to set the limit : 1) Per socket option TCP_NOTSENT_LOWAT 2) A sysctl (/proc/sys/net/ipv4/tcp_notsent_lowat) for sockets not using TCP_NOTSENT_LOWAT socket option (or setting a zero value) Default value being UINT_MAX (0xFFFFFFFF), meaning this has no effect. This changes poll()/select()/epoll() to report POLLOUT only if number of unsent bytes is below tp->nosent_lowat Note this might increase number of sendmsg()/sendfile() calls when using non blocking sockets, and increase number of context switches for blocking sockets. Note this is not related to SO_SNDLOWAT (as SO_SNDLOWAT is defined as : Specify the minimum number of bytes in the buffer until the socket layer will pass the data to the protocol) Tested: netperf sessions, and watching /proc/net/protocols "memory" column for TCP With 200 concurrent netperf -t TCP_STREAM sessions, amount of kernel memory used by TCP buffers shrinks by ~55 % (20567 pages instead of 45458) lpq83:~# echo -1 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# (super_netperf 200 -t TCP_STREAM -H remote -l 90 &); sleep 60 ; grep TCP /proc/net/protocols TCPv6 1880 2 45458 no 208 yes ipv6 y y y y y y y y y y y y y n y y y y y TCP 1696 508 45458 no 208 yes kernel y y y y y y y y y y y y y n y y y y y lpq83:~# echo 131072 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# (super_netperf 200 -t TCP_STREAM -H remote -l 90 &); sleep 60 ; grep TCP /proc/net/protocols TCPv6 1880 2 20567 no 208 yes ipv6 y y y y y y y y y y y y y n y y y y y TCP 1696 508 20567 no 208 yes kernel y y y y y y y y y y y y y n y y y y y Using 128KB has no bad effect on the throughput or cpu usage of a single flow, although there is an increase of context switches. A bonus is that we hold socket lock for a shorter amount of time and should improve latencies of ACK processing. lpq83:~# echo -1 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# perf stat -e context-switches ./netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3 OMNI Send TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.7.84 () port 0 AF_INET : +/-2.500% @ 99% conf. Local Remote Local Elapsed Throughput Throughput Local Local Remote Remote Local Remote Service Send Socket Recv Socket Send Time Units CPU CPU CPU CPU Service Service Demand Size Size Size (sec) Util Util Util Util Demand Demand Units Final Final % Method % Method 1651584 6291456 16384 20.00 17447.90 10^6bits/s 3.13 S -1.00 U 0.353 -1.000 usec/KB Performance counter stats for './netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3': 412,514 context-switches 200.034645535 seconds time elapsed lpq83:~# echo 131072 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# perf stat -e context-switches ./netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3 OMNI Send TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.7.84 () port 0 AF_INET : +/-2.500% @ 99% conf. Local Remote Local Elapsed Throughput Throughput Local Local Remote Remote Local Remote Service Send Socket Recv Socket Send Time Units CPU CPU CPU CPU Service Service Demand Size Size Size (sec) Util Util Util Util Demand Demand Units Final Final % Method % Method 1593240 6291456 16384 20.00 17321.16 10^6bits/s 3.35 S -1.00 U 0.381 -1.000 usec/KB Performance counter stats for './netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3': 2,675,818 context-switches 200.029651391 seconds time elapsed Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Acked-By: Yuchung Cheng Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 13 +++++++++++++ include/linux/tcp.h | 1 + include/net/sock.h | 19 +++++++++++++------ include/net/tcp.h | 14 ++++++++++++++ include/uapi/linux/tcp.h | 1 + net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv4/tcp.c | 7 +++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 3 +++ net/ipv6/tcp_ipv6.c | 1 + 10 files changed, 61 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 10742902146f..53cea9bcb14c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -516,6 +516,19 @@ tcp_wmem - vector of 3 INTEGERs: min, default, max this value is ignored. Default: between 64K and 4MB, depending on RAM size. +tcp_notsent_lowat - UNSIGNED INTEGER + A TCP socket can control the amount of unsent bytes in its write queue, + thanks to TCP_NOTSENT_LOWAT socket option. poll()/select()/epoll() + reports POLLOUT events if the amount of unsent bytes is below a per + socket value, and if the write queue is not full. sendmsg() will + also not add new buffers if the limit is hit. + + This global variable controls the amount of unsent data for + sockets not using TCP_NOTSENT_LOWAT. For these sockets, a change + to the global variable has immediate effect. + + Default: UINT_MAX (0xFFFFFFFF) + tcp_workaround_signed_windows - BOOLEAN If set, assume no receipt of a window scaling option means the remote TCP is broken and treats the window as a signed quantity. diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 472120b4fac5..9640803a17a7 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -238,6 +238,7 @@ struct tcp_sock { u32 rcv_wnd; /* Current receiver window */ u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ + u32 notsent_lowat; /* TCP_NOTSENT_LOWAT */ u32 pushed_seq; /* Last pushed seq, required to talk to windows */ u32 lost_out; /* Lost packets */ u32 sacked_out; /* SACK'd packets */ diff --git a/include/net/sock.h b/include/net/sock.h index d0b5fdee50a2..b9f2b095b1ab 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -746,11 +746,6 @@ static inline int sk_stream_wspace(const struct sock *sk) extern void sk_stream_write_space(struct sock *sk); -static inline bool sk_stream_memory_free(const struct sock *sk) -{ - return sk->sk_wmem_queued < sk->sk_sndbuf; -} - /* OOB backlog add */ static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) { @@ -950,6 +945,7 @@ struct proto { unsigned int inuse_idx; #endif + bool (*stream_memory_free)(const struct sock *sk); /* Memory pressure */ void (*enter_memory_pressure)(struct sock *sk); atomic_long_t *memory_allocated; /* Current allocated memory. */ @@ -1088,11 +1084,22 @@ static inline struct cg_proto *parent_cg_proto(struct proto *proto, } #endif +static inline bool sk_stream_memory_free(const struct sock *sk) +{ + if (sk->sk_wmem_queued >= sk->sk_sndbuf) + return false; + + return sk->sk_prot->stream_memory_free ? + sk->sk_prot->stream_memory_free(sk) : true; +} + static inline bool sk_stream_is_writeable(const struct sock *sk) { - return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk); + return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && + sk_stream_memory_free(sk); } + static inline bool sk_has_memory_pressure(const struct sock *sk) { return sk->sk_prot->memory_pressure != NULL; diff --git a/include/net/tcp.h b/include/net/tcp.h index c5868471abae..18fc999dae3c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -284,6 +284,7 @@ extern int sysctl_tcp_thin_dupack; extern int sysctl_tcp_early_retrans; extern int sysctl_tcp_limit_output_bytes; extern int sysctl_tcp_challenge_ack_limit; +extern unsigned int sysctl_tcp_notsent_lowat; extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; @@ -1539,6 +1540,19 @@ extern int tcp_gro_complete(struct sk_buff *skb); extern void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr); +static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp) +{ + return tp->notsent_lowat ?: sysctl_tcp_notsent_lowat; +} + +static inline bool tcp_stream_memory_free(const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + u32 notsent_bytes = tp->write_seq - tp->snd_nxt; + + return notsent_bytes < tcp_notsent_lowat(tp); +} + #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 8d776ebc4829..377f1e59411d 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -111,6 +111,7 @@ enum { #define TCP_REPAIR_OPTIONS 22 #define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ #define TCP_TIMESTAMP 24 +#define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ struct tcp_repair_opt { __u32 opt_code; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index b2c123c44d69..69ed203802da 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -554,6 +554,13 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one, }, + { + .procname = "tcp_notsent_lowat", + .data = &sysctl_tcp_notsent_lowat, + .maxlen = sizeof(sysctl_tcp_notsent_lowat), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "tcp_rmem", .data = &sysctl_tcp_rmem, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 5eca9060bb8e..c27e81392398 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2631,6 +2631,10 @@ static int do_tcp_setsockopt(struct sock *sk, int level, else tp->tsoffset = val - tcp_time_stamp; break; + case TCP_NOTSENT_LOWAT: + tp->notsent_lowat = val; + sk->sk_write_space(sk); + break; default: err = -ENOPROTOOPT; break; @@ -2847,6 +2851,9 @@ static int do_tcp_getsockopt(struct sock *sk, int level, case TCP_TIMESTAMP: val = tcp_time_stamp + tp->tsoffset; break; + case TCP_NOTSENT_LOWAT: + val = tp->notsent_lowat; + break; default: return -ENOPROTOOPT; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2e3f129df0eb..2a5d5c469d17 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2800,6 +2800,7 @@ struct proto tcp_prot = { .unhash = inet_unhash, .get_port = inet_csk_get_port, .enter_memory_pressure = tcp_enter_memory_pressure, + .stream_memory_free = tcp_stream_memory_free, .sockets_allocated = &tcp_sockets_allocated, .orphan_count = &tcp_orphan_count, .memory_allocated = &tcp_memory_allocated, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 92fde8d1aa82..884efff5b531 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -65,6 +65,9 @@ int sysctl_tcp_base_mss __read_mostly = TCP_BASE_MSS; /* By default, RFC2861 behavior. */ int sysctl_tcp_slow_start_after_idle __read_mostly = 1; +unsigned int sysctl_tcp_notsent_lowat __read_mostly = UINT_MAX; +EXPORT_SYMBOL(sysctl_tcp_notsent_lowat); + static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 80fe69ef2188..b792e870686b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1924,6 +1924,7 @@ struct proto tcpv6_prot = { .unhash = inet_unhash, .get_port = inet_csk_get_port, .enter_memory_pressure = tcp_enter_memory_pressure, + .stream_memory_free = tcp_stream_memory_free, .sockets_allocated = &tcp_sockets_allocated, .memory_allocated = &tcp_memory_allocated, .memory_pressure = &tcp_memory_pressure, -- cgit v1.2.3 From 18afa4b028b46f8b45ca64f94aefe717c297b07d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 Jul 2013 16:13:17 +0200 Subject: net: Make devnet_rename_seq static No users outside net/core/dev.c. Signed-off-by: Thomas Gleixner Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 --- net/core/dev.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2bb2357d83bb..3ca60b070ef0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1666,9 +1666,6 @@ extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev); extern rwlock_t dev_base_lock; /* Device list lock */ -extern seqcount_t devnet_rename_seq; /* Device rename seq */ - - #define for_each_netdev(net, d) \ list_for_each_entry(d, &(net)->dev_base_head, dev_list) #define for_each_netdev_reverse(net, d) \ diff --git a/net/core/dev.c b/net/core/dev.c index 26755dd40daa..dfd9f5d56ae0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -174,7 +174,7 @@ static DEFINE_SPINLOCK(napi_hash_lock); static unsigned int napi_gen_id; static DEFINE_HASHTABLE(napi_hash, 8); -seqcount_t devnet_rename_seq; +static seqcount_t devnet_rename_seq; static inline void dev_base_seq_inc(struct net *net) { -- cgit v1.2.3 From a88c32ae15f25fcf0a3c9fadd92f840a1abf0e43 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 25 Jul 2013 13:47:53 +0800 Subject: USBNET: centralize computing of max rx/tx qlen This patch centralizes computing of max rx/tx qlen, because: - RX_QLEN()/TX_QLEN() is called in hot path - computing depends on device's usb speed, now we have ls/fs, hs, ss, so more checks need to be involved - in fact, max rx/tx qlen should not only depend on device USB speed, but also depend on ethernet link speed, so we need to consider that in future. - if SG support is done, max tx qlen may need change too Generally, hard_mtu and rx_urb_size are changed in bind(), reset() and link_reset() callback, and change mtu network operation, this patches introduces the API of usbnet_update_max_qlen(), and calls it in above path. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/asix_devices.c | 3 +++ drivers/net/usb/ax88179_178a.c | 3 +++ drivers/net/usb/usbnet.c | 45 ++++++++++++++++++++++++++++++++++-------- include/linux/usb/usbnet.h | 3 +++ 4 files changed, 46 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index ad5d1e4384db..b96ad4f15b8a 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -778,6 +778,9 @@ static int ax88178_change_mtu(struct net_device *net, int new_mtu) dev->hard_mtu = net->mtu + net->hard_header_len; ax88178_set_mfb(dev); + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 1e3c302d94fe..658540433faf 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -688,6 +688,9 @@ static int ax88179_change_mtu(struct net_device *net, int new_mtu) 2, 2, &tmp16); } + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 06ee82f557d4..d74de074a438 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -59,15 +59,13 @@ * For high speed, each frame comfortably fits almost 36 max size * Ethernet packets (so queues should be bigger). * - * REVISIT qlens should be members of 'struct usbnet'; the goal is to - * let the USB host controller be busy for 5msec or more before an irq - * is required, under load. Jumbograms change the equation. + * The goal is to let the USB host controller be busy for 5msec or + * more before an irq is required, under load. Jumbograms change + * the equation. */ -#define RX_MAX_QUEUE_MEMORY (60 * 1518) -#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4) -#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4) +#define MAX_QUEUE_MEMORY (60 * 1518) +#define RX_QLEN(dev) ((dev)->rx_qlen) +#define TX_QLEN(dev) ((dev)->tx_qlen) // reawaken network queue this soon after stopping; else watchdog barks #define TX_TIMEOUT_JIFFIES (5*HZ) @@ -347,6 +345,22 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(usbnet_skb_return); +/* must be called if hard_mtu or rx_urb_size changed */ +void usbnet_update_max_qlen(struct usbnet *dev) +{ + enum usb_device_speed speed = dev->udev->speed; + + switch (speed) { + case USB_SPEED_HIGH: + dev->rx_qlen = MAX_QUEUE_MEMORY / dev->rx_urb_size; + dev->tx_qlen = MAX_QUEUE_MEMORY / dev->hard_mtu; + break; + default: + dev->rx_qlen = dev->tx_qlen = 4; + } +} +EXPORT_SYMBOL_GPL(usbnet_update_max_qlen); + /*------------------------------------------------------------------------- * @@ -375,6 +389,9 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu) usbnet_unlink_rx_urbs(dev); } + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } EXPORT_SYMBOL_GPL(usbnet_change_mtu); @@ -843,6 +860,9 @@ int usbnet_open (struct net_device *net) goto done; } + /* hard_mtu or rx_urb_size may change in reset() */ + usbnet_update_max_qlen(dev); + // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval); @@ -927,6 +947,9 @@ int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) if (dev->driver_info->link_reset) dev->driver_info->link_reset(dev); + /* hard_mtu or rx_urb_size may change in link_reset() */ + usbnet_update_max_qlen(dev); + return retval; } @@ -1020,6 +1043,9 @@ static void __handle_link_change(struct usbnet *dev) tasklet_schedule(&dev->bh); } + /* hard_mtu or rx_urb_size may change during link change */ + usbnet_update_max_qlen(dev); + clear_bit(EVENT_LINK_CHANGE, &dev->flags); } @@ -1599,6 +1625,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if ((dev->driver_info->flags & FLAG_WWAN) != 0) SET_NETDEV_DEVTYPE(net, &wwan_type); + /* initialize max rx_qlen and tx_qlen */ + usbnet_update_max_qlen(dev); + status = register_netdev (net); if (status) goto out4; diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index f18d64129f99..8fbc008e183e 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -34,6 +34,7 @@ struct usbnet { struct mutex phy_mutex; unsigned char suspend_count; unsigned char pkt_cnt, pkt_err; + unsigned short rx_qlen, tx_qlen; /* i/o info: pipes etc */ unsigned in, out; @@ -253,4 +254,6 @@ extern void usbnet_link_change(struct usbnet *, bool, bool); extern int usbnet_status_start(struct usbnet *dev, gfp_t mem_flags); extern void usbnet_status_stop(struct usbnet *dev); +extern void usbnet_update_max_qlen(struct usbnet *dev); + #endif /* __LINUX_USB_USBNET_H */ -- cgit v1.2.3 From fe6f700d6cbb7e8a61711e325f53d9c9e0a42a4c Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sun, 28 Jul 2013 18:54:21 +0300 Subject: net/mlx4_core: Respond to operation request by firmware This commit adds new firmware command and new firmware event. The firmware raises the MLX4_EVENT_TYPE_OP_REQUIRED event in order to signal the driver it needs to perform an administrative operation throughout the MLX4_CMD_GET_OP_REQ command. At the moment the supported operation is adding/removing multicast entries which are used by the firmware for handling NCSI traffic in B0 steering mode. Also, had to swap the order of mlx4_init_mcg_table() and mlx4_init_eq_table() to make sure that driver will get events only after resources are initialized to handle it. Signed-off-by: Yevgeny Petrilin Signed-off-by: Jack Morgenstein Signed-off-by: Eugenia Emantayev Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 18 ++++++ drivers/net/ethernet/mellanox/mlx4/eq.c | 9 +++ drivers/net/ethernet/mellanox/mlx4/fw.c | 104 ++++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw.h | 1 + drivers/net/ethernet/mellanox/mlx4/main.c | 35 +++++----- drivers/net/ethernet/mellanox/mlx4/mcg.c | 11 ---- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 13 ++++ include/linux/mlx4/cmd.h | 1 + include/linux/mlx4/device.h | 1 + 9 files changed, 166 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 299d0184f983..141322c31ae9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -809,6 +809,15 @@ int MLX4_CMD_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, return -EPERM; } +int MLX4_CMD_GET_OP_REQ_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return -EPERM; +} + int mlx4_DMA_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, @@ -1251,6 +1260,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = MLX4_CMD_UPDATE_QP_wrapper }, + { + .opcode = MLX4_CMD_GET_OP_REQ, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = MLX4_CMD_GET_OP_REQ_wrapper, + }, { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 7e042869ef0c..0416c5b3b35c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -79,6 +79,7 @@ enum { (1ull << MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE) | \ (1ull << MLX4_EVENT_TYPE_SRQ_LIMIT) | \ (1ull << MLX4_EVENT_TYPE_CMD) | \ + (1ull << MLX4_EVENT_TYPE_OP_REQUIRED) | \ (1ull << MLX4_EVENT_TYPE_COMM_CHANNEL) | \ (1ull << MLX4_EVENT_TYPE_FLR_EVENT) | \ (1ull << MLX4_EVENT_TYPE_FATAL_WARNING)) @@ -629,6 +630,14 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) mlx4_warn(dev, "EQ overrun on EQN %d\n", eq->eqn); break; + case MLX4_EVENT_TYPE_OP_REQUIRED: + atomic_inc(&priv->opreq_count); + /* FW commands can't be executed from interrupt context + * working in deferred task + */ + queue_work(mlx4_wq, &priv->opreq_task); + break; + case MLX4_EVENT_TYPE_COMM_CHANNEL: if (!mlx4_is_master(dev)) { mlx4_warn(dev, "Received comm channel event " diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 8873d6802c80..aec6f5802da5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1705,3 +1705,107 @@ int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port) MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); } EXPORT_SYMBOL_GPL(mlx4_wol_write); + +enum { + ADD_TO_MCG = 0x26, +}; + + +void mlx4_opreq_action(struct work_struct *work) +{ + struct mlx4_priv *priv = container_of(work, struct mlx4_priv, + opreq_task); + struct mlx4_dev *dev = &priv->dev; + int num_tasks = atomic_read(&priv->opreq_count); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + u32 *outbox; + u32 modifier; + u16 token; + u16 type_m; + u16 type; + int err; + u32 num_qps; + struct mlx4_qp qp; + int i; + u8 rem_mcg; + u8 prot; + +#define GET_OP_REQ_MODIFIER_OFFSET 0x08 +#define GET_OP_REQ_TOKEN_OFFSET 0x14 +#define GET_OP_REQ_TYPE_OFFSET 0x1a +#define GET_OP_REQ_DATA_OFFSET 0x20 + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + mlx4_err(dev, "Failed to allocate mailbox for GET_OP_REQ\n"); + return; + } + outbox = mailbox->buf; + + while (num_tasks) { + err = mlx4_cmd_box(dev, 0, mailbox->dma, 0, 0, + MLX4_CMD_GET_OP_REQ, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) { + mlx4_err(dev, "Failed to retreive required operation: %d\n", + err); + return; + } + MLX4_GET(modifier, outbox, GET_OP_REQ_MODIFIER_OFFSET); + MLX4_GET(token, outbox, GET_OP_REQ_TOKEN_OFFSET); + MLX4_GET(type, outbox, GET_OP_REQ_TYPE_OFFSET); + type_m = type >> 12; + type &= 0xfff; + + switch (type) { + case ADD_TO_MCG: + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + mlx4_warn(dev, "ADD MCG operation is not supported in DEVICE_MANAGED steering mode\n"); + err = EPERM; + break; + } + mgm = (struct mlx4_mgm *)((u8 *)(outbox) + + GET_OP_REQ_DATA_OFFSET); + num_qps = be32_to_cpu(mgm->members_count) & + MGM_QPN_MASK; + rem_mcg = ((u8 *)(&mgm->members_count))[0] & 1; + prot = ((u8 *)(&mgm->members_count))[0] >> 6; + + for (i = 0; i < num_qps; i++) { + qp.qpn = be32_to_cpu(mgm->qp[i]); + if (rem_mcg) + err = mlx4_multicast_detach(dev, &qp, + mgm->gid, + prot, 0); + else + err = mlx4_multicast_attach(dev, &qp, + mgm->gid, + mgm->gid[5] + , 0, prot, + NULL); + if (err) + break; + } + break; + default: + mlx4_warn(dev, "Bad type for required operation\n"); + err = EINVAL; + break; + } + err = mlx4_cmd(dev, 0, ((u32) err | cpu_to_be32(token) << 16), + 1, MLX4_CMD_GET_OP_REQ, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) { + mlx4_err(dev, "Failed to acknowledge required request: %d\n", + err); + goto out; + } + memset(outbox, 0, 0xffc); + num_tasks = atomic_dec_return(&priv->opreq_count); + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); +} diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index fdf41665a059..a0a368b7c939 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -220,5 +220,6 @@ int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev); int mlx4_NOP(struct mlx4_dev *dev); int mlx4_MOD_STAT_CFG(struct mlx4_dev *dev, struct mlx4_mod_stat_cfg *cfg); +void mlx4_opreq_action(struct work_struct *work); #endif /* MLX4_FW_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index e85af922dcdc..f1d818f9bb01 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1692,11 +1692,19 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) goto err_xrcd_table_free; } + if (!mlx4_is_slave(dev)) { + err = mlx4_init_mcg_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize multicast group table, aborting.\n"); + goto err_mr_table_free; + } + } + err = mlx4_init_eq_table(dev); if (err) { mlx4_err(dev, "Failed to initialize " "event queue table, aborting.\n"); - goto err_mr_table_free; + goto err_mcg_table_free; } err = mlx4_cmd_use_events(dev); @@ -1746,19 +1754,10 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) goto err_srq_table_free; } - if (!mlx4_is_slave(dev)) { - err = mlx4_init_mcg_table(dev); - if (err) { - mlx4_err(dev, "Failed to initialize " - "multicast group table, aborting.\n"); - goto err_qp_table_free; - } - } - err = mlx4_init_counters_table(dev); if (err && err != -ENOENT) { mlx4_err(dev, "Failed to initialize counters table, aborting.\n"); - goto err_mcg_table_free; + goto err_qp_table_free; } if (!mlx4_is_slave(dev)) { @@ -1803,9 +1802,6 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) err_counters_table_free: mlx4_cleanup_counters_table(dev); -err_mcg_table_free: - mlx4_cleanup_mcg_table(dev); - err_qp_table_free: mlx4_cleanup_qp_table(dev); @@ -1821,6 +1817,10 @@ err_cmd_poll: err_eq_table_free: mlx4_cleanup_eq_table(dev); +err_mcg_table_free: + if (!mlx4_is_slave(dev)) + mlx4_cleanup_mcg_table(dev); + err_mr_table_free: mlx4_cleanup_mr_table(dev); @@ -2197,6 +2197,9 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data) } } + atomic_set(&priv->opreq_count, 0); + INIT_WORK(&priv->opreq_task, mlx4_opreq_action); + /* * Now reset the HCA before we touch the PCI capabilities or * attempt a firmware command, since a boot ROM may have left @@ -2315,12 +2318,12 @@ err_port: mlx4_cleanup_port_info(&priv->port[port]); mlx4_cleanup_counters_table(dev); - mlx4_cleanup_mcg_table(dev); mlx4_cleanup_qp_table(dev); mlx4_cleanup_srq_table(dev); mlx4_cleanup_cq_table(dev); mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); + mlx4_cleanup_mcg_table(dev); mlx4_cleanup_mr_table(dev); mlx4_cleanup_xrcd_table(dev); mlx4_cleanup_pd_table(dev); @@ -2403,12 +2406,12 @@ static void mlx4_remove_one(struct pci_dev *pdev) RES_TR_FREE_SLAVES_ONLY); mlx4_cleanup_counters_table(dev); - mlx4_cleanup_mcg_table(dev); mlx4_cleanup_qp_table(dev); mlx4_cleanup_srq_table(dev); mlx4_cleanup_cq_table(dev); mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); + mlx4_cleanup_mcg_table(dev); mlx4_cleanup_mr_table(dev); mlx4_cleanup_xrcd_table(dev); mlx4_cleanup_pd_table(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index f3e804f2a35f..55f6245efb6c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -39,19 +39,8 @@ #include "mlx4.h" -#define MGM_QPN_MASK 0x00FFFFFF -#define MGM_BLCK_LB_BIT 30 - static const u8 zero_gid[16]; /* automatically initialized to 0 */ -struct mlx4_mgm { - __be32 next_gid_index; - __be32 members_count; - u32 reserved[2]; - u8 gid[16]; - __be32 qp[MLX4_MAX_QP_PER_MGM]; -}; - int mlx4_get_mgm_entry_size(struct mlx4_dev *dev) { return 1 << dev->oper_log_mgm_entry_size; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 17d9277e33ef..348bb8c7d9a7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -554,6 +554,17 @@ struct mlx4_mfunc { struct mlx4_mfunc_master_ctx master; }; +#define MGM_QPN_MASK 0x00FFFFFF +#define MGM_BLCK_LB_BIT 30 + +struct mlx4_mgm { + __be32 next_gid_index; + __be32 members_count; + u32 reserved[2]; + u8 gid[16]; + __be32 qp[MLX4_MAX_QP_PER_MGM]; +}; + struct mlx4_cmd { struct pci_pool *pool; void __iomem *hcr; @@ -802,6 +813,8 @@ struct mlx4_priv { u8 virt2phys_pkey[MLX4_MFUNC_MAX][MLX4_MAX_PORTS][MLX4_MAX_PORT_PKEYS]; __be64 slave_node_guids[MLX4_MFUNC_MAX]; + atomic_t opreq_count; + struct work_struct opreq_task; }; static inline struct mlx4_priv *mlx4_priv(struct mlx4_dev *dev) diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index bb1c8096a7eb..cd1fdf75103b 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -69,6 +69,7 @@ enum { MLX4_CMD_SET_ICM_SIZE = 0xffd, /*master notify fw on finish for slave's flr*/ MLX4_CMD_INFORM_FLR_DONE = 0x5b, + MLX4_CMD_GET_OP_REQ = 0x59, /* TPT commands */ MLX4_CMD_SW2HW_MPT = 0xd, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 52c23a892bab..6aebdfe0ed8b 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -207,6 +207,7 @@ enum mlx4_event { MLX4_EVENT_TYPE_CMD = 0x0a, MLX4_EVENT_TYPE_VEP_UPDATE = 0x19, MLX4_EVENT_TYPE_COMM_CHANNEL = 0x18, + MLX4_EVENT_TYPE_OP_REQUIRED = 0x1a, MLX4_EVENT_TYPE_FATAL_WARNING = 0x1b, MLX4_EVENT_TYPE_FLR_EVENT = 0x1c, MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT = 0x1d, -- cgit v1.2.3 From 66b52b0dc82c5c88d769dc1c7d44cf45d0deb07c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 29 Jul 2013 18:16:49 +0200 Subject: net: add ndo to get id of physical port of the device This patch adds a ndo for getting physical port of the device. Driver which is aware of being virtual function of some physical port should implement this ndo. This is applicable not only for IOV, but for other solutions (NPAR, multichannel) as well. Basically if there is possible to have multiple netdevs on the single hw port. Signed-off-by: Jiri Pirko Acked-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 20 ++++++++++++++++++++ net/core/dev.c | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3ca60b070ef0..875f869dc38a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -728,6 +728,16 @@ struct netdev_fcoe_hbainfo { }; #endif +#define MAX_PHYS_PORT_ID_LEN 32 + +/* This structure holds a unique identifier to identify the + * physical port used by a netdevice. + */ +struct netdev_phys_port_id { + unsigned char id[MAX_PHYS_PORT_ID_LEN]; + unsigned char id_len; +}; + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -932,6 +942,12 @@ struct netdev_fcoe_hbainfo { * that determine carrier state from physical hardware properties (eg * network cables) or protocol-dependent mechanisms (eg * USB_CDC_NOTIFY_NETWORK_CONNECTION) should NOT implement this function. + * + * int (*ndo_get_phys_port_id)(struct net_device *dev, + * struct netdev_phys_port_id *ppid); + * Called to get ID of physical port of this device. If driver does + * not implement this, it is assumed that the hw is not able to have + * multiple net devices on single physical port. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1060,6 +1076,8 @@ struct net_device_ops { struct nlmsghdr *nlh); int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier); + int (*ndo_get_phys_port_id)(struct net_device *dev, + struct netdev_phys_port_id *ppid); }; /* @@ -2315,6 +2333,8 @@ extern int dev_set_mac_address(struct net_device *, struct sockaddr *); extern int dev_change_carrier(struct net_device *, bool new_carrier); +extern int dev_get_phys_port_id(struct net_device *dev, + struct netdev_phys_port_id *ppid); extern int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq); diff --git a/net/core/dev.c b/net/core/dev.c index dfd9f5d56ae0..58eb802584b9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4988,6 +4988,24 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier) } EXPORT_SYMBOL(dev_change_carrier); +/** + * dev_get_phys_port_id - Get device physical port ID + * @dev: device + * @ppid: port ID + * + * Get device physical port ID + */ +int dev_get_phys_port_id(struct net_device *dev, + struct netdev_phys_port_id *ppid) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_get_phys_port_id) + return -EOPNOTSUPP; + return ops->ndo_get_phys_port_id(dev, ppid); +} +EXPORT_SYMBOL(dev_get_phys_port_id); + /** * dev_new_index - allocate an ifindex * @net: the applicable net namespace -- cgit v1.2.3 From 59da381ee2afc806f85becf3aa64ffc952355552 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 31 Jul 2013 06:53:21 +0000 Subject: PCI: move enum pcie_link_width into pci.h pcie_link_width is the enum used to define the link width values for a pcie device. This enum should not be contained solely in pci_hotplug.h, and this patch moves it next to pci_bus_speed in pci.h Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Acked-by: Bjorn Helgaas Signed-off-by: Jeff Kirsher --- include/linux/pci.h | 13 +++++++++++++ include/linux/pci_hotplug.h | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..a0bf22d816cf 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -183,6 +183,19 @@ enum pci_bus_flags { PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2, }; +/* These values come from the PCI Express Spec */ +enum pcie_link_width { + PCIE_LNK_WIDTH_RESRV = 0x00, + PCIE_LNK_X1 = 0x01, + PCIE_LNK_X2 = 0x02, + PCIE_LNK_X4 = 0x04, + PCIE_LNK_X8 = 0x08, + PCIE_LNK_X12 = 0x0C, + PCIE_LNK_X16 = 0x10, + PCIE_LNK_X32 = 0x20, + PCIE_LNK_WIDTH_UNKNOWN = 0xFF, +}; + /* Based on the PCI Hotplug Spec, but some values are made up by us */ enum pci_bus_speed { PCI_SPEED_33MHz = 0x00, diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 8db71dcd6337..64e61e05d9a0 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -28,19 +28,6 @@ #ifndef _PCI_HOTPLUG_H #define _PCI_HOTPLUG_H -/* These values come from the PCI Express Spec */ -enum pcie_link_width { - PCIE_LNK_WIDTH_RESRV = 0x00, - PCIE_LNK_X1 = 0x01, - PCIE_LNK_X2 = 0x02, - PCIE_LNK_X4 = 0x04, - PCIE_LNK_X8 = 0x08, - PCIE_LNK_X12 = 0x0C, - PCIE_LNK_X16 = 0x10, - PCIE_LNK_X32 = 0x20, - PCIE_LNK_WIDTH_UNKNOWN = 0xFF, -}; - /** * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use * @owner: The module owner of this structure -- cgit v1.2.3 From 81377c8d3563e7aec5c8baaaacacb48034f430a0 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 31 Jul 2013 06:53:26 +0000 Subject: PCI: Add function to obtain minimum link width and speed A PCI Express device can potentially report a link width and speed which it will not properly fulfill due to being plugged into a slower link higher in the chain. This function walks up the PCI bus chain and calculates the minimum link width and speed of this entire chain. This can be useful to enable a device to determine if it has enough bandwidth for optimum functionality. Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Acked-by: Bjorn Helgaas Signed-off-by: Jeff Kirsher --- drivers/pci/pci.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 45 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e37fea6e178d..c71e78c46705 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3578,6 +3578,49 @@ int pcie_set_mps(struct pci_dev *dev, int mps) PCI_EXP_DEVCTL_PAYLOAD, v); } +/** + * pcie_get_minimum_link - determine minimum link settings of a PCI device + * @dev: PCI device to query + * @speed: storage for minimum speed + * @width: storage for minimum width + * + * This function will walk up the PCI device chain and determine the minimum + * link width and speed of the device. + */ +int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + int ret; + + *speed = PCI_SPEED_UNKNOWN; + *width = PCIE_LNK_WIDTH_UNKNOWN; + + while (dev) { + u16 lnksta; + enum pci_bus_speed next_speed; + enum pcie_link_width next_width; + + ret = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + return ret; + + next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS]; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + if (next_speed < *speed) + *speed = next_speed; + + if (next_width < *width) + *width = next_width; + + dev = dev->bus->self; + } + + return 0; +} +EXPORT_SYMBOL(pcie_get_minimum_link); + /** * pci_select_bars - Make BAR mask from the type of resource * @dev: the PCI device for which BAR mask is made diff --git a/include/linux/pci.h b/include/linux/pci.h index a0bf22d816cf..2edbee64aeba 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -934,6 +934,8 @@ int pcie_get_readrq(struct pci_dev *dev); int pcie_set_readrq(struct pci_dev *dev, int rq); int pcie_get_mps(struct pci_dev *dev); int pcie_set_mps(struct pci_dev *dev, int mps); +int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width); int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); -- cgit v1.2.3 From 312a0c16c1fa9dd7cb5af413cf73b2fe2806c962 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 28 Jul 2013 22:54:08 +0200 Subject: netfilter: nf_conntrack: constify sk_buff argument to nf_ct_attach() Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 4 ++-- net/netfilter/core.c | 7 ++++--- net/netfilter/nf_conntrack_core.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index de70f7b45b68..f4bbf2cd22d8 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -314,8 +314,8 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) #endif /*CONFIG_NETFILTER*/ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu; -extern void nf_ct_attach(struct sk_buff *, struct sk_buff *); +extern void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) __rcu; +extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; struct nf_conn; diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 2217363ab422..593b16ea45e0 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -234,12 +234,13 @@ EXPORT_SYMBOL(skb_make_writable); /* This does not belong here, but locally generated errors need it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated with it. */ -void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu __read_mostly; +void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) + __rcu __read_mostly; EXPORT_SYMBOL(ip_ct_attach); -void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) +void nf_ct_attach(struct sk_buff *new, const struct sk_buff *skb) { - void (*attach)(struct sk_buff *, struct sk_buff *); + void (*attach)(struct sk_buff *, const struct sk_buff *); if (skb->nfct) { rcu_read_lock(); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0283baedcdfb..d32afaff72f8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1192,7 +1192,7 @@ EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size); #endif /* Used by ipt_REJECT and ip6t_REJECT. */ -static void nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) +static void nf_conntrack_attach(struct sk_buff *nskb, const struct sk_buff *skb) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; -- cgit v1.2.3 From 2d89c68ac78ae432038ef23371d2fa949d725d43 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 28 Jul 2013 22:54:10 +0200 Subject: netfilter: nf_nat: change sequence number adjustments to 32 bits Using 16 bits is too small, when many adjustments happen the offsets might overflow and break the connection. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 2 +- include/net/netfilter/nf_conntrack.h | 2 +- include/net/netfilter/nf_nat.h | 2 +- include/net/netfilter/nf_nat_helper.h | 6 +++--- net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_conntrack_proto_tcp.c | 4 ++-- net/netfilter/nf_nat_helper.c | 8 ++++---- 7 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index f4bbf2cd22d8..655d5d198d49 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -330,7 +330,7 @@ extern struct nfq_ct_hook __rcu *nfq_ct_hook; struct nfq_ct_nat_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, - u32 ctinfo, int off); + u32 ctinfo, s32 off); }; extern struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook; #else diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 939aced35a02..e5eb8b62538c 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -234,7 +234,7 @@ static inline bool nf_ct_kill(struct nf_conn *ct) } /* These are for NAT. Icky. */ -extern s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, +extern s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index ad14a799fd2e..e2441413675c 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -19,7 +19,7 @@ struct nf_nat_seq { u_int32_t correction_pos; /* sequence number offset before and after last modification */ - int16_t offset_before, offset_after; + int32_t offset_before, offset_after; }; #include diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index b4d6bfc2af03..194c34794923 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -41,7 +41,7 @@ extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, extern void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s16 off); + __be32 seq, s32 off); extern int nf_nat_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -56,11 +56,11 @@ extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, extern void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this); -extern s16 nf_nat_get_offset(const struct nf_conn *ct, +extern s32 nf_nat_get_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - u32 dir, int off); + u32 dir, s32 off); #endif diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 089e408676fa..0934611ff9f3 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1695,7 +1695,7 @@ err_stat: return ret; } -s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, +s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); EXPORT_SYMBOL_GPL(nf_ct_nat_offset); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 7dcc376eea5f..8f308d896324 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -496,7 +496,7 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, } #ifdef CONFIG_NF_NAT_NEEDED -static inline s16 nat_offset(const struct nf_conn *ct, +static inline s32 nat_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq) { @@ -525,7 +525,7 @@ static bool tcp_in_window(const struct nf_conn *ct, struct ip_ct_tcp_state *receiver = &state->seen[!dir]; const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple; __u32 seq, ack, sack, end, win, swin; - s16 receiver_offset; + s32 receiver_offset; bool res; /* diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c index a7262ed055c6..ff4a589e3e39 100644 --- a/net/netfilter/nf_nat_helper.c +++ b/net/netfilter/nf_nat_helper.c @@ -68,13 +68,13 @@ adjust_tcp_sequence(u32 seq, } /* Get the offset value, for conntrack */ -s16 nf_nat_get_offset(const struct nf_conn *ct, +s32 nf_nat_get_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq) { struct nf_conn_nat *nat = nfct_nat(ct); struct nf_nat_seq *this_way; - s16 offset; + s32 offset; if (!nat) return 0; @@ -143,7 +143,7 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra) } void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s16 off) + __be32 seq, s32 off) { if (!off) return; @@ -370,7 +370,7 @@ nf_nat_seq_adjust(struct sk_buff *skb, struct tcphdr *tcph; int dir; __be32 newseq, newack; - s16 seqoff, ackoff; + s32 seqoff, ackoff; struct nf_conn_nat *nat = nfct_nat(ct); struct nf_nat_seq *this_way, *other_way; int res; -- cgit v1.2.3 From c0155b2da4cd6583b1b729451249ca346e1c05a2 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Wed, 31 Jul 2013 13:39:45 +0400 Subject: tcp: Remove unused tcpct declarations and comments Remove declaration, 4 defines and confusing comment that are no longer used since 1a2c6181c4 ("tcp: Remove TCPCT"). Signed-off-by: Dmitry Popov Acked-by: Christoph Paasch Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 4 ---- net/ipv4/tcp.c | 4 ---- 3 files changed, 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 9640803a17a7..d68633452d9b 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -107,7 +107,6 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt) * only four options will fit in a standard TCP header */ #define TCP_NUM_SACKS 4 -struct tcp_cookie_values; struct tcp_request_sock_ops; struct tcp_request_sock { diff --git a/include/net/tcp.h b/include/net/tcp.h index 18fc999dae3c..27b652f379ef 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -192,10 +192,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_TIMESTAMP 10 #define TCPOLEN_MD5SIG 18 #define TCPOLEN_EXP_FASTOPEN_BASE 4 -#define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */ -#define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */ -#define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MIN) -#define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MAX) /* But this is what stacks really send out. */ #define TCPOLEN_TSTAMP_ALIGNED 12 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c27e81392398..ab64eea042fa 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -410,10 +410,6 @@ void tcp_init_sock(struct sock *sk) icsk->icsk_sync_mss = tcp_sync_mss; - /* Presumed zeroed, in order of appearance: - * cookie_in_always, cookie_out_never, - * s_data_constant, s_data_in, s_data_out - */ sk->sk_sndbuf = sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sysctl_tcp_rmem[1]; -- cgit v1.2.3 From c34a761231b56dea4bd205cb9c29ffe37475d232 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 30 Jul 2013 16:11:15 -0700 Subject: net: skb_orphan() changes It is illegal to set skb->sk without corresponding destructor. Its therefore safe for skb_orphan() to not clear skb->sk if skb->destructor is not set. Also avoid clearing skb->destructor if already NULL. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5afefa01a13c..a95547adff25 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1805,10 +1805,11 @@ static inline void pskb_trim_unique(struct sk_buff *skb, unsigned int len) */ static inline void skb_orphan(struct sk_buff *skb) { - if (skb->destructor) + if (skb->destructor) { skb->destructor(skb); - skb->destructor = NULL; - skb->sk = NULL; + skb->destructor = NULL; + skb->sk = NULL; + } } /** -- cgit v1.2.3 From dcb7a6ce0a08012a17cca34711a02017abed554b Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 26 Jul 2013 17:02:29 -0700 Subject: ieee80211: add definition for interworking support IEEE802.11u interworking support is advertised via extended capabilities IE bit 31. This is 7th bit of 4th byte of extended capabilities. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b0dc87a2a376..22c909413d32 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1860,6 +1860,11 @@ enum ieee80211_tdls_actioncode { WLAN_TDLS_DISCOVERY_REQUEST = 10, }; +/* Interworking capabilities are set in 7th bit of 4th byte of the + * @WLAN_EID_EXT_CAPABILITY information element + */ +#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED BIT(7) + /* * TDLS capabililites to be enabled in the 5th byte of the * @WLAN_EID_EXT_CAPABILITY information element -- cgit v1.2.3 From 9e2bc79bce58a1ce0005015c9351b3bcaaa02e5c Mon Sep 17 00:00:00 2001 From: Fred Zhou Date: Thu, 1 Aug 2013 14:16:28 +0800 Subject: ieee80211: add definition for 802.11ac information elements Add element IDs for Extended BSS Load, VHT TX Power Envelope, AID, and Quiet Channel. Signed-off-by: Fred Zhou Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 22c909413d32..b3ce299782af 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1709,6 +1709,10 @@ enum ieee80211_eid { WLAN_EID_OPMODE_NOTIF = 199, WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194, WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196, + WLAN_EID_EXTENDED_BSS_LOAD = 193, + WLAN_EID_VHT_TX_POWER_ENVELOPE = 195, + WLAN_EID_AID = 197, + WLAN_EID_QUIET_CHANNEL = 198, /* 802.11ad */ WLAN_EID_NON_TX_BSSID_CAP = 83, -- cgit v1.2.3 From 376c7311bdb6efea3322310333576a04d73fbe4c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 1 Aug 2013 11:43:08 -0700 Subject: net: add a temporary sanity check in skb_orphan() David suggested to add a BUG_ON() to catch if some layer sets skb->sk pointer without a corresponding destructor. As skb can sit in a queue, it's mandatory to make sure the socket cannot disappear, and it's usually done by taking a reference on the socket, then releasing it from the skb destructor. This patch is a follow-up to commit c34a761231b5 ("net: skb_orphan() changes") and will be reverted after catching all possible offenders if any. Suggested-by: David Miller Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a95547adff25..d48de687c21f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1809,6 +1809,8 @@ static inline void skb_orphan(struct sk_buff *skb) skb->destructor(skb); skb->destructor = NULL; skb->sk = NULL; + } else { + BUG_ON(skb->sk); } } -- cgit v1.2.3 From 574e2af7c0af3273836def5e66f236521bb433c9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 1 Aug 2013 16:17:48 -0700 Subject: include: Convert ethernet mac address declarations to use ETH_ALEN It's convenient to have ethernet mac addresses use ETH_ALEN to be able to grep for them a bit easier and also to ensure that the addresses are __aligned(2). Add #include as necessary. Signed-off-by: Joe Perches Acked-by: Mauro Carvalho Chehab Signed-off-by: David S. Miller --- include/linux/dm9000.h | 4 ++- include/linux/fs_enet_pd.h | 3 ++- include/linux/ieee80211.h | 59 +++++++++++++++++++++-------------------- include/linux/mlx4/device.h | 11 ++++---- include/linux/mlx4/qp.h | 5 ++-- include/linux/mv643xx_eth.h | 3 ++- include/linux/sh_eth.h | 3 ++- include/linux/smsc911x.h | 3 ++- include/linux/uwb/spec.h | 5 ++-- include/media/tveeprom.h | 4 ++- include/net/irda/irlan_common.h | 3 ++- 11 files changed, 58 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dm9000.h b/include/linux/dm9000.h index 96e87693d933..841925fbfe8a 100644 --- a/include/linux/dm9000.h +++ b/include/linux/dm9000.h @@ -14,6 +14,8 @@ #ifndef __DM9000_PLATFORM_DATA #define __DM9000_PLATFORM_DATA __FILE__ +#include + /* IO control flags */ #define DM9000_PLATF_8BITONLY (0x0001) @@ -27,7 +29,7 @@ struct dm9000_plat_data { unsigned int flags; - unsigned char dev_addr[6]; + unsigned char dev_addr[ETH_ALEN]; /* allow replacement IO routines */ diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 51b793466ff3..343d82a54468 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -18,6 +18,7 @@ #include #include +#include #include #define FS_ENET_NAME "fs_enet" @@ -135,7 +136,7 @@ struct fs_platform_info { const struct fs_mii_bus_info *bus_info; int rx_ring, tx_ring; /* number of buffers on rx */ - __u8 macaddr[6]; /* mac address */ + __u8 macaddr[ETH_ALEN]; /* mac address */ int rx_copybreak; /* limit we copy small frames */ int use_napi; /* use NAPI */ int napi_weight; /* NAPI weight */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b0dc87a2a376..4e101af9c1ff 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -16,6 +16,7 @@ #define LINUX_IEEE80211_H #include +#include #include /* @@ -209,28 +210,28 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2) struct ieee80211_hdr { __le16 frame_control; __le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; __le16 seq_ctrl; - u8 addr4[6]; + u8 addr4[ETH_ALEN]; } __packed __aligned(2); struct ieee80211_hdr_3addr { __le16 frame_control; __le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; __le16 seq_ctrl; } __packed __aligned(2); struct ieee80211_qos_hdr { __le16 frame_control; __le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; __le16 seq_ctrl; __le16 qos_ctrl; } __packed __aligned(2); @@ -608,8 +609,8 @@ struct ieee80211s_hdr { u8 flags; u8 ttl; __le32 seqnum; - u8 eaddr1[6]; - u8 eaddr2[6]; + u8 eaddr1[ETH_ALEN]; + u8 eaddr2[ETH_ALEN]; } __packed __aligned(2); /* Mesh flags */ @@ -758,7 +759,7 @@ struct ieee80211_rann_ie { u8 rann_flags; u8 rann_hopcount; u8 rann_ttl; - u8 rann_addr[6]; + u8 rann_addr[ETH_ALEN]; __le32 rann_seq; __le32 rann_interval; __le32 rann_metric; @@ -802,9 +803,9 @@ enum ieee80211_vht_opmode_bits { struct ieee80211_mgmt { __le16 frame_control; __le16 duration; - u8 da[6]; - u8 sa[6]; - u8 bssid[6]; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 bssid[ETH_ALEN]; __le16 seq_ctrl; union { struct { @@ -833,7 +834,7 @@ struct ieee80211_mgmt { struct { __le16 capab_info; __le16 listen_interval; - u8 current_ap[6]; + u8 current_ap[ETH_ALEN]; /* followed by SSID and Supported rates */ u8 variable[0]; } __packed reassoc_req; @@ -966,21 +967,21 @@ struct ieee80211_vendor_ie { struct ieee80211_rts { __le16 frame_control; __le16 duration; - u8 ra[6]; - u8 ta[6]; + u8 ra[ETH_ALEN]; + u8 ta[ETH_ALEN]; } __packed __aligned(2); struct ieee80211_cts { __le16 frame_control; __le16 duration; - u8 ra[6]; + u8 ra[ETH_ALEN]; } __packed __aligned(2); struct ieee80211_pspoll { __le16 frame_control; __le16 aid; - u8 bssid[6]; - u8 ta[6]; + u8 bssid[ETH_ALEN]; + u8 ta[ETH_ALEN]; } __packed __aligned(2); /* TDLS */ @@ -989,14 +990,14 @@ struct ieee80211_pspoll { struct ieee80211_tdls_lnkie { u8 ie_type; /* Link Identifier IE */ u8 ie_len; - u8 bssid[6]; - u8 init_sta[6]; - u8 resp_sta[6]; + u8 bssid[ETH_ALEN]; + u8 init_sta[ETH_ALEN]; + u8 resp_sta[ETH_ALEN]; } __packed; struct ieee80211_tdls_data { - u8 da[6]; - u8 sa[6]; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; __be16 ether_type; u8 payload_type; u8 category; @@ -1090,8 +1091,8 @@ struct ieee80211_p2p_noa_attr { struct ieee80211_bar { __le16 frame_control; __le16 duration; - __u8 ra[6]; - __u8 ta[6]; + __u8 ra[ETH_ALEN]; + __u8 ta[ETH_ALEN]; __le16 control; __le16 start_seq_num; } __packed; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 6aebdfe0ed8b..09ef2f448a00 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -33,6 +33,7 @@ #ifndef MLX4_DEVICE_H #define MLX4_DEVICE_H +#include #include #include #include @@ -620,7 +621,7 @@ struct mlx4_eth_av { u8 dgid[16]; u32 reserved4[2]; __be16 vlan; - u8 mac[6]; + u8 mac[ETH_ALEN]; }; union mlx4_ext_av { @@ -914,10 +915,10 @@ enum mlx4_net_trans_promisc_mode { }; struct mlx4_spec_eth { - u8 dst_mac[6]; - u8 dst_mac_msk[6]; - u8 src_mac[6]; - u8 src_mac_msk[6]; + u8 dst_mac[ETH_ALEN]; + u8 dst_mac_msk[ETH_ALEN]; + u8 src_mac[ETH_ALEN]; + u8 src_mac_msk[ETH_ALEN]; u8 ether_type_enable; __be16 ether_type; __be16 vlan_id_msk; diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 262deac02c9e..6d351473c292 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -34,6 +34,7 @@ #define MLX4_QP_H #include +#include #include @@ -143,7 +144,7 @@ struct mlx4_qp_path { u8 feup; u8 fvl_rx; u8 reserved4[2]; - u8 dmac[6]; + u8 dmac[ETH_ALEN]; }; enum { /* fl */ @@ -318,7 +319,7 @@ struct mlx4_wqe_datagram_seg { __be32 dqpn; __be32 qkey; __be16 vlan; - u8 mac[6]; + u8 mac[ETH_ALEN]; }; struct mlx4_wqe_lso_seg { diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 6e8215b15998..61a0da38d0cb 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -6,6 +6,7 @@ #define __LINUX_MV643XX_ETH_H #include +#include #define MV643XX_ETH_SHARED_NAME "mv643xx_eth" #define MV643XX_ETH_NAME "mv643xx_eth_port" @@ -48,7 +49,7 @@ struct mv643xx_eth_platform_data { * Use this MAC address if it is valid, overriding the * address that is already in the hardware. */ - u8 mac_addr[6]; + u8 mac_addr[ETH_ALEN]; /* * If speed is 0, autonegotiation is enabled. diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index fc305713fc6d..6205eeba392b 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -2,6 +2,7 @@ #define __ASM_SH_ETH_H__ #include +#include enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN}; enum { @@ -18,7 +19,7 @@ struct sh_eth_plat_data { phy_interface_t phy_interface; void (*set_mdio_gate)(void *addr); - unsigned char mac_addr[6]; + unsigned char mac_addr[ETH_ALEN]; unsigned no_ether_link:1; unsigned ether_link_active_low:1; unsigned needs_init:1; diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h index 4dde70e74822..eec3efd19beb 100644 --- a/include/linux/smsc911x.h +++ b/include/linux/smsc911x.h @@ -22,6 +22,7 @@ #define __LINUX_SMSC911X_H__ #include +#include /* platform_device configuration data, should be assigned to * the platform_device's dev.platform_data */ @@ -31,7 +32,7 @@ struct smsc911x_platform_config { unsigned int flags; unsigned int shift; phy_interface_t phy_interface; - unsigned char mac[6]; + unsigned char mac[ETH_ALEN]; }; /* Constants for platform_device irq polarity configuration */ diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h index b52e44f1bd33..0df24bfcdb38 100644 --- a/include/linux/uwb/spec.h +++ b/include/linux/uwb/spec.h @@ -32,6 +32,7 @@ #include #include +#include #define i1480_FW 0x00000303 /* #define i1480_FW 0x00000302 */ @@ -130,7 +131,7 @@ enum { UWB_DRP_BACKOFF_WIN_MAX = 16 }; * it is also used to define headers sent down and up the wire/radio). */ struct uwb_mac_addr { - u8 data[6]; + u8 data[ETH_ALEN]; } __attribute__((packed)); @@ -568,7 +569,7 @@ struct uwb_rc_evt_confirm { /* Device Address Management event. [WHCI] section 3.1.3.2. */ struct uwb_rc_evt_dev_addr_mgmt { struct uwb_rceb rceb; - u8 baAddr[6]; + u8 baAddr[ETH_ALEN]; u8 bResultCode; } __attribute__((packed)); diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h index 4a1191abd936..f7119ee3977b 100644 --- a/include/media/tveeprom.h +++ b/include/media/tveeprom.h @@ -12,6 +12,8 @@ enum tveeprom_audio_processor { TVEEPROM_AUDPROC_OTHER, }; +#include + struct tveeprom { u32 has_radio; /* If has_ir == 0, then it is unknown what the IR capabilities are, @@ -40,7 +42,7 @@ struct tveeprom { u32 revision; u32 serial_number; char rev_str[5]; - u8 MAC_address[6]; + u8 MAC_address[ETH_ALEN]; }; void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, diff --git a/include/net/irda/irlan_common.h b/include/net/irda/irlan_common.h index 0af8b8dfbc22..550c2d6ec7ff 100644 --- a/include/net/irda/irlan_common.h +++ b/include/net/irda/irlan_common.h @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -161,7 +162,7 @@ struct irlan_provider_cb { int access_type; /* Access type */ __u16 send_arb_val; - __u8 mac_address[6]; /* Generated MAC address for peer device */ + __u8 mac_address[ETH_ALEN]; /* Generated MAC address for peer device */ }; /* -- cgit v1.2.3 From e7f1935c11269bc53cd52425b1025657adddb839 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 25 Jul 2013 21:45:17 +0200 Subject: wireless: make TU conversion macros available A few places in the code (mac80211 and iwlmvm) use the same TU_TO_JIFFIES() macro and could use TU_TO_EXP_TIME() that mac80211 has. Make these available to everyone and use them. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/time-event.c | 7 ++----- include/linux/ieee80211.h | 4 ++++ net/mac80211/ieee80211_i.h | 3 --- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index ad9bbca99213..9f100363b177 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -73,7 +73,6 @@ #include "iwl-prph.h" /* A TimeUnit is 1024 microsecond */ -#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024)) #define MSEC_TO_TU(_msec) (_msec*1000/1024) /* @@ -191,8 +190,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, iwl_mvm_te_clear_data(mvm, te_data); } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) { te_data->running = true; - te_data->end_jiffies = jiffies + - TU_TO_JIFFIES(te_data->duration); + te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); @@ -329,8 +327,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); if (te_data->running && - time_after(te_data->end_jiffies, - jiffies + TU_TO_JIFFIES(min_duration))) { + time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", jiffies_to_msecs(te_data->end_jiffies - jiffies)); return; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b3ce299782af..23a8877f4ded 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2288,4 +2288,8 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, return !!(tim->virtual_map[index] & mask); } +/* convert time units */ +#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) +#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) + #endif /* LINUX_IEEE80211_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e94c84050e9c..b6186517ec56 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -53,9 +53,6 @@ struct ieee80211_local; * increased memory use (about 2 kB of RAM per entry). */ #define IEEE80211_FRAGMENT_MAX 4 -#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) -#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) - /* power level hasn't been configured (or set to automatic) */ #define IEEE80211_UNSET_POWER_LEVEL INT_MIN -- cgit v1.2.3 From b4bf07771faaf959b0a916d35b1b930c030e30a8 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 6 Aug 2013 17:45:03 +0800 Subject: net: move iov_pages() to net/core/iovec.c To let it be reused and reduce code duplication. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 23 ----------------------- drivers/net/tun.c | 23 ----------------------- include/linux/socket.h | 2 ++ net/core/iovec.c | 24 ++++++++++++++++++++++++ 4 files changed, 26 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index a98fb0ed6aef..dfec20df17ba 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -698,29 +698,6 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, return 0; } -static unsigned long iov_pages(const struct iovec *iv, int offset, - unsigned long nr_segs) -{ - unsigned long seg, base; - int pages = 0, len, size; - - while (nr_segs && (offset >= iv->iov_len)) { - offset -= iv->iov_len; - ++iv; - --nr_segs; - } - - for (seg = 0; seg < nr_segs; seg++) { - base = (unsigned long)iv[seg].iov_base + offset; - len = iv[seg].iov_len - offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - pages += size; - offset = 0; - } - - return pages; -} - /* Get packet from user space buffer */ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, const struct iovec *iv, unsigned long total_len, diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5dce262f538e..18527ef0cc75 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1041,29 +1041,6 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, return 0; } -static unsigned long iov_pages(const struct iovec *iv, int offset, - unsigned long nr_segs) -{ - unsigned long seg, base; - int pages = 0, len, size; - - while (nr_segs && (offset >= iv->iov_len)) { - offset -= iv->iov_len; - ++iv; - --nr_segs; - } - - for (seg = 0; seg < nr_segs; seg++) { - base = (unsigned long)iv[seg].iov_base + offset; - len = iv[seg].iov_len - offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - pages += size; - offset = 0; - } - - return pages; -} - /* Get packet from user space buffer */ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, void *msg_control, const struct iovec *iv, diff --git a/include/linux/socket.h b/include/linux/socket.h index 230c04bda3e2..445ef7519dc2 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -313,6 +313,8 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, unsigned int len, __wsum *csump); +extern unsigned long iov_pages(const struct iovec *iov, int offset, + unsigned long nr_segs); extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode); extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, diff --git a/net/core/iovec.c b/net/core/iovec.c index de178e462682..b77eeecc0011 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -212,3 +212,27 @@ out_fault: goto out; } EXPORT_SYMBOL(csum_partial_copy_fromiovecend); + +unsigned long iov_pages(const struct iovec *iov, int offset, + unsigned long nr_segs) +{ + unsigned long seg, base; + int pages = 0, len, size; + + while (nr_segs && (offset >= iov->iov_len)) { + offset -= iov->iov_len; + ++iov; + --nr_segs; + } + + for (seg = 0; seg < nr_segs; seg++) { + base = (unsigned long)iov[seg].iov_base + offset; + len = iov[seg].iov_len - offset; + size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; + pages += size; + offset = 0; + } + + return pages; +} +EXPORT_SYMBOL(iov_pages); -- cgit v1.2.3 From c3bdeb5c7cc073ccf5ff9624642022a8613a956e Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 6 Aug 2013 17:45:04 +0800 Subject: net: move zerocopy_sg_from_iovec() to net/core/datagram.c To let it be reused and reduce code duplication. Also document this function. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 80 ------------------------------------------- drivers/net/tun.c | 80 ------------------------------------------- include/linux/skbuff.h | 4 +++ net/core/datagram.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 160 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index dfec20df17ba..182364abfa35 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -536,86 +536,6 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad, return skb; } -/* set skb frags from iovec, this can move to core network code for reuse */ -static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, - int offset, size_t count) -{ - int len = iov_length(from, count) - offset; - int copy = skb_headlen(skb); - int size, offset1 = 0; - int i = 0; - - /* Skip over from offset */ - while (count && (offset >= from->iov_len)) { - offset -= from->iov_len; - ++from; - --count; - } - - /* copy up to skb headlen */ - while (count && (copy > 0)) { - size = min_t(unsigned int, copy, from->iov_len - offset); - if (copy_from_user(skb->data + offset1, from->iov_base + offset, - size)) - return -EFAULT; - if (copy > size) { - ++from; - --count; - offset = 0; - } else - offset += size; - copy -= size; - offset1 += size; - } - - if (len == offset1) - return 0; - - while (count--) { - struct page *page[MAX_SKB_FRAGS]; - int num_pages; - unsigned long base; - unsigned long truesize; - - len = from->iov_len - offset; - if (!len) { - offset = 0; - ++from; - continue; - } - base = (unsigned long)from->iov_base + offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - if (i + size > MAX_SKB_FRAGS) - return -EMSGSIZE; - num_pages = get_user_pages_fast(base, size, 0, &page[i]); - if (num_pages != size) { - int j; - - for (j = 0; j < num_pages; j++) - put_page(page[i + j]); - return -EFAULT; - } - truesize = size * PAGE_SIZE; - skb->data_len += len; - skb->len += len; - skb->truesize += truesize; - atomic_add(truesize, &skb->sk->sk_wmem_alloc); - while (len) { - int off = base & ~PAGE_MASK; - int size = min_t(int, len, PAGE_SIZE - off); - __skb_fill_page_desc(skb, i, page[i], off, size); - skb_shinfo(skb)->nr_frags++; - /* increase sk_wmem_alloc */ - base += size; - len -= size; - i++; - } - offset = 0; - ++from; - } - return 0; -} - /* * macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should * be shared with the tun/tap driver. diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 18527ef0cc75..b1630479d4a1 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -961,86 +961,6 @@ static struct sk_buff *tun_alloc_skb(struct tun_file *tfile, return skb; } -/* set skb frags from iovec, this can move to core network code for reuse */ -static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, - int offset, size_t count) -{ - int len = iov_length(from, count) - offset; - int copy = skb_headlen(skb); - int size, offset1 = 0; - int i = 0; - - /* Skip over from offset */ - while (count && (offset >= from->iov_len)) { - offset -= from->iov_len; - ++from; - --count; - } - - /* copy up to skb headlen */ - while (count && (copy > 0)) { - size = min_t(unsigned int, copy, from->iov_len - offset); - if (copy_from_user(skb->data + offset1, from->iov_base + offset, - size)) - return -EFAULT; - if (copy > size) { - ++from; - --count; - offset = 0; - } else - offset += size; - copy -= size; - offset1 += size; - } - - if (len == offset1) - return 0; - - while (count--) { - struct page *page[MAX_SKB_FRAGS]; - int num_pages; - unsigned long base; - unsigned long truesize; - - len = from->iov_len - offset; - if (!len) { - offset = 0; - ++from; - continue; - } - base = (unsigned long)from->iov_base + offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - if (i + size > MAX_SKB_FRAGS) - return -EMSGSIZE; - num_pages = get_user_pages_fast(base, size, 0, &page[i]); - if (num_pages != size) { - int j; - - for (j = 0; j < num_pages; j++) - put_page(page[i + j]); - return -EFAULT; - } - truesize = size * PAGE_SIZE; - skb->data_len += len; - skb->len += len; - skb->truesize += truesize; - atomic_add(truesize, &skb->sk->sk_wmem_alloc); - while (len) { - int off = base & ~PAGE_MASK; - int size = min_t(int, len, PAGE_SIZE - off); - __skb_fill_page_desc(skb, i, page[i], off, size); - skb_shinfo(skb)->nr_frags++; - /* increase sk_wmem_alloc */ - base += size; - len -= size; - i++; - } - offset = 0; - ++from; - } - return 0; -} - /* Get packet from user space buffer */ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, void *msg_control, const struct iovec *iv, diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ae46475d4568..5ac96f31d546 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2359,6 +2359,10 @@ extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, const struct iovec *from, int from_offset, int len); +extern int zerocopy_sg_from_iovec(struct sk_buff *skb, + const struct iovec *frm, + int offset, + size_t count); extern int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset, const struct iovec *to, diff --git a/net/core/datagram.c b/net/core/datagram.c index 8ab48cd89559..f987faef21ce 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -573,6 +573,99 @@ fault: } EXPORT_SYMBOL(skb_copy_datagram_from_iovec); +/** + * zerocopy_sg_from_iovec - Build a zerocopy datagram from an iovec + * @skb: buffer to copy + * @from: io vector to copy to + * @offset: offset in the io vector to start copying from + * @count: amount of vectors to copy to buffer from + * + * The function will first copy up to headlen, and then pin the userspace + * pages and build frags through them. + * + * Returns 0, -EFAULT or -EMSGSIZE. + * Note: the iovec is not modified during the copy + */ +int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, + int offset, size_t count) +{ + int len = iov_length(from, count) - offset; + int copy = skb_headlen(skb); + int size, offset1 = 0; + int i = 0; + + /* Skip over from offset */ + while (count && (offset >= from->iov_len)) { + offset -= from->iov_len; + ++from; + --count; + } + + /* copy up to skb headlen */ + while (count && (copy > 0)) { + size = min_t(unsigned int, copy, from->iov_len - offset); + if (copy_from_user(skb->data + offset1, from->iov_base + offset, + size)) + return -EFAULT; + if (copy > size) { + ++from; + --count; + offset = 0; + } else + offset += size; + copy -= size; + offset1 += size; + } + + if (len == offset1) + return 0; + + while (count--) { + struct page *page[MAX_SKB_FRAGS]; + int num_pages; + unsigned long base; + unsigned long truesize; + + len = from->iov_len - offset; + if (!len) { + offset = 0; + ++from; + continue; + } + base = (unsigned long)from->iov_base + offset; + size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; + if (i + size > MAX_SKB_FRAGS) + return -EMSGSIZE; + num_pages = get_user_pages_fast(base, size, 0, &page[i]); + if (num_pages != size) { + int j; + + for (j = 0; j < num_pages; j++) + put_page(page[i + j]); + return -EFAULT; + } + truesize = size * PAGE_SIZE; + skb->data_len += len; + skb->len += len; + skb->truesize += truesize; + atomic_add(truesize, &skb->sk->sk_wmem_alloc); + while (len) { + int off = base & ~PAGE_MASK; + int size = min_t(int, len, PAGE_SIZE - off); + __skb_fill_page_desc(skb, i, page[i], off, size); + skb_shinfo(skb)->nr_frags++; + /* increase sk_wmem_alloc */ + base += size; + len -= size; + i++; + } + offset = 0; + ++from; + } + return 0; +} +EXPORT_SYMBOL(zerocopy_sg_from_iovec); + static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 __user *to, int len, __wsum *csump) -- cgit v1.2.3 From 5c6fe01c1fe3140a8fb088d2a9c32548b731c35e Mon Sep 17 00:00:00 2001 From: William Manley Date: Tue, 6 Aug 2013 19:03:14 +0100 Subject: net: igmp: Don't flush routing cache when force_igmp_version is modified The procfs knob /proc/sys/net/ipv4/conf/*/force_igmp_version allows the IGMP protocol version to use to be explicitly set. As a side effect this caused the routing cache to be flushed as it was declared as a DEVINET_SYSCTL_FLUSHING_ENTRY. Flushing is unnecessary and this patch makes it so flushing does not occur. Requested by Hannes Frederic Sowa as he was reviewing other patches adding procfs entries. Suggested-by: Hannes Frederic Sowa Signed-off-by: William Manley Acked-by: Hannes Frederic Sowa Acked-by: Benjamin LaHaise Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 2 +- net/ipv4/devinet.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index b99cd23f3474..0a4a6cb35338 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -27,9 +27,9 @@ enum IPV4_DEVCONF_TAG, IPV4_DEVCONF_ARPFILTER, IPV4_DEVCONF_MEDIUM_ID, + IPV4_DEVCONF_FORCE_IGMP_VERSION, IPV4_DEVCONF_NOXFRM, IPV4_DEVCONF_NOPOLICY, - IPV4_DEVCONF_FORCE_IGMP_VERSION, IPV4_DEVCONF_ARP_ANNOUNCE, IPV4_DEVCONF_ARP_IGNORE, IPV4_DEVCONF_PROMOTE_SECONDARIES, diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 43923dc77440..87d47ce9fa33 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2094,11 +2094,11 @@ static struct devinet_sysctl_table { DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), + DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, + "force_igmp_version"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), - DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, - "force_igmp_version"), DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, "promote_secondaries"), DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, -- cgit v1.2.3 From 2690048c01f32bf45d1c1e1ab3079bc10ad2aea7 Mon Sep 17 00:00:00 2001 From: William Manley Date: Tue, 6 Aug 2013 19:03:15 +0100 Subject: net: igmp: Allow user-space configuration of igmp unsolicited report interval Adds the new procfs knobs: /proc/sys/net/ipv4/conf/*/igmpv2_unsolicited_report_interval /proc/sys/net/ipv4/conf/*/igmpv3_unsolicited_report_interval Which will allow userspace configuration of the IGMP unsolicited report interval (see below) in milliseconds. The defaults are 10000ms for IGMPv2 and 1000ms for IGMPv3 in accordance with RFC2236 and RFC3376. Background: If an IGMP join packet is lost you will not receive data sent to the multicast group so if no data arrives from that multicast group in a period of time after the IGMP join a second IGMP join will be sent. The delay between joins is the "IGMP Unsolicited Report Interval". Prior to this patch this value was hard coded in the kernel to 10s for IGMPv2 and 1s for IGMPv3. 10s is unsuitable for some use-cases, such as IPTV as it can cause channel change to be slow in the presence of packet loss. This patch allows the value to be overridden from userspace for both IGMPv2 and IGMPv3 such that it can be tuned accoding to the network. Tested with Wireshark and a simple program to join a (non-existent) multicast group. The distribution of timings for the second join differ based upon setting the procfs knobs. igmpvX_unsolicited_report_interval is intended to follow the pattern established by force_igmp_version, and while a procfs entry has been added a corresponding sysctl knob has not as it is my understanding that sysctl is deprecated[1]. [1]: http://lwn.net/Articles/247243/ Signed-off-by: William Manley Acked-by: Hannes Frederic Sowa Acked-by: Benjamin LaHaise Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 2 ++ net/ipv4/devinet.c | 8 ++++++++ net/ipv4/igmp.c | 19 +++++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 0a4a6cb35338..c796ce26c7c0 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -28,6 +28,8 @@ enum IPV4_DEVCONF_ARPFILTER, IPV4_DEVCONF_MEDIUM_ID, IPV4_DEVCONF_FORCE_IGMP_VERSION, + IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, + IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, IPV4_DEVCONF_NOXFRM, IPV4_DEVCONF_NOPOLICY, IPV4_DEVCONF_ARP_ANNOUNCE, diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 87d47ce9fa33..a1b5bcbd04ae 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -73,6 +73,8 @@ static struct ipv4_devconf ipv4_devconf = { [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, + [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, + [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, }, }; @@ -83,6 +85,8 @@ static struct ipv4_devconf ipv4_devconf_dflt = { [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, + [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, + [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, }, }; @@ -2096,6 +2100,10 @@ static struct devinet_sysctl_table { DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, "force_igmp_version"), + DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, + "igmpv2_unsolicited_report_interval"), + DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, + "igmpv3_unsolicited_report_interval"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index ceb19ab9cf9d..d6c0e64ec97f 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -142,10 +142,25 @@ static int unsolicited_report_interval(struct in_device *in_dev) { + int interval_ms, interval_jiffies; + if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) - return IGMP_V2_Unsolicited_Report_Interval; + interval_ms = IN_DEV_CONF_GET( + in_dev, + IGMPV2_UNSOLICITED_REPORT_INTERVAL); else /* v3 */ - return IGMP_V3_Unsolicited_Report_Interval; + interval_ms = IN_DEV_CONF_GET( + in_dev, + IGMPV3_UNSOLICITED_REPORT_INTERVAL); + + interval_jiffies = msecs_to_jiffies(interval_ms); + + /* _timer functions can't handle a delay of 0 jiffies so ensure + * we always return a positive value. + */ + if (interval_jiffies <= 0) + interval_jiffies = 1; + return interval_jiffies; } static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); -- cgit v1.2.3 From bd0779370588386e4a67ba5d0b176cfded8e6a53 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 7 Aug 2013 18:13:20 +0200 Subject: netfilter: nfnetlink_queue: allow to attach expectations to conntracks This patch adds the capability to attach expectations via nfnetlink_queue. This is required by conntrack helpers that trigger expectations based on the first packet seen like the TFTP and the DHCPv6 user-space helpers. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 2 + include/net/netfilter/nfnetlink_queue.h | 8 +++ include/uapi/linux/netfilter/nfnetlink_queue.h | 1 + net/netfilter/nf_conntrack_netlink.c | 95 ++++++++++++++++++++++---- net/netfilter/nfnetlink_queue_core.c | 9 ++- net/netfilter/nfnetlink_queue_ct.c | 15 ++++ 6 files changed, 114 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 655d5d198d49..e2cf786be22f 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -325,6 +325,8 @@ struct nfq_ct_hook { size_t (*build_size)(const struct nf_conn *ct); int (*build)(struct sk_buff *skb, struct nf_conn *ct); int (*parse)(const struct nlattr *attr, struct nf_conn *ct); + int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, + u32 portid, u32 report); }; extern struct nfq_ct_hook __rcu *nfq_ct_hook; diff --git a/include/net/netfilter/nfnetlink_queue.h b/include/net/netfilter/nfnetlink_queue.h index 86267a529514..aff88ba91391 100644 --- a/include/net/netfilter/nfnetlink_queue.h +++ b/include/net/netfilter/nfnetlink_queue.h @@ -15,6 +15,8 @@ int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo); void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff); +int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, + u32 portid, u32 report); #else inline struct nf_conn * nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) @@ -39,5 +41,11 @@ inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff) { } + +inline int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, + u32 portid, u32 report) +{ + return 0; +} #endif /* NF_CONNTRACK */ #endif diff --git a/include/uapi/linux/netfilter/nfnetlink_queue.h b/include/uapi/linux/netfilter/nfnetlink_queue.h index 3a9b92147339..0132bad79de7 100644 --- a/include/uapi/linux/netfilter/nfnetlink_queue.h +++ b/include/uapi/linux/netfilter/nfnetlink_queue.h @@ -46,6 +46,7 @@ enum nfqnl_attr_type { NFQA_CT_INFO, /* enum ip_conntrack_info */ NFQA_CAP_LEN, /* __u32 length of captured packet */ NFQA_SKB_INFO, /* __u32 skb meta information */ + NFQA_EXP, /* nf_conntrack_netlink.h */ __NFQA_MAX }; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 9aaa68bbbcdb..fa61fea63234 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1987,6 +1987,27 @@ out: return err == -EAGAIN ? -ENOBUFS : err; } +static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { + [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, + [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, + [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, + [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, + [CTA_EXPECT_ID] = { .type = NLA_U32 }, + [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING, + .len = NF_CT_HELPER_NAME_LEN - 1 }, + [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, + [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, + [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, + [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, +}; + +static struct nf_conntrack_expect * +ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, + struct nf_conntrack_helper *helper, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask); + #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT static size_t ctnetlink_nfqueue_build_size(const struct nf_conn *ct) @@ -2127,10 +2148,69 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) return ret; } +static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda, + const struct nf_conn *ct, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask) +{ + int err; + + err = ctnetlink_parse_tuple(cda, tuple, CTA_EXPECT_TUPLE, + nf_ct_l3num(ct)); + if (err < 0) + return err; + + return ctnetlink_parse_tuple(cda, mask, CTA_EXPECT_MASK, + nf_ct_l3num(ct)); +} + +static int +ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, + u32 portid, u32 report) +{ + struct nlattr *cda[CTA_EXPECT_MAX+1]; + struct nf_conntrack_tuple tuple, mask; + struct nf_conntrack_helper *helper; + struct nf_conntrack_expect *exp; + int err; + + err = nla_parse_nested(cda, CTA_EXPECT_MAX, attr, exp_nla_policy); + if (err < 0) + return err; + + err = ctnetlink_nfqueue_exp_parse((const struct nlattr * const *)cda, + ct, &tuple, &mask); + if (err < 0) + return err; + + if (cda[CTA_EXPECT_HELP_NAME]) { + const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); + + helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), + nf_ct_protonum(ct)); + if (helper == NULL) + return -EOPNOTSUPP; + } + + exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, + helper, &tuple, &mask); + if (IS_ERR(exp)) + return PTR_ERR(exp); + + err = nf_ct_expect_related_report(exp, portid, report); + if (err < 0) { + nf_ct_expect_put(exp); + return err; + } + + return 0; +} + static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .build_size = ctnetlink_nfqueue_build_size, .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, + .attach_expect = ctnetlink_nfqueue_attach_expect, }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ @@ -2498,21 +2578,6 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb, return err; } -static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { - [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, - [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, - [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, - [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, - [CTA_EXPECT_ID] = { .type = NLA_U32 }, - [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING, - .len = NF_CT_HELPER_NAME_LEN - 1 }, - [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, - [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, - [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, - [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, - [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, -}; - static int ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index ec9de12aa488..e8c9f3bb779c 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -859,6 +859,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { [NFQA_MARK] = { .type = NLA_U32 }, [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, [NFQA_CT] = { .type = NLA_UNSPEC }, + [NFQA_EXP] = { .type = NLA_UNSPEC }, }; static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { @@ -987,8 +988,14 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, if (entry == NULL) return -ENOENT; - if (nfqa[NFQA_CT]) + if (nfqa[NFQA_CT]) { ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); + if (ct && nfqa[NFQA_EXP]) { + nfqnl_attach_expect(ct, nfqa[NFQA_EXP], + NETLINK_CB(skb).portid, + nlmsg_report(nlh)); + } + } if (nfqa[NFQA_PAYLOAD]) { u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c index ab61d66bc0b9..be893039966d 100644 --- a/net/netfilter/nfnetlink_queue_ct.c +++ b/net/netfilter/nfnetlink_queue_ct.c @@ -96,3 +96,18 @@ void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, if ((ct->status & IPS_NAT_MASK) && diff) nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); } + +int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, + u32 portid, u32 report) +{ + struct nfq_ct_hook *nfq_ct; + + if (nf_ct_is_untracked(ct)) + return 0; + + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) + return -EOPNOTSUPP; + + return nfq_ct->attach_expect(attr, ct, portid, report); +} -- cgit v1.2.3 From fc4eba58b4c1462ff3d6247b66fb47d6928db6d2 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 14 Aug 2013 01:03:46 +0200 Subject: ipv6: make unsolicited report intervals configurable for mld Commit cab70040dfd95ee32144f02fade64f0cb94f31a0 ("net: igmp: Reduce Unsolicited report interval to 1s when using IGMPv3") and 2690048c01f32bf45d1c1e1ab3079bc10ad2aea7 ("net: igmp: Allow user-space configuration of igmp unsolicited report interval") by William Manley made igmp unsolicited report intervals configurable per interface and corrected the interval of unsolicited igmpv3 report messages resendings to 1s. Same needs to be done for IPv6: MLDv1 (RFC2710 7.10.): 10 seconds MLDv2 (RFC3810 9.11.): 1 second Both intervals are configurable via new procfs knobs mldv1_unsolicited_report_interval and mldv2_unsolicited_report_interval. (also added .force_mld_version to ipv6_devconf_dflt to bring structs in line without semantic changes) v2: a) Joined documentation update for IPv4 and IPv6 MLD/IGMP unsolicited_report_interval procfs knobs. b) incorporate stylistic feedback from William Manley v3: a) add new DEVCONF_* values to the end of the enum (thanks to David Miller) Cc: Cong Wang Cc: William Manley Cc: Benjamin LaHaise Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 18 ++++++++++++++++++ include/linux/ipv6.h | 2 ++ include/uapi/linux/ipv6.h | 2 ++ net/ipv6/addrconf.c | 25 +++++++++++++++++++++++++ net/ipv6/mcast.c | 17 ++++++++++++++--- 5 files changed, 61 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 36be26b2ef7a..debfe857d8f9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1039,7 +1039,15 @@ disable_policy - BOOLEAN disable_xfrm - BOOLEAN Disable IPSEC encryption on this interface, whatever the policy +igmpv2_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + IGMPv1 or IGMPv2 report retransmit will take place. + Default: 10000 (10 seconds) +igmpv3_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + IGMPv3 report retransmit will take place. + Default: 1000 (1 seconds) tag - INTEGER Allows you to write a number, which can be used as required. @@ -1331,6 +1339,16 @@ ndisc_notify - BOOLEAN 1 - Generate unsolicited neighbour advertisements when device is brought up or hardware address changes. +mldv1_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + MLDv1 report retransmit will take place. + Default: 10000 (10 seconds) + +mldv2_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + MLDv2 report retransmit will take place. + Default: 1000 (1 second) + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 850e95bc766c..77a478462d8e 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -19,6 +19,8 @@ struct ipv6_devconf { __s32 rtr_solicit_interval; __s32 rtr_solicit_delay; __s32 force_mld_version; + __s32 mldv1_unsolicited_report_interval; + __s32 mldv2_unsolicited_report_interval; #ifdef CONFIG_IPV6_PRIVACY __s32 use_tempaddr; __s32 temp_valid_lft; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 4bda4cf5b0f5..d07ac6903e59 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -160,6 +160,8 @@ enum { DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, DEVCONF_NDISC_NOTIFY, + DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 7fd8572bac80..ad12f7c43f25 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -177,6 +177,8 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_redirects = 1, .autoconf = 1, .force_mld_version = 0, + .mldv1_unsolicited_report_interval = 10 * HZ, + .mldv2_unsolicited_report_interval = HZ, .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, @@ -211,6 +213,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra = 1, .accept_redirects = 1, .autoconf = 1, + .force_mld_version = 0, + .mldv1_unsolicited_report_interval = 10 * HZ, + .mldv2_unsolicited_report_interval = HZ, .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, @@ -4179,6 +4184,10 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_SOLICIT_DELAY] = jiffies_to_msecs(cnf->rtr_solicit_delay); array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; + array[DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL] = + jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval); + array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] = + jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval); #ifdef CONFIG_IPV6_PRIVACY array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; @@ -4862,6 +4871,22 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "mldv1_unsolicited_report_interval", + .data = + &ipv6_devconf.mldv1_unsolicited_report_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_ms_jiffies, + }, + { + .procname = "mldv2_unsolicited_report_interval", + .data = + &ipv6_devconf.mldv2_unsolicited_report_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_ms_jiffies, + }, #ifdef CONFIG_IPV6_PRIVACY { .procname = "use_tempaddr", diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index db25b8eb62bd..6c76df9909bf 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -108,7 +108,6 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, struct inet6_dev *idev); -#define IGMP6_UNSOLICITED_IVAL (10*HZ) #define MLD_QRV_DEFAULT 2 #define MLD_V1_SEEN(idev) (dev_net((idev)->dev)->ipv6.devconf_all->force_mld_version == 1 || \ @@ -129,6 +128,18 @@ int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF; pmc != NULL; \ pmc = rcu_dereference(pmc->next)) +static int unsolicited_report_interval(struct inet6_dev *idev) +{ + int iv; + + if (MLD_V1_SEEN(idev)) + iv = idev->cnf.mldv1_unsolicited_report_interval; + else + iv = idev->cnf.mldv2_unsolicited_report_interval; + + return iv > 0 ? iv : 1; +} + int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct net_device *dev = NULL; @@ -2158,7 +2169,7 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); - delay = net_random() % IGMP6_UNSOLICITED_IVAL; + delay = net_random() % unsolicited_report_interval(ma->idev); spin_lock_bh(&ma->mca_lock); if (del_timer(&ma->mca_timer)) { @@ -2325,7 +2336,7 @@ void ipv6_mc_init_dev(struct inet6_dev *idev) setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire, (unsigned long)idev); idev->mc_qrv = MLD_QRV_DEFAULT; - idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL; + idev->mc_maxdelay = unsolicited_report_interval(idev); idev->mc_v1_seen = 0; write_unlock_bh(&idev->lock); } -- cgit v1.2.3 From f0c03956ac40fdc4fbce7bed262ae74ac372e765 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 13 Aug 2013 15:05:38 +0200 Subject: netfilter: export xt_rpfilter.h to userland This file contains the API for the match "rpfilter", hence it should be exported to userland. Signed-off-by: Nicolas Dichtel Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_rpfilter.h | 23 ----------------------- include/uapi/linux/netfilter/Kbuild | 1 + include/uapi/linux/netfilter/xt_rpfilter.h | 23 +++++++++++++++++++++++ 3 files changed, 24 insertions(+), 23 deletions(-) delete mode 100644 include/linux/netfilter/xt_rpfilter.h create mode 100644 include/uapi/linux/netfilter/xt_rpfilter.h (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_rpfilter.h b/include/linux/netfilter/xt_rpfilter.h deleted file mode 100644 index 8358d4f71952..000000000000 --- a/include/linux/netfilter/xt_rpfilter.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _XT_RPATH_H -#define _XT_RPATH_H - -#include - -enum { - XT_RPFILTER_LOOSE = 1 << 0, - XT_RPFILTER_VALID_MARK = 1 << 1, - XT_RPFILTER_ACCEPT_LOCAL = 1 << 2, - XT_RPFILTER_INVERT = 1 << 3, -#ifdef __KERNEL__ - XT_RPFILTER_OPTION_MASK = XT_RPFILTER_LOOSE | - XT_RPFILTER_VALID_MARK | - XT_RPFILTER_ACCEPT_LOCAL | - XT_RPFILTER_INVERT, -#endif -}; - -struct xt_rpfilter_info { - __u8 flags; -}; - -#endif diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild index 41115776d76f..dc00927ffd62 100644 --- a/include/uapi/linux/netfilter/Kbuild +++ b/include/uapi/linux/netfilter/Kbuild @@ -68,6 +68,7 @@ header-y += xt_quota.h header-y += xt_rateest.h header-y += xt_realm.h header-y += xt_recent.h +header-y += xt_rpfilter.h header-y += xt_sctp.h header-y += xt_set.h header-y += xt_socket.h diff --git a/include/uapi/linux/netfilter/xt_rpfilter.h b/include/uapi/linux/netfilter/xt_rpfilter.h new file mode 100644 index 000000000000..8358d4f71952 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_rpfilter.h @@ -0,0 +1,23 @@ +#ifndef _XT_RPATH_H +#define _XT_RPATH_H + +#include + +enum { + XT_RPFILTER_LOOSE = 1 << 0, + XT_RPFILTER_VALID_MARK = 1 << 1, + XT_RPFILTER_ACCEPT_LOCAL = 1 << 2, + XT_RPFILTER_INVERT = 1 << 3, +#ifdef __KERNEL__ + XT_RPFILTER_OPTION_MASK = XT_RPFILTER_LOOSE | + XT_RPFILTER_VALID_MARK | + XT_RPFILTER_ACCEPT_LOCAL | + XT_RPFILTER_INVERT, +#endif +}; + +struct xt_rpfilter_info { + __u8 flags; +}; + +#endif -- cgit v1.2.3 From 38c67328ac79cb9eaf61b5d4750fe3b9cff0dd15 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 13 Aug 2013 15:05:39 +0200 Subject: netfilter: export xt_HMARK.h to userland This file contains the API for the target "HMARK", hence it should be exported to userland. Signed-off-by: Nicolas Dichtel Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_HMARK.h | 50 --------------------------------- include/uapi/linux/netfilter/Kbuild | 1 + include/uapi/linux/netfilter/xt_HMARK.h | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 50 deletions(-) delete mode 100644 include/linux/netfilter/xt_HMARK.h create mode 100644 include/uapi/linux/netfilter/xt_HMARK.h (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_HMARK.h b/include/linux/netfilter/xt_HMARK.h deleted file mode 100644 index 826fc5807577..000000000000 --- a/include/linux/netfilter/xt_HMARK.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef XT_HMARK_H_ -#define XT_HMARK_H_ - -#include - -enum { - XT_HMARK_SADDR_MASK, - XT_HMARK_DADDR_MASK, - XT_HMARK_SPI, - XT_HMARK_SPI_MASK, - XT_HMARK_SPORT, - XT_HMARK_DPORT, - XT_HMARK_SPORT_MASK, - XT_HMARK_DPORT_MASK, - XT_HMARK_PROTO_MASK, - XT_HMARK_RND, - XT_HMARK_MODULUS, - XT_HMARK_OFFSET, - XT_HMARK_CT, - XT_HMARK_METHOD_L3, - XT_HMARK_METHOD_L3_4, -}; -#define XT_HMARK_FLAG(flag) (1 << flag) - -union hmark_ports { - struct { - __u16 src; - __u16 dst; - } p16; - struct { - __be16 src; - __be16 dst; - } b16; - __u32 v32; - __be32 b32; -}; - -struct xt_hmark_info { - union nf_inet_addr src_mask; - union nf_inet_addr dst_mask; - union hmark_ports port_mask; - union hmark_ports port_set; - __u32 flags; - __u16 proto_mask; - __u32 hashrnd; - __u32 hmodulus; - __u32 hoffset; /* Mark offset to start from */ -}; - -#endif /* XT_HMARK_H_ */ diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild index dc00927ffd62..174915420d3f 100644 --- a/include/uapi/linux/netfilter/Kbuild +++ b/include/uapi/linux/netfilter/Kbuild @@ -22,6 +22,7 @@ header-y += xt_CONNMARK.h header-y += xt_CONNSECMARK.h header-y += xt_CT.h header-y += xt_DSCP.h +header-y += xt_HMARK.h header-y += xt_IDLETIMER.h header-y += xt_LED.h header-y += xt_LOG.h diff --git a/include/uapi/linux/netfilter/xt_HMARK.h b/include/uapi/linux/netfilter/xt_HMARK.h new file mode 100644 index 000000000000..826fc5807577 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_HMARK.h @@ -0,0 +1,50 @@ +#ifndef XT_HMARK_H_ +#define XT_HMARK_H_ + +#include + +enum { + XT_HMARK_SADDR_MASK, + XT_HMARK_DADDR_MASK, + XT_HMARK_SPI, + XT_HMARK_SPI_MASK, + XT_HMARK_SPORT, + XT_HMARK_DPORT, + XT_HMARK_SPORT_MASK, + XT_HMARK_DPORT_MASK, + XT_HMARK_PROTO_MASK, + XT_HMARK_RND, + XT_HMARK_MODULUS, + XT_HMARK_OFFSET, + XT_HMARK_CT, + XT_HMARK_METHOD_L3, + XT_HMARK_METHOD_L3_4, +}; +#define XT_HMARK_FLAG(flag) (1 << flag) + +union hmark_ports { + struct { + __u16 src; + __u16 dst; + } p16; + struct { + __be16 src; + __be16 dst; + } b16; + __u32 v32; + __be32 b32; +}; + +struct xt_hmark_info { + union nf_inet_addr src_mask; + union nf_inet_addr dst_mask; + union hmark_ports port_mask; + union hmark_ports port_set; + __u32 flags; + __u16 proto_mask; + __u32 hashrnd; + __u32 hmodulus; + __u32 hoffset; /* Mark offset to start from */ +}; + +#endif /* XT_HMARK_H_ */ -- cgit v1.2.3 From b05e92545d9582be15699e4a33d0f93ac00b37dd Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 10 Aug 2013 12:27:26 +0200 Subject: brcmfmac: abstract tx packet processing functions Abstract brcmf_sdio_txpkt_prep and brcmf_sdio_txpkt_postp as a preparation of chained tx packets for host side tx glomming. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 16 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 237 +++++++++++++++------ .../net/wireless/brcm80211/brcmfmac/sdio_host.h | 2 +- include/linux/platform_data/brcmfmac-sdio.h | 6 + 4 files changed, 183 insertions(+), 78 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e3f3c48f86d4..e13b1a65c65f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -592,6 +592,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; + struct sk_buff_head pktq; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -602,7 +603,10 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, } memcpy(mypkt->data, buf, nbytes); - err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt); + __skb_queue_head_init(&pktq); + __skb_queue_tail(&pktq, mypkt); + err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq); + __skb_dequeue_tail(&pktq); brcmu_pkt_buf_free_skb(mypkt); return err; @@ -611,22 +615,18 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt) + uint flags, struct sk_buff_head *pktq) { uint width; int err = 0; - struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pkt->len); + fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; brcmf_sdio_addrprep(sdiodev, width, &addr); - skb_queue_head_init(&pkt_list); - skb_queue_tail(&pkt_list, pkt); - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list); - skb_dequeue_tail(&pkt_list); + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq); return err; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index db31312eba6a..aa4cacaf8b03 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -510,7 +510,6 @@ struct brcmf_sdio { #ifdef DEBUG static int qcount[NUMPRIO]; -static int tx_packets[NUMPRIO]; #endif /* DEBUG */ #define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ @@ -1759,85 +1758,185 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) return; } +/* flag marking a dummy skb added for DMA alignment requirement */ +#define DUMMY_SKB_FLAG 0x10000 +/* bit mask of data length chopped from the previous packet */ +#define DUMMY_SKB_CHOP_LEN_MASK 0xffff +/** + * brcmf_sdio_txpkt_prep - packet preparation for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * @chan: virtual channel to transmit the packet + * + * Processes to be applied to the packet + * - Align data buffer pointer + * - Align data buffer length + * - Prepare header + * Return: negative value if there is error + */ +static int +brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + u16 head_pad, tail_pad, tail_chop, pkt_len; + u16 head_align, sg_align; + u32 sw_header; + int ntail; + struct sk_buff *pkt_next, *pkt_new; + u8 *dat_buf; + unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize; + + /* SDIO ADMA requires at least 32 bit alignment */ + head_align = 4; + sg_align = 4; + if (bus->sdiodev->pdata) { + head_align = bus->sdiodev->pdata->sd_head_align > 4 ? + bus->sdiodev->pdata->sd_head_align : 4; + sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ? + bus->sdiodev->pdata->sd_sgentry_align : 4; + } + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % sg_align); + + pkt_next = pktq->next; + dat_buf = (u8 *)(pkt_next->data); + + /* Check head padding */ + head_pad = ((unsigned long)dat_buf % head_align); + if (head_pad) { + if (skb_headroom(pkt_next) < head_pad) { + bus->sdiodev->bus_if->tx_realloc++; + head_pad = 0; + if (skb_cow(pkt_next, head_pad)) + return -ENOMEM; + } + skb_push(pkt_next, head_pad); + dat_buf = (u8 *)(pkt_next->data); + memset(dat_buf, 0, head_pad + SDPCM_HDRLEN); + } + + /* Check tail padding */ + pkt_new = NULL; + tail_chop = pkt_next->len % sg_align; + tail_pad = sg_align - tail_chop; + tail_pad += blksize - (pkt_next->len + tail_pad) % blksize; + if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) { + pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); + if (pkt_new == NULL) + return -ENOMEM; + memcpy(pkt_new->data, + pkt_next->data + pkt_next->len - tail_chop, + tail_chop); + *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop; + skb_trim(pkt_next, pkt_next->len - tail_chop); + __skb_queue_after(pktq, pkt_next, pkt_new); + } else { + ntail = pkt_next->data_len + tail_pad - + (pkt_next->end - pkt_next->tail); + if (skb_cloned(pkt_next) || ntail > 0) + if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt_next)) + return -ENOMEM; + dat_buf = (u8 *)(pkt_next->data); + __skb_put(pkt_next, tail_pad); + } + + /* Now prep the header */ + /* 4 bytes hardware header (frame tag) + * Byte 0~1: Frame length + * Byte 2~3: Checksum, bit-wise inverse of frame length + */ + if (pkt_new) + pkt_len = pkt_next->len + tail_chop; + else + pkt_len = pkt_next->len - tail_pad; + *(__le16 *)dat_buf = cpu_to_le16(pkt_len); + *(((__le16 *)dat_buf) + 1) = cpu_to_le16(~pkt_len); + /* 8 bytes software header + * Byte 0: Tx sequence number + * Byte 1: 4 MSB Channel number + * Byte 2: Reserved + * Byte 3: Data offset + * Byte 4~7: Reserved + */ + sw_header = bus->tx_seq; + sw_header |= ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK); + sw_header |= ((head_pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & + SDPCM_DOFFSET_MASK; + *(((__le32 *)dat_buf) + 1) = cpu_to_le32(sw_header); + *(((__le32 *)dat_buf) + 2) = 0; + + if (BRCMF_BYTES_ON() && + ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || + (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) + brcmf_dbg_hex_dump(true, pkt_next, pkt_len, "Tx Frame:\n"); + else if (BRCMF_HDRS_ON()) + brcmf_dbg_hex_dump(true, pkt_next, head_pad + SDPCM_HDRLEN, + "Tx Header:\n"); + + return 0; +} + +/** + * brcmf_sdio_txpkt_postp - packet post processing for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * + * Processes to be applied to the packet + * - Remove head padding + * - Remove tail padding + */ +static void +brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) +{ + u8 *hdr; + u32 dat_offset; + u32 dummy_flags, chop_len; + struct sk_buff *pkt_next, *tmp, *pkt_prev; + + skb_queue_walk_safe(pktq, pkt_next, tmp) { + dummy_flags = *(u32 *)(pkt_next->cb); + if (dummy_flags & DUMMY_SKB_FLAG) { + chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK; + if (chop_len) { + pkt_prev = pkt_next->prev; + memcpy(pkt_prev->data + pkt_prev->len, + pkt_next->data, chop_len); + skb_put(pkt_prev, chop_len); + } + __skb_unlink(pkt_next, pktq); + brcmu_pkt_buf_free_skb(pkt_next); + } else { + hdr = pkt_next->data + SDPCM_FRAMETAG_LEN; + dat_offset = le32_to_cpu(*(__le32 *)hdr); + dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> + SDPCM_DOFFSET_SHIFT; + skb_pull(pkt_next, dat_offset); + } + } +} + /* Writes a HW/SW header into the packet and sends it. */ /* Assumes: (a) header space already there, (b) caller holds lock */ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, uint chan) { int ret; - u8 *frame; - u16 len, pad = 0; - u32 swheader; int i; + struct sk_buff_head localq; brcmf_dbg(TRACE, "Enter\n"); - frame = (u8 *) (pkt->data); - - /* Add alignment padding, allocate new packet if needed */ - pad = ((unsigned long)frame % BRCMF_SDALIGN); - if (pad) { - if (skb_headroom(pkt) < pad) { - brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n", - skb_headroom(pkt), pad); - bus->sdiodev->bus_if->tx_realloc++; - ret = skb_cow(pkt, BRCMF_SDALIGN); - if (ret) - goto done; - pad = ((unsigned long)frame % BRCMF_SDALIGN); - } - skb_push(pkt, pad); - frame = (u8 *) (pkt->data); - memset(frame, 0, pad + SDPCM_HDRLEN); - } - /* precondition: pad < BRCMF_SDALIGN */ - - /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ - len = (u16) (pkt->len); - *(__le16 *) frame = cpu_to_le16(len); - *(((__le16 *) frame) + 1) = cpu_to_le16(~len); - - /* Software tag: channel, sequence number, data offset */ - swheader = - ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | - (((pad + - SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); - - *(((__le32 *) frame) + 1) = cpu_to_le32(swheader); - *(((__le32 *) frame) + 2) = 0; - -#ifdef DEBUG - tx_packets[pkt->priority]++; -#endif - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && - chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && - chan != SDPCM_CONTROL_CHANNEL))) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - /* Raise len to next SDIO block to eliminate tail command */ - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - u16 pad = bus->blocksize - (len % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize)) - len += pad; - } else if (len % BRCMF_SDALIGN) { - len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); - } - - /* Some controllers have trouble with odd bytes -- round to even */ - if (len & (ALIGNMENT - 1)) - len = roundup(len, ALIGNMENT); + __skb_queue_head_init(&localq); + __skb_queue_tail(&localq, pkt); + ret = brcmf_sdio_txpkt_prep(bus, &localq, chan); + if (ret) + goto done; sdio_claim_host(bus->sdiodev->func[1]); ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, pkt); + SDIO_FUNC_2, F2SYNC, &localq); bus->sdcnt.f2txdata++; if (ret < 0) { @@ -1868,8 +1967,8 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; done: - /* restore pkt buffer pointer before calling tx complete routine */ - skb_pull(pkt, SDPCM_HDRLEN + pad); + brcmf_sdio_txpkt_postp(bus, &localq); + __skb_dequeue_tail(&localq); brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0); return ret; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 09786a539950..2b5407f002e5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -208,7 +208,7 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, */ extern int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt); + uint flags, struct sk_buff_head *pktq); extern int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes); diff --git a/include/linux/platform_data/brcmfmac-sdio.h b/include/linux/platform_data/brcmfmac-sdio.h index b7174998c24a..e75dcbf2b230 100644 --- a/include/linux/platform_data/brcmfmac-sdio.h +++ b/include/linux/platform_data/brcmfmac-sdio.h @@ -94,6 +94,10 @@ void __init brcmfmac_init_pdata(void) * Set this to true if the SDIO host controller has higher align requirement * than 32 bytes for each scatterlist item. * + * sd_head_align: alignment requirement for start of data buffer + * + * sd_sgentry_align: length alignment requirement for each sg entry + * * power_on: This function is called by the brcmfmac when the module gets * loaded. This can be particularly useful for low power devices. The platform * spcific routine may for example decide to power up the complete device. @@ -121,6 +125,8 @@ struct brcmfmac_sdio_platform_data { unsigned int oob_irq_nr; unsigned long oob_irq_flags; bool broken_sg_support; + unsigned short sd_head_align; + unsigned short sd_sgentry_align; void (*power_on)(void); void (*power_off)(void); void (*reset)(void); -- cgit v1.2.3 From 8d3214c4e8c8be6efd8ec7a172239ebbd4deb04b Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 18 Aug 2013 03:13:26 +0400 Subject: sh_eth: remove 'register_type' field from 'struct sh_eth_plat_data' Now that the 'register_type' field of the 'sh_eth' driver's platform data is not used by the driver anymore, it's time to remove it and its initializers from the SH platform code. Also move *enum* declaring values for this field from to the local driver's header file as they're only needed by the driver itself now... Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/arm/mach-shmobile/board-armadillo800eva.c | 1 - arch/arm/mach-shmobile/board-bockw.c | 1 - arch/sh/boards/board-espt.c | 1 - arch/sh/boards/board-sh7757lcr.c | 4 ---- arch/sh/boards/mach-ecovec24/setup.c | 1 - arch/sh/boards/mach-se/7724/setup.c | 1 - arch/sh/boards/mach-sh7763rdp/setup.c | 1 - arch/sh/kernel/cpu/sh2/setup-sh7619.c | 1 - drivers/net/ethernet/renesas/sh_eth.h | 7 +++++++ include/linux/sh_eth.h | 7 ------- 10 files changed, 7 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index c5be60d85e4b..3a6ffa250fb1 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -358,7 +358,6 @@ static struct platform_device usbhsf_device = { static struct sh_eth_plat_data sh_eth_platdata = { .phy = 0x00, /* LAN8710A */ .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .phy_interface = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c index 3354a85c90f7..fa8885b2d5a5 100644 --- a/arch/arm/mach-shmobile/board-bockw.c +++ b/arch/arm/mach-shmobile/board-bockw.c @@ -89,7 +89,6 @@ static struct sh_mobile_sdhi_info sdhi0_info = { static struct sh_eth_plat_data ether_platform_data __initdata = { .phy = 0x01, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_RCAR, .phy_interface = PHY_INTERFACE_MODE_RMII, /* * Although the LINK signal is available on the board, it's connected to diff --git a/arch/sh/boards/board-espt.c b/arch/sh/boards/board-espt.c index 4d94dff9015c..7291e2f11a47 100644 --- a/arch/sh/boards/board-espt.c +++ b/arch/sh/boards/board-espt.c @@ -80,7 +80,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh7763_eth_pdata = { .phy = 0, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .phy_interface = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index 4f114d1cd019..25c5a932f9fe 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -77,7 +77,6 @@ static struct resource sh_eth0_resources[] = { static struct sh_eth_plat_data sh7757_eth0_pdata = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .set_mdio_gate = sh7757_eth_set_mdio_gate, }; @@ -106,7 +105,6 @@ static struct resource sh_eth1_resources[] = { static struct sh_eth_plat_data sh7757_eth1_pdata = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .set_mdio_gate = sh7757_eth_set_mdio_gate, }; @@ -151,7 +149,6 @@ static struct resource sh_eth_giga0_resources[] = { static struct sh_eth_plat_data sh7757_eth_giga0_pdata = { .phy = 18, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .set_mdio_gate = sh7757_eth_giga_set_mdio_gate, .phy_interface = PHY_INTERFACE_MODE_RGMII_ID, }; @@ -186,7 +183,6 @@ static struct resource sh_eth_giga1_resources[] = { static struct sh_eth_plat_data sh7757_eth_giga1_pdata = { .phy = 19, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .set_mdio_gate = sh7757_eth_giga_set_mdio_gate, .phy_interface = PHY_INTERFACE_MODE_RGMII_ID, }; diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 61fade0ffa96..a4f630f04ea3 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -159,7 +159,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh_eth_plat = { .phy = 0x1f, /* SMSC LAN8700 */ .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .phy_interface = PHY_INTERFACE_MODE_MII, .ether_link_active_low = 1 }; diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index aed4ca9ee1f5..e96e053f2609 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -377,7 +377,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh_eth_plat = { .phy = 0x1f, /* SMSC LAN8187 */ .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .phy_interace = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/sh/boards/mach-sh7763rdp/setup.c b/arch/sh/boards/mach-sh7763rdp/setup.c index 50ba481fa240..2c8fb04685d4 100644 --- a/arch/sh/boards/mach-sh7763rdp/setup.c +++ b/arch/sh/boards/mach-sh7763rdp/setup.c @@ -88,7 +88,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh7763_eth_pdata = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .phy_interface = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c index bbadd4820336..19472817e274 100644 --- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c +++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c @@ -114,7 +114,6 @@ static struct platform_device scif2_device = { static struct sh_eth_plat_data eth_platform_data = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH3_SH2, .phy_interace = PHY_INTERFACE_MODE_MII, }; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 1e7852810c8d..a0db02c63b11 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -157,6 +157,13 @@ enum { SH_ETH_MAX_REGISTER_OFFSET, }; +enum { + SH_ETH_REG_GIGABIT, + SH_ETH_REG_FAST_RCAR, + SH_ETH_REG_FAST_SH4, + SH_ETH_REG_FAST_SH3_SH2 +}; + /* Driver's parameters */ #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) #define SH4_SKB_RX_ALIGN 32 diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index 6205eeba392b..90b5e30c2f22 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -5,17 +5,10 @@ #include enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN}; -enum { - SH_ETH_REG_GIGABIT, - SH_ETH_REG_FAST_RCAR, - SH_ETH_REG_FAST_SH4, - SH_ETH_REG_FAST_SH3_SH2 -}; struct sh_eth_plat_data { int phy; int edmac_endian; - int register_type; phy_interface_t phy_interface; void (*set_mdio_gate)(void *addr); -- cgit v1.2.3 From dbe34724c08ea25d39d31735120077013fbbb6fb Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Mon, 19 Aug 2013 17:47:40 +0530 Subject: drivers: net: cpsw: remove platform data header file of cpsw CPSW driver no longer supports platform register as all the SoCs which has CPSW are supporting DT only booting, so moving cpsw.h header file from platform include to drivers/net/ethernet/ti Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 2 +- drivers/net/ethernet/ti/cpsw.h | 42 ++++++++++++++++++++++++++++++++++++ include/linux/platform_data/cpsw.h | 44 -------------------------------------- 3 files changed, 43 insertions(+), 45 deletions(-) create mode 100644 drivers/net/ethernet/ti/cpsw.h delete mode 100644 include/linux/platform_data/cpsw.h (limited to 'include/linux') diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 0fcf21254ad3..b4a85f5531ed 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -34,9 +34,9 @@ #include #include -#include #include +#include "cpsw.h" #include "cpsw_ale.h" #include "cpts.h" #include "davinci_cpdma.h" diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h new file mode 100644 index 000000000000..eb3e101ec048 --- /dev/null +++ b/drivers/net/ethernet/ti/cpsw.h @@ -0,0 +1,42 @@ +/* Texas Instruments Ethernet Switch Driver + * + * Copyright (C) 2013 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __CPSW_H__ +#define __CPSW_H__ + +#include + +struct cpsw_slave_data { + char phy_id[MII_BUS_ID_SIZE]; + int phy_if; + u8 mac_addr[ETH_ALEN]; + u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ +}; + +struct cpsw_platform_data { + struct cpsw_slave_data *slave_data; + u32 ss_reg_ofs; /* Subsystem control register offset */ + u32 channels; /* number of cpdma channels (symmetric) */ + u32 slaves; /* number of slave cpgmac ports */ + u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ + u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ + u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ + u32 ale_entries; /* ale table size */ + u32 bd_ram_size; /*buffer descriptor ram size */ + u32 rx_descs; /* Number of Rx Descriptios */ + u32 mac_control; /* Mac control register */ + u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ + bool dual_emac; /* Enable Dual EMAC mode */ +}; + +#endif /* __CPSW_H__ */ diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h deleted file mode 100644 index bb3cd58d71e3..000000000000 --- a/include/linux/platform_data/cpsw.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Texas Instruments Ethernet Switch Driver - * - * Copyright (C) 2012 Texas Instruments - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __CPSW_H__ -#define __CPSW_H__ - -#include - -struct cpsw_slave_data { - char phy_id[MII_BUS_ID_SIZE]; - int phy_if; - u8 mac_addr[ETH_ALEN]; - u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ - -}; - -struct cpsw_platform_data { - u32 ss_reg_ofs; /* Subsystem control register offset */ - u32 channels; /* number of cpdma channels (symmetric) */ - u32 slaves; /* number of slave cpgmac ports */ - struct cpsw_slave_data *slave_data; - u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ - u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ - u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ - u32 ale_entries; /* ale table size */ - u32 bd_ram_size; /*buffer descriptor ram size */ - u32 rx_descs; /* Number of Rx Descriptios */ - u32 mac_control; /* Mac control register */ - u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ - bool dual_emac; /* Enable Dual EMAC mode */ -}; - -#endif /* __CPSW_H__ */ -- cgit v1.2.3 From 1ddff7da0faecffdcdeab3d981fb8241453cea44 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 19 Aug 2013 15:39:19 +0400 Subject: can: mcp251x: Replace power callbacks with regulator API This patch replaces power callbacks to the regulator API. To improve the readability of the code, helper for the regulator enable/disable was added. Acked-by: Haojian Zhuang Signed-off-by: Alexander Shiyan Signed-off-by: Marc Kleine-Budde --- arch/arm/mach-pxa/icontrol.c | 3 -- arch/arm/mach-pxa/zeus.c | 46 +++++++++++--------- drivers/net/can/mcp251x.c | 83 +++++++++++++++++++----------------- include/linux/can/platform/mcp251x.h | 13 +----- 4 files changed, 69 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-pxa/icontrol.c b/arch/arm/mach-pxa/icontrol.c index fe31bfcbb8df..c98511c5abd1 100644 --- a/arch/arm/mach-pxa/icontrol.c +++ b/arch/arm/mach-pxa/icontrol.c @@ -73,9 +73,6 @@ static struct pxa2xx_spi_chip mcp251x_chip_info4 = { static struct mcp251x_platform_data mcp251x_info = { .oscillator_frequency = 16E6, - .board_specific_setup = NULL, - .power_enable = NULL, - .transceiver_enable = NULL }; static struct spi_board_info mcp251x_board_info[] = { diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c index f5d436434566..04a0aea23873 100644 --- a/arch/arm/mach-pxa/zeus.c +++ b/arch/arm/mach-pxa/zeus.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -391,33 +393,34 @@ static struct pxa2xx_spi_master pxa2xx_spi_ssp3_master_info = { }; /* CAN bus on SPI */ -static int zeus_mcp2515_setup(struct spi_device *sdev) -{ - int err; - - err = gpio_request(ZEUS_CAN_SHDN_GPIO, "CAN shutdown"); - if (err) - return err; +static struct regulator_consumer_supply can_regulator_consumer = + REGULATOR_SUPPLY("vdd", "spi3.0"); - err = gpio_direction_output(ZEUS_CAN_SHDN_GPIO, 1); - if (err) { - gpio_free(ZEUS_CAN_SHDN_GPIO); - return err; - } +static struct regulator_init_data can_regulator_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = &can_regulator_consumer, + .num_consumer_supplies = 1, +}; - return 0; -} +static struct fixed_voltage_config can_regulator_pdata = { + .supply_name = "CAN_SHDN", + .microvolts = 3300000, + .gpio = ZEUS_CAN_SHDN_GPIO, + .init_data = &can_regulator_init_data, +}; -static int zeus_mcp2515_transceiver_enable(int enable) -{ - gpio_set_value(ZEUS_CAN_SHDN_GPIO, !enable); - return 0; -} +static struct platform_device can_regulator_device = { + .name = "reg-fixed-volage", + .id = -1, + .dev = { + .platform_data = &can_regulator_pdata, + }, +}; static struct mcp251x_platform_data zeus_mcp2515_pdata = { .oscillator_frequency = 16*1000*1000, - .board_specific_setup = zeus_mcp2515_setup, - .power_enable = zeus_mcp2515_transceiver_enable, }; static struct spi_board_info zeus_spi_board_info[] = { @@ -516,6 +519,7 @@ static struct platform_device *zeus_devices[] __initdata = { &zeus_leds_device, &zeus_pcmcia_device, &zeus_max6369_device, + &can_regulator_device, }; /* AC'97 */ diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index 8cda23bf0614..d2c54888b211 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -37,9 +37,6 @@ * * static struct mcp251x_platform_data mcp251x_info = { * .oscillator_frequency = 8000000, - * .board_specific_setup = &mcp251x_setup, - * .power_enable = mcp251x_power_enable, - * .transceiver_enable = NULL, * }; * * static struct spi_board_info spi_board_info[] = { @@ -76,6 +73,7 @@ #include #include #include +#include /* SPI interface instruction set */ #define INSTRUCTION_WRITE 0x02 @@ -264,6 +262,8 @@ struct mcp251x_priv { #define AFTER_SUSPEND_POWER 4 #define AFTER_SUSPEND_RESTART 8 int restart_tx; + struct regulator *power; + struct regulator *transceiver; }; #define MCP251X_IS(_model) \ @@ -667,16 +667,25 @@ static int mcp251x_hw_probe(struct spi_device *spi) return (st1 == 0x80 && st2 == 0x07) ? 1 : 0; } +static int mcp251x_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + static void mcp251x_open_clean(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; free_irq(spi->irq, priv); mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); close_candev(net); } @@ -684,7 +693,6 @@ static int mcp251x_stop(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; close_candev(net); @@ -704,8 +712,7 @@ static int mcp251x_stop(struct net_device *net) mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); priv->can.state = CAN_STATE_STOPPED; @@ -939,8 +946,7 @@ static int mcp251x_open(struct net_device *net) } mutex_lock(&priv->mcp_lock); - if (pdata->transceiver_enable) - pdata->transceiver_enable(1); + mcp251x_power_enable(priv->transceiver, 1); priv->force_quit = 0; priv->tx_skb = NULL; @@ -956,8 +962,7 @@ static int mcp251x_open(struct net_device *net) flags, DEVICE_NAME, priv); if (ret) { dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); close_candev(net); goto open_unlock; } @@ -1026,6 +1031,19 @@ static int mcp251x_can_probe(struct spi_device *spi) CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; priv->model = spi_get_device_id(spi)->driver_data; priv->net = net; + + priv->power = devm_regulator_get(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto error_power; + } + + ret = mcp251x_power_enable(priv->power, 1); + if (ret) + goto error_power; + spi_set_drvdata(spi, priv); priv->spi = spi; @@ -1068,13 +1086,6 @@ static int mcp251x_can_probe(struct spi_device *spi) } } - if (pdata->power_enable) - pdata->power_enable(1); - - /* Call out to platform specific setup */ - if (pdata->board_specific_setup) - pdata->board_specific_setup(spi); - SET_NETDEV_DEV(net, &spi->dev); /* Configure the SPI bus */ @@ -1084,14 +1095,11 @@ static int mcp251x_can_probe(struct spi_device *spi) /* Here is OK to not lock the MCP, no one knows about it yet */ if (!mcp251x_hw_probe(spi)) { - dev_info(&spi->dev, "Probe failed\n"); + ret = -ENODEV; goto error_probe; } mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); - ret = register_candev(net); if (ret) goto error_probe; @@ -1109,13 +1117,13 @@ error_rx_buf: if (!mcp251x_enable_dma) kfree(priv->spi_tx_buf); error_tx_buf: - free_candev(net); if (mcp251x_enable_dma) dma_free_coherent(&spi->dev, PAGE_SIZE, priv->spi_tx_buf, priv->spi_tx_dma); + mcp251x_power_enable(priv->power, 0); +error_power: + free_candev(net); error_alloc: - if (pdata->power_enable) - pdata->power_enable(0); dev_err(&spi->dev, "probe failed\n"); error_out: return ret; @@ -1123,12 +1131,10 @@ error_out: static int mcp251x_can_remove(struct spi_device *spi) { - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = spi_get_drvdata(spi); struct net_device *net = priv->net; unregister_candev(net); - free_candev(net); if (mcp251x_enable_dma) { dma_free_coherent(&spi->dev, PAGE_SIZE, @@ -1138,8 +1144,9 @@ static int mcp251x_can_remove(struct spi_device *spi) kfree(priv->spi_rx_buf); } - if (pdata->power_enable) - pdata->power_enable(0); + mcp251x_power_enable(priv->power, 0); + + free_candev(net); return 0; } @@ -1149,7 +1156,6 @@ static int mcp251x_can_remove(struct spi_device *spi) static int mcp251x_can_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = spi_get_drvdata(spi); struct net_device *net = priv->net; @@ -1163,15 +1169,14 @@ static int mcp251x_can_suspend(struct device *dev) netif_device_detach(net); mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); priv->after_suspend = AFTER_SUSPEND_UP; } else { priv->after_suspend = AFTER_SUSPEND_DOWN; } - if (pdata->power_enable) { - pdata->power_enable(0); + if (!IS_ERR(priv->power)) { + regulator_disable(priv->power); priv->after_suspend |= AFTER_SUSPEND_POWER; } @@ -1181,16 +1186,14 @@ static int mcp251x_can_suspend(struct device *dev) static int mcp251x_can_resume(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = spi_get_drvdata(spi); if (priv->after_suspend & AFTER_SUSPEND_POWER) { - pdata->power_enable(1); + mcp251x_power_enable(priv->power, 1); queue_work(priv->wq, &priv->restart_work); } else { if (priv->after_suspend & AFTER_SUSPEND_UP) { - if (pdata->transceiver_enable) - pdata->transceiver_enable(1); + mcp251x_power_enable(priv->transceiver, 1); queue_work(priv->wq, &priv->restart_work); } else { priv->after_suspend = 0; diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index 089fe43211a4..8a2725676d8b 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -9,26 +9,15 @@ #include -/** +/* * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data * @oscillator_frequency: - oscillator frequency in Hz * @irq_flags: - IRQF configuration flags - * @board_specific_setup: - called before probing the chip (power,reset) - * @transceiver_enable: - called to power on/off the transceiver - * @power_enable: - called to power on/off the mcp *and* the - * transceiver - * - * Please note that you should define power_enable or transceiver_enable or - * none of them. Defining both of them is no use. - * */ struct mcp251x_platform_data { unsigned long oscillator_frequency; unsigned long irq_flags; - int (*board_specific_setup)(struct spi_device *spi); - int (*transceiver_enable)(int enable); - int (*power_enable) (int enable); }; #endif /* __CAN_PLATFORM_MCP251X_H__ */ -- cgit v1.2.3 From ae5d589e5f9f3217656ada632869968178886ac6 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 19 Aug 2013 15:39:20 +0400 Subject: can: mcp251x: Eliminate irq_flags from driver platform_data Flags is not used by boards, so remove this field from the driver platform_data. Signed-off-by: Alexander Shiyan Signed-off-by: Marc Kleine-Budde --- drivers/net/can/mcp251x.c | 9 +-------- include/linux/can/platform/mcp251x.h | 2 -- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index d2c54888b211..ec84ce163608 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -935,8 +935,7 @@ static int mcp251x_open(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; - unsigned long flags; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_FALLING; int ret; ret = open_candev(net); @@ -952,12 +951,6 @@ static int mcp251x_open(struct net_device *net) priv->tx_skb = NULL; priv->tx_len = 0; - flags = IRQF_ONESHOT; - if (pdata->irq_flags) - flags |= pdata->irq_flags; - else - flags |= IRQF_TRIGGER_FALLING; - ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, flags, DEVICE_NAME, priv); if (ret) { diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index 8a2725676d8b..dc029dba7a03 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -12,12 +12,10 @@ /* * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data * @oscillator_frequency: - oscillator frequency in Hz - * @irq_flags: - IRQF configuration flags */ struct mcp251x_platform_data { unsigned long oscillator_frequency; - unsigned long irq_flags; }; #endif /* __CAN_PLATFORM_MCP251X_H__ */ -- cgit v1.2.3 From 2771399ac9986c75437a83b1c723493cfcdfa439 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Thu, 22 Aug 2013 21:55:13 +0200 Subject: fs_enet: cleanup clock API use make the Freescale ethernet driver get, prepare and enable the FEC clock during probe(); disable and unprepare the clock upon remove(), put is done by the devm approach; hold a reference to the clock over the period of use. clock lookup is non-fatal as not all platforms provide clock specs in their device tree; failure to enable specified clocks is fatal. Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin Signed-off-by: David S. Miller --- .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 20 ++++++++++++++++++++ include/linux/fs_enet_pd.h | 3 +++ 2 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index c04eb3aeb834..6b60582ce8cf 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -999,6 +999,8 @@ static int fs_enet_probe(struct platform_device *ofdev) struct fs_enet_private *fep; struct fs_platform_info *fpi; const u32 *data; + struct clk *clk; + int err; const u8 *mac_addr; const char *phy_connection_type; int privsize, len, ret = -ENODEV; @@ -1036,6 +1038,20 @@ static int fs_enet_probe(struct platform_device *ofdev) fpi->use_rmii = 1; } + /* make clock lookup non-fatal (the driver is shared among platforms), + * but require enable to succeed when a clock was specified/found, + * keep a reference to the clock upon successful acquisition + */ + clk = devm_clk_get(&ofdev->dev, "per"); + if (!IS_ERR(clk)) { + err = clk_prepare_enable(clk); + if (err) { + ret = err; + goto out_free_fpi; + } + fpi->clk_per = clk; + } + privsize = sizeof(*fep) + sizeof(struct sk_buff **) * (fpi->rx_ring + fpi->tx_ring); @@ -1107,6 +1123,8 @@ out_free_dev: free_netdev(ndev); out_put: of_node_put(fpi->phy_node); + if (fpi->clk_per) + clk_disable_unprepare(fpi->clk_per); out_free_fpi: kfree(fpi); return ret; @@ -1123,6 +1141,8 @@ static int fs_enet_remove(struct platform_device *ofdev) fep->ops->cleanup_data(ndev); dev_set_drvdata(fep->dev, NULL); of_node_put(fep->fpi->phy_node); + if (fep->fpi->clk_per) + clk_disable_unprepare(fep->fpi->clk_per); free_netdev(ndev); return 0; } diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 343d82a54468..efb05961bdd8 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -16,6 +16,7 @@ #ifndef FS_ENET_PD_H #define FS_ENET_PD_H +#include #include #include #include @@ -143,6 +144,8 @@ struct fs_platform_info { int use_rmii; /* use RMII mode */ int has_phy; /* if the network is phy container as well...*/ + + struct clk *clk_per; /* 'per' clock for register access */ }; struct fs_mii_fec_platform_info { u32 irq[32]; -- cgit v1.2.3 From cfe51ec1ae427ec0be5a7670eae815ce5eb30e1c Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 24 Aug 2013 00:32:30 +0200 Subject: bcma: add method to power up and down the PCIe core by wifi driver The wifi driver should tell the PCIe core that it is now in operation so that some workarounds can be applied and the power state is changed. This should replace the call to bcma_core_pci_extend_L1timer by the brcmsmac driver. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 26 ++++++++++++++++++++++++++ include/linux/bcma/bcma_driver_pci.h | 3 +++ 2 files changed, 29 insertions(+) (limited to 'include/linux') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index cf7a476a519f..6ea817fae59f 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -275,3 +275,29 @@ void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); } EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer); + +void bcma_core_pci_up(struct bcma_bus *bus) +{ + struct bcma_drv_pci *pc; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + bcma_core_pci_extend_L1timer(pc, true); +} +EXPORT_SYMBOL_GPL(bcma_core_pci_up); + +void bcma_core_pci_down(struct bcma_bus *bus) +{ + struct bcma_drv_pci *pc; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + bcma_core_pci_extend_L1timer(pc, false); +} +EXPORT_SYMBOL_GPL(bcma_core_pci_down); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 424760f01b9d..27f9ceb68b42 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -185,6 +185,7 @@ struct pci_dev; #define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 struct bcma_drv_pci; +struct bcma_bus; #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE struct bcma_drv_pci_host { @@ -220,6 +221,8 @@ extern void bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend); +extern void bcma_core_pci_up(struct bcma_bus *bus); +extern void bcma_core_pci_down(struct bcma_bus *bus); extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev); -- cgit v1.2.3 From 780335acc815802dcee63d75f5589d43c3ccb402 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 24 Aug 2013 00:32:32 +0200 Subject: bcma: do not export bcma_core_pci_extend_L1timer() This is not called any more, do not export it. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 3 +-- include/linux/bcma/bcma_driver_pci.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 6ea817fae59f..d51d1943a202 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -262,7 +262,7 @@ out: } EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); -void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) +static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) { u32 w; @@ -274,7 +274,6 @@ void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w); bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); } -EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer); void bcma_core_pci_up(struct bcma_bus *bus) { diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 27f9ceb68b42..0234955aa9c7 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -220,7 +220,6 @@ struct bcma_drv_pci { extern void bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); -extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend); extern void bcma_core_pci_up(struct bcma_bus *bus); extern void bcma_core_pci_down(struct bcma_bus *bus); -- cgit v1.2.3 From 521deea64088bc885a76bd174241eaa3d3a6876f Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 24 Aug 2013 00:32:33 +0200 Subject: bcma: add bcma_core_pci_power_save() This enables or disables power saving on the PCIe bus when the wifi is in operation or not. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 36 +++++++++++++++++++++++++++++++++--- include/linux/bcma/bcma_driver_pci.h | 20 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index d51d1943a202..c9fd6943ce45 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -31,7 +31,7 @@ static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data) pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data); } -static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) +static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy) { u32 v; int i; @@ -55,7 +55,7 @@ static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) } } -static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) +static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address) { int max_retries = 10; u16 ret = 0; @@ -98,7 +98,7 @@ static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) return ret; } -static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, +static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device, u8 address, u16 data) { int max_retries = 10; @@ -137,6 +137,13 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); } +static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device, + u8 address, u16 data) +{ + bcma_pcie_mdio_write(pc, device, address, data); + return bcma_pcie_mdio_read(pc, device, address); +} + /************************************************** * Workarounds. **************************************************/ @@ -203,6 +210,25 @@ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) } } +static void bcma_core_pci_power_save(struct bcma_drv_pci *pc, bool up) +{ + u16 data; + + if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) { + data = up ? 0x74 : 0x7C; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) { + data = up ? 0x75 : 0x7D; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } +} + /************************************************** * Init. **************************************************/ @@ -284,6 +310,8 @@ void bcma_core_pci_up(struct bcma_bus *bus) pc = &bus->drv_pci[0]; + bcma_core_pci_power_save(pc, true); + bcma_core_pci_extend_L1timer(pc, true); } EXPORT_SYMBOL_GPL(bcma_core_pci_up); @@ -298,5 +326,7 @@ void bcma_core_pci_down(struct bcma_bus *bus) pc = &bus->drv_pci[0]; bcma_core_pci_extend_L1timer(pc, false); + + bcma_core_pci_power_save(pc, false); } EXPORT_SYMBOL_GPL(bcma_core_pci_down); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 0234955aa9c7..d66033f418c9 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -181,6 +181,26 @@ struct pci_dev; #define BCMA_CORE_PCI_CFG_DEVCTRL 0xd8 +#define BCMA_CORE_PCI_ + +/* MDIO devices (SERDES modules) */ +#define BCMA_CORE_PCI_MDIO_IEEE0 0x000 +#define BCMA_CORE_PCI_MDIO_IEEE1 0x001 +#define BCMA_CORE_PCI_MDIO_BLK0 0x800 +#define BCMA_CORE_PCI_MDIO_BLK1 0x801 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT0 0x16 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT1 0x17 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT2 0x18 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT3 0x19 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT4 0x1A +#define BCMA_CORE_PCI_MDIO_BLK2 0x802 +#define BCMA_CORE_PCI_MDIO_BLK3 0x803 +#define BCMA_CORE_PCI_MDIO_BLK4 0x804 +#define BCMA_CORE_PCI_MDIO_TXPLL 0x808 /* TXPLL register block idx */ +#define BCMA_CORE_PCI_MDIO_TXCTRL0 0x820 +#define BCMA_CORE_PCI_MDIO_SERDESID 0x831 +#define BCMA_CORE_PCI_MDIO_RXCTRL0 0x840 + /* PCIE Root Capability Register bits (Host mode only) */ #define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 -- cgit v1.2.3 From 41d73ec053d2424599c4ed8452b889374d523ade Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 27 Aug 2013 08:50:12 +0200 Subject: netfilter: nf_conntrack: make sequence number adjustments usuable without NAT Split out sequence number adjustments from NAT and move them to the conntrack core to make them usable for SYN proxying. The sequence number adjustment information is moved to a seperate extend. The extend is added to new conntracks when a NAT mapping is set up for a connection using a helper. As a side effect, this saves 24 bytes per connection with NAT in the common case that a connection does not have a helper assigned. Signed-off-by: Patrick McHardy Tested-by: Martin Topholm Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 9 +- include/net/netfilter/nf_conntrack_extend.h | 2 + include/net/netfilter/nf_conntrack_seqadj.h | 49 +++++ include/net/netfilter/nf_nat.h | 10 - include/net/netfilter/nf_nat_helper.h | 19 -- include/uapi/linux/netfilter/nf_conntrack_common.h | 3 +- include/uapi/linux/netfilter/nfnetlink_conntrack.h | 15 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 7 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 7 +- net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_core.c | 16 +- net/netfilter/nf_conntrack_netlink.c | 115 +++++------ net/netfilter/nf_conntrack_proto_tcp.c | 18 +- net/netfilter/nf_conntrack_seqadj.c | 218 ++++++++++++++++++++ net/netfilter/nf_nat_core.c | 16 +- net/netfilter/nf_nat_helper.c | 228 +-------------------- net/netfilter/nf_nat_sip.c | 3 +- net/netfilter/nfnetlink_queue_ct.c | 8 +- 18 files changed, 369 insertions(+), 376 deletions(-) create mode 100644 include/net/netfilter/nf_conntrack_seqadj.h create mode 100644 net/netfilter/nf_conntrack_seqadj.c (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index e2cf786be22f..708fe72ab913 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -319,6 +319,7 @@ extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; struct nf_conn; +enum ip_conntrack_info; struct nlattr; struct nfq_ct_hook { @@ -327,14 +328,10 @@ struct nfq_ct_hook { int (*parse)(const struct nlattr *attr, struct nf_conn *ct); int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report); -}; -extern struct nfq_ct_hook __rcu *nfq_ct_hook; - -struct nfq_ct_nat_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, - u32 ctinfo, s32 off); + enum ip_conntrack_info ctinfo, s32 off); }; -extern struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook; +extern struct nfq_ct_hook __rcu *nfq_ct_hook; #else static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 977bc8a46444..2a22bcbfe6e4 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -9,6 +9,7 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, #if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE) NF_CT_EXT_NAT, + NF_CT_EXT_SEQADJ, #endif NF_CT_EXT_ACCT, #ifdef CONFIG_NF_CONNTRACK_EVENTS @@ -31,6 +32,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat +#define NF_CT_EXT_SEQADJ_TYPE struct nf_conn_seqadj #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone diff --git a/include/net/netfilter/nf_conntrack_seqadj.h b/include/net/netfilter/nf_conntrack_seqadj.h new file mode 100644 index 000000000000..30bfbbed9f47 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_seqadj.h @@ -0,0 +1,49 @@ +#ifndef _NF_CONNTRACK_SEQADJ_H +#define _NF_CONNTRACK_SEQADJ_H + +#include + +/** + * struct nf_ct_seqadj - sequence number adjustment information + * + * @correction_pos: position of the last TCP sequence number modification + * @offset_before: sequence number offset before last modification + * @offset_after: sequence number offset after last modification + */ +struct nf_ct_seqadj { + u32 correction_pos; + s32 offset_before; + s32 offset_after; +}; + +struct nf_conn_seqadj { + struct nf_ct_seqadj seq[IP_CT_DIR_MAX]; +}; + +static inline struct nf_conn_seqadj *nfct_seqadj(const struct nf_conn *ct) +{ + return nf_ct_ext_find(ct, NF_CT_EXT_SEQADJ); +} + +static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct) +{ + return nf_ct_ext_add(ct, NF_CT_EXT_SEQADJ, GFP_ATOMIC); +} + +extern int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + __be32 seq, s32 off); +extern void nf_ct_tcp_seqadj_set(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + s32 off); + +extern int nf_ct_seq_adjust(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff); +extern s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir, + u32 seq); + +extern int nf_conntrack_seqadj_init(void); +extern void nf_conntrack_seqadj_fini(void); + +#endif /* _NF_CONNTRACK_SEQADJ_H */ diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index e2441413675c..59a192420053 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -13,15 +13,6 @@ enum nf_nat_manip_type { #define HOOK2MANIP(hooknum) ((hooknum) != NF_INET_POST_ROUTING && \ (hooknum) != NF_INET_LOCAL_IN) -/* NAT sequence number modifications */ -struct nf_nat_seq { - /* position of the last TCP sequence number modification (if any) */ - u_int32_t correction_pos; - - /* sequence number offset before and after last modification */ - int32_t offset_before, offset_after; -}; - #include #include #include @@ -39,7 +30,6 @@ struct nf_conn; /* The structure embedded in the conntrack structure. */ struct nf_conn_nat { struct hlist_node bysource; - struct nf_nat_seq seq[IP_CT_DIR_MAX]; struct nf_conn *ct; union nf_conntrack_nat_help help; #if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 194c34794923..404324d1d0c4 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -39,28 +39,9 @@ extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, const char *rep_buffer, unsigned int rep_len); -extern void nf_nat_set_seq_adjust(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - __be32 seq, s32 off); -extern int nf_nat_seq_adjust(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff); -extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff); - /* Setup NAT on this expected conntrack so it follows master, but goes * to port ct->master->saved_proto. */ extern void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this); -extern s32 nf_nat_get_offset(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq); - -extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - u32 dir, s32 off); - #endif diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index d69483fb3825..8dd803818ebe 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -99,7 +99,8 @@ enum ip_conntrack_events { IPCT_PROTOINFO, /* protocol information has changed */ IPCT_HELPER, /* new helper has been set */ IPCT_MARK, /* new mark has been set */ - IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */ + IPCT_SEQADJ, /* sequence adjustment has changed */ + IPCT_NATSEQADJ = IPCT_SEQADJ, IPCT_SECMARK, /* new security mark has been set */ IPCT_LABEL, /* new connlabel has been set */ }; diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 08fabc6c93f3..acad6c52a652 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -42,8 +42,10 @@ enum ctattr_type { CTA_ID, CTA_NAT_DST, CTA_TUPLE_MASTER, - CTA_NAT_SEQ_ADJ_ORIG, - CTA_NAT_SEQ_ADJ_REPLY, + CTA_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, + CTA_SEQ_ADJ_REPLY, + CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, CTA_SECMARK, /* obsolete */ CTA_ZONE, CTA_SECCTX, @@ -165,6 +167,15 @@ enum ctattr_protonat { }; #define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) +enum ctattr_seqadj { + CTA_SEQADJ_UNSPEC, + CTA_SEQADJ_CORRECTION_POS, + CTA_SEQADJ_OFFSET_BEFORE, + CTA_SEQADJ_OFFSET_AFTER, + __CTA_SEQADJ_MAX +}; +#define CTA_SEQADJ_MAX (__CTA_SEQADJ_MAX - 1) + enum ctattr_natseq { CTA_NAT_SEQ_UNSPEC, CTA_NAT_SEQ_CORRECTION_POS, diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 0a2e0e3e95ba..86f5b34a4ed1 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -136,11 +137,7 @@ static unsigned int ipv4_confirm(unsigned int hooknum, /* adjust seqs for loopback traffic only in outgoing direction */ if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb)) { - typeof(nf_nat_seq_adjust_hook) seq_adjust; - - seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); - if (!seq_adjust || - !seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) { + if (!nf_ct_seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) { NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); return NF_DROP; } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index c9b6a6e6a1e8..d6e4dd8b58df 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -158,11 +159,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, /* adjust seqs for loopback traffic only in outgoing direction */ if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb)) { - typeof(nf_nat_seq_adjust_hook) seq_adjust; - - seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); - if (!seq_adjust || - !seq_adjust(skb, ct, ctinfo, protoff)) { + if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); return NF_DROP; } diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index ebfa7dc747cd..89a9c1658f5e 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,6 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o -nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o +nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack_seqadj.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index da6f1787a102..00a7a94d4132 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1326,6 +1327,7 @@ void nf_conntrack_cleanup_end(void) nf_ct_extend_unregister(&nf_ct_zone_extend); #endif nf_conntrack_proto_fini(); + nf_conntrack_seqadj_fini(); nf_conntrack_labels_fini(); nf_conntrack_helper_fini(); nf_conntrack_timeout_fini(); @@ -1531,6 +1533,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_labels; + ret = nf_conntrack_seqadj_init(); + if (ret < 0) + goto err_seqadj; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1555,6 +1561,8 @@ err_proto: nf_ct_extend_unregister(&nf_ct_zone_extend); err_extend: #endif + nf_conntrack_seqadj_fini(); +err_seqadj: nf_conntrack_labels_fini(); err_labels: nf_conntrack_helper_fini(); @@ -1577,9 +1585,6 @@ void nf_conntrack_init_end(void) /* For use by REJECT target */ RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach); RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack); - - /* Howto get NAT offsets */ - RCU_INIT_POINTER(nf_ct_nat_offset, NULL); } /* @@ -1666,8 +1671,3 @@ err_slabname: err_stat: return ret; } - -s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq); -EXPORT_SYMBOL_GPL(nf_ct_nat_offset); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index fa61fea63234..7c55745ececf 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -381,9 +382,8 @@ nla_put_failure: return -1; } -#ifdef CONFIG_NF_NAT_NEEDED static int -dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) +dump_ct_seq_adj(struct sk_buff *skb, const struct nf_ct_seqadj *seq, int type) { struct nlattr *nest_parms; @@ -391,12 +391,12 @@ dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) if (!nest_parms) goto nla_put_failure; - if (nla_put_be32(skb, CTA_NAT_SEQ_CORRECTION_POS, - htonl(natseq->correction_pos)) || - nla_put_be32(skb, CTA_NAT_SEQ_OFFSET_BEFORE, - htonl(natseq->offset_before)) || - nla_put_be32(skb, CTA_NAT_SEQ_OFFSET_AFTER, - htonl(natseq->offset_after))) + if (nla_put_be32(skb, CTA_SEQADJ_CORRECTION_POS, + htonl(seq->correction_pos)) || + nla_put_be32(skb, CTA_SEQADJ_OFFSET_BEFORE, + htonl(seq->offset_before)) || + nla_put_be32(skb, CTA_SEQADJ_OFFSET_AFTER, + htonl(seq->offset_after))) goto nla_put_failure; nla_nest_end(skb, nest_parms); @@ -408,27 +408,24 @@ nla_put_failure: } static inline int -ctnetlink_dump_nat_seq_adj(struct sk_buff *skb, const struct nf_conn *ct) +ctnetlink_dump_ct_seq_adj(struct sk_buff *skb, const struct nf_conn *ct) { - struct nf_nat_seq *natseq; - struct nf_conn_nat *nat = nfct_nat(ct); + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + struct nf_ct_seqadj *seq; - if (!(ct->status & IPS_SEQ_ADJUST) || !nat) + if (!(ct->status & IPS_SEQ_ADJUST) || !seqadj) return 0; - natseq = &nat->seq[IP_CT_DIR_ORIGINAL]; - if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_ORIG) == -1) + seq = &seqadj->seq[IP_CT_DIR_ORIGINAL]; + if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_ORIG) == -1) return -1; - natseq = &nat->seq[IP_CT_DIR_REPLY]; - if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_REPLY) == -1) + seq = &seqadj->seq[IP_CT_DIR_REPLY]; + if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_REPLY) == -1) return -1; return 0; } -#else -#define ctnetlink_dump_nat_seq_adj(a, b) (0) -#endif static inline int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct) @@ -502,7 +499,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 || - ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; nlmsg_end(skb, nlh); @@ -707,8 +704,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) ctnetlink_dump_master(skb, ct) < 0) goto nla_put_failure; - if (events & (1 << IPCT_NATSEQADJ) && - ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + if (events & (1 << IPCT_SEQADJ) && + ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; } @@ -1439,66 +1436,65 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[] return err; } -#ifdef CONFIG_NF_NAT_NEEDED -static const struct nla_policy nat_seq_policy[CTA_NAT_SEQ_MAX+1] = { - [CTA_NAT_SEQ_CORRECTION_POS] = { .type = NLA_U32 }, - [CTA_NAT_SEQ_OFFSET_BEFORE] = { .type = NLA_U32 }, - [CTA_NAT_SEQ_OFFSET_AFTER] = { .type = NLA_U32 }, +static const struct nla_policy seqadj_policy[CTA_SEQADJ_MAX+1] = { + [CTA_SEQADJ_CORRECTION_POS] = { .type = NLA_U32 }, + [CTA_SEQADJ_OFFSET_BEFORE] = { .type = NLA_U32 }, + [CTA_SEQADJ_OFFSET_AFTER] = { .type = NLA_U32 }, }; static inline int -change_nat_seq_adj(struct nf_nat_seq *natseq, const struct nlattr * const attr) +change_seq_adj(struct nf_ct_seqadj *seq, const struct nlattr * const attr) { int err; - struct nlattr *cda[CTA_NAT_SEQ_MAX+1]; + struct nlattr *cda[CTA_SEQADJ_MAX+1]; - err = nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, nat_seq_policy); + err = nla_parse_nested(cda, CTA_SEQADJ_MAX, attr, seqadj_policy); if (err < 0) return err; - if (!cda[CTA_NAT_SEQ_CORRECTION_POS]) + if (!cda[CTA_SEQADJ_CORRECTION_POS]) return -EINVAL; - natseq->correction_pos = - ntohl(nla_get_be32(cda[CTA_NAT_SEQ_CORRECTION_POS])); + seq->correction_pos = + ntohl(nla_get_be32(cda[CTA_SEQADJ_CORRECTION_POS])); - if (!cda[CTA_NAT_SEQ_OFFSET_BEFORE]) + if (!cda[CTA_SEQADJ_OFFSET_BEFORE]) return -EINVAL; - natseq->offset_before = - ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_BEFORE])); + seq->offset_before = + ntohl(nla_get_be32(cda[CTA_SEQADJ_OFFSET_BEFORE])); - if (!cda[CTA_NAT_SEQ_OFFSET_AFTER]) + if (!cda[CTA_SEQADJ_OFFSET_AFTER]) return -EINVAL; - natseq->offset_after = - ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_AFTER])); + seq->offset_after = + ntohl(nla_get_be32(cda[CTA_SEQADJ_OFFSET_AFTER])); return 0; } static int -ctnetlink_change_nat_seq_adj(struct nf_conn *ct, - const struct nlattr * const cda[]) +ctnetlink_change_seq_adj(struct nf_conn *ct, + const struct nlattr * const cda[]) { + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); int ret = 0; - struct nf_conn_nat *nat = nfct_nat(ct); - if (!nat) + if (!seqadj) return 0; - if (cda[CTA_NAT_SEQ_ADJ_ORIG]) { - ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_ORIGINAL], - cda[CTA_NAT_SEQ_ADJ_ORIG]); + if (cda[CTA_SEQ_ADJ_ORIG]) { + ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_ORIGINAL], + cda[CTA_SEQ_ADJ_ORIG]); if (ret < 0) return ret; ct->status |= IPS_SEQ_ADJUST; } - if (cda[CTA_NAT_SEQ_ADJ_REPLY]) { - ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_REPLY], - cda[CTA_NAT_SEQ_ADJ_REPLY]); + if (cda[CTA_SEQ_ADJ_REPLY]) { + ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_REPLY], + cda[CTA_SEQ_ADJ_REPLY]); if (ret < 0) return ret; @@ -1507,7 +1503,6 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct, return 0; } -#endif static int ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[]) @@ -1573,13 +1568,12 @@ ctnetlink_change_conntrack(struct nf_conn *ct, ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); #endif -#ifdef CONFIG_NF_NAT_NEEDED - if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { - err = ctnetlink_change_nat_seq_adj(ct, cda); + if (cda[CTA_SEQ_ADJ_ORIG] || cda[CTA_SEQ_ADJ_REPLY]) { + err = ctnetlink_change_seq_adj(ct, cda); if (err < 0) return err; } -#endif + if (cda[CTA_LABELS]) { err = ctnetlink_attach_labels(ct, cda); if (err < 0) @@ -1684,13 +1678,11 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, goto err2; } -#ifdef CONFIG_NF_NAT_NEEDED - if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { - err = ctnetlink_change_nat_seq_adj(ct, cda); + if (cda[CTA_SEQ_ADJ_ORIG] || cda[CTA_SEQ_ADJ_REPLY]) { + err = ctnetlink_change_seq_adj(ct, cda); if (err < 0) goto err2; } -#endif memset(&ct->proto, 0, sizeof(ct->proto)); if (cda[CTA_PROTOINFO]) { @@ -1804,7 +1796,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, (1 << IPCT_ASSURED) | (1 << IPCT_HELPER) | (1 << IPCT_PROTOINFO) | - (1 << IPCT_NATSEQADJ) | + (1 << IPCT_SEQADJ) | (1 << IPCT_MARK) | events, ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); @@ -1827,7 +1819,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, (1 << IPCT_HELPER) | (1 << IPCT_LABEL) | (1 << IPCT_PROTOINFO) | - (1 << IPCT_NATSEQADJ) | + (1 << IPCT_SEQADJ) | (1 << IPCT_MARK), ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); @@ -2082,7 +2074,7 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) goto nla_put_failure; if ((ct->status & IPS_SEQ_ADJUST) && - ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; #ifdef CONFIG_NF_CONNTRACK_MARK @@ -2211,6 +2203,7 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, .attach_expect = ctnetlink_nfqueue_attach_expect, + .seq_adjust = nf_ct_tcp_seqadj_set, }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index d224e001f14f..984a8d1a3359 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -495,21 +496,6 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, } } -#ifdef CONFIG_NF_NAT_NEEDED -static inline s32 nat_offset(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq) -{ - typeof(nf_ct_nat_offset) get_offset = rcu_dereference(nf_ct_nat_offset); - - return get_offset != NULL ? get_offset(ct, dir, seq) : 0; -} -#define NAT_OFFSET(ct, dir, seq) \ - (nat_offset(ct, dir, seq)) -#else -#define NAT_OFFSET(ct, dir, seq) 0 -#endif - static bool tcp_in_window(const struct nf_conn *ct, struct ip_ct_tcp *state, enum ip_conntrack_dir dir, @@ -540,7 +526,7 @@ static bool tcp_in_window(const struct nf_conn *ct, tcp_sack(skb, dataoff, tcph, &sack); /* Take into account NAT sequence number mangling */ - receiver_offset = NAT_OFFSET(ct, !dir, ack - 1); + receiver_offset = nf_ct_seq_offset(ct, !dir, ack - 1); ack -= receiver_offset; sack -= receiver_offset; diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c new file mode 100644 index 000000000000..483eb9ce3216 --- /dev/null +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -0,0 +1,218 @@ +#include +#include +#include + +#include +#include +#include + +int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + __be32 seq, s32 off) +{ + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_ct_seqadj *this_way; + + if (off == 0) + return 0; + + set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); + + spin_lock_bh(&ct->lock); + this_way = &seqadj->seq[dir]; + if (this_way->offset_before == this_way->offset_after || + before(this_way->correction_pos, seq)) { + this_way->correction_pos = seq; + this_way->offset_before = this_way->offset_after; + this_way->offset_after += off; + } + spin_unlock_bh(&ct->lock); + return 0; +} +EXPORT_SYMBOL_GPL(nf_ct_seqadj_set); + +void nf_ct_tcp_seqadj_set(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + s32 off) +{ + const struct tcphdr *th; + + if (nf_ct_protonum(ct) != IPPROTO_TCP) + return; + + th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb)); + nf_ct_seqadj_set(ct, ctinfo, th->seq, off); +} +EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set); + +/* Adjust one found SACK option including checksum correction */ +static void nf_ct_sack_block_adjust(struct sk_buff *skb, + struct tcphdr *tcph, + unsigned int sackoff, + unsigned int sackend, + struct nf_ct_seqadj *seq) +{ + while (sackoff < sackend) { + struct tcp_sack_block_wire *sack; + __be32 new_start_seq, new_end_seq; + + sack = (void *)skb->data + sackoff; + if (after(ntohl(sack->start_seq) - seq->offset_before, + seq->correction_pos)) + new_start_seq = htonl(ntohl(sack->start_seq) - + seq->offset_after); + else + new_start_seq = htonl(ntohl(sack->start_seq) - + seq->offset_before); + + if (after(ntohl(sack->end_seq) - seq->offset_before, + seq->correction_pos)) + new_end_seq = htonl(ntohl(sack->end_seq) - + seq->offset_after); + else + new_end_seq = htonl(ntohl(sack->end_seq) - + seq->offset_before); + + pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", + ntohl(sack->start_seq), new_start_seq, + ntohl(sack->end_seq), new_end_seq); + + inet_proto_csum_replace4(&tcph->check, skb, + sack->start_seq, new_start_seq, 0); + inet_proto_csum_replace4(&tcph->check, skb, + sack->end_seq, new_end_seq, 0); + sack->start_seq = new_start_seq; + sack->end_seq = new_end_seq; + sackoff += sizeof(*sack); + } +} + +/* TCP SACK sequence number adjustment */ +static unsigned int nf_ct_sack_adjust(struct sk_buff *skb, + unsigned int protoff, + struct tcphdr *tcph, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo) +{ + unsigned int dir, optoff, optend; + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + + optoff = protoff + sizeof(struct tcphdr); + optend = protoff + tcph->doff * 4; + + if (!skb_make_writable(skb, optend)) + return 0; + + dir = CTINFO2DIR(ctinfo); + + while (optoff < optend) { + /* Usually: option, length. */ + unsigned char *op = skb->data + optoff; + + switch (op[0]) { + case TCPOPT_EOL: + return 1; + case TCPOPT_NOP: + optoff++; + continue; + default: + /* no partial options */ + if (optoff + 1 == optend || + optoff + op[1] > optend || + op[1] < 2) + return 0; + if (op[0] == TCPOPT_SACK && + op[1] >= 2+TCPOLEN_SACK_PERBLOCK && + ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) + nf_ct_sack_block_adjust(skb, tcph, optoff + 2, + optoff+op[1], + &seqadj->seq[!dir]); + optoff += op[1]; + } + } + return 1; +} + +/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ +int nf_ct_seq_adjust(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff) +{ + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct tcphdr *tcph; + __be32 newseq, newack; + s32 seqoff, ackoff; + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + struct nf_ct_seqadj *this_way, *other_way; + int res; + + this_way = &seqadj->seq[dir]; + other_way = &seqadj->seq[!dir]; + + if (!skb_make_writable(skb, protoff + sizeof(*tcph))) + return 0; + + tcph = (void *)skb->data + protoff; + spin_lock_bh(&ct->lock); + if (after(ntohl(tcph->seq), this_way->correction_pos)) + seqoff = this_way->offset_after; + else + seqoff = this_way->offset_before; + + if (after(ntohl(tcph->ack_seq) - other_way->offset_before, + other_way->correction_pos)) + ackoff = other_way->offset_after; + else + ackoff = other_way->offset_before; + + newseq = htonl(ntohl(tcph->seq) + seqoff); + newack = htonl(ntohl(tcph->ack_seq) - ackoff); + + inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); + inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); + + pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n", + ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), + ntohl(newack)); + + tcph->seq = newseq; + tcph->ack_seq = newack; + + res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo); + spin_unlock_bh(&ct->lock); + + return res; +} +EXPORT_SYMBOL_GPL(nf_ct_seq_adjust); + +s32 nf_ct_seq_offset(const struct nf_conn *ct, + enum ip_conntrack_dir dir, + u32 seq) +{ + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + struct nf_ct_seqadj *this_way; + + if (!seqadj) + return 0; + + this_way = &seqadj->seq[dir]; + return after(seq, this_way->correction_pos) ? + this_way->offset_after : this_way->offset_before; +} +EXPORT_SYMBOL_GPL(nf_ct_seq_offset); + +static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly = { + .len = sizeof(struct nf_conn_seqadj), + .align = __alignof__(struct nf_conn_seqadj), + .id = NF_CT_EXT_SEQADJ, +}; + +int nf_conntrack_seqadj_init(void) +{ + return nf_ct_extend_register(&nf_ct_seqadj_extend); +} + +void nf_conntrack_seqadj_fini(void) +{ + nf_ct_extend_unregister(&nf_ct_seqadj_extend); +} diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 6ff808375b5e..6f0f4f7f68a5 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -402,6 +403,9 @@ nf_nat_setup_info(struct nf_conn *ct, ct->status |= IPS_SRC_NAT; else ct->status |= IPS_DST_NAT; + + if (nfct_help(ct)) + nfct_seqadj_ext_add(ct); } if (maniptype == NF_NAT_MANIP_SRC) { @@ -764,10 +768,6 @@ static struct nf_ct_helper_expectfn follow_master_nat = { .expectfn = nf_nat_follow_master, }; -static struct nfq_ct_nat_hook nfq_ct_nat = { - .seq_adjust = nf_nat_tcp_seq_adjust, -}; - static int __init nf_nat_init(void) { int ret; @@ -787,14 +787,9 @@ static int __init nf_nat_init(void) /* Initialize fake conntrack so that NAT will skip it */ nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); - BUG_ON(nf_nat_seq_adjust_hook != NULL); - RCU_INIT_POINTER(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, nfnetlink_parse_nat_setup); - BUG_ON(nf_ct_nat_offset != NULL); - RCU_INIT_POINTER(nf_ct_nat_offset, nf_nat_get_offset); - RCU_INIT_POINTER(nfq_ct_nat_hook, &nfq_ct_nat); #ifdef CONFIG_XFRM BUG_ON(nf_nat_decode_session_hook != NULL); RCU_INIT_POINTER(nf_nat_decode_session_hook, __nf_nat_decode_session); @@ -813,10 +808,7 @@ static void __exit nf_nat_cleanup(void) unregister_pernet_subsys(&nf_nat_net_ops); nf_ct_extend_unregister(&nat_extend); nf_ct_helper_expectfn_unregister(&follow_master_nat); - RCU_INIT_POINTER(nf_nat_seq_adjust_hook, NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL); - RCU_INIT_POINTER(nf_ct_nat_offset, NULL); - RCU_INIT_POINTER(nfq_ct_nat_hook, NULL); #ifdef CONFIG_XFRM RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL); #endif diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c index 46b9baa845a6..2840abb5bb99 100644 --- a/net/netfilter/nf_nat_helper.c +++ b/net/netfilter/nf_nat_helper.c @@ -20,67 +20,13 @@ #include #include #include +#include #include #include #include #include #include -#define DUMP_OFFSET(x) \ - pr_debug("offset_before=%d, offset_after=%d, correction_pos=%u\n", \ - x->offset_before, x->offset_after, x->correction_pos); - -/* Setup TCP sequence correction given this change at this sequence */ -static inline void -adjust_tcp_sequence(u32 seq, - int sizediff, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way = &nat->seq[dir]; - - pr_debug("adjust_tcp_sequence: seq = %u, sizediff = %d\n", - seq, sizediff); - - pr_debug("adjust_tcp_sequence: Seq_offset before: "); - DUMP_OFFSET(this_way); - - spin_lock_bh(&ct->lock); - - /* SYN adjust. If it's uninitialized, or this is after last - * correction, record it: we don't handle more than one - * adjustment in the window, but do deal with common case of a - * retransmit */ - if (this_way->offset_before == this_way->offset_after || - before(this_way->correction_pos, seq)) { - this_way->correction_pos = seq; - this_way->offset_before = this_way->offset_after; - this_way->offset_after += sizediff; - } - spin_unlock_bh(&ct->lock); - - pr_debug("adjust_tcp_sequence: Seq_offset after: "); - DUMP_OFFSET(this_way); -} - -/* Get the offset value, for conntrack. Caller must have the conntrack locked */ -s32 nf_nat_get_offset(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq) -{ - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way; - - if (!nat) - return 0; - - this_way = &nat->seq[dir]; - return after(seq, this_way->correction_pos) - ? this_way->offset_after : this_way->offset_before; -} - /* Frobs data inside this packet, which is linear. */ static void mangle_contents(struct sk_buff *skb, unsigned int dataoff, @@ -135,30 +81,6 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra) return 1; } -void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s32 off) -{ - if (!off) - return; - set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); - adjust_tcp_sequence(ntohl(seq), off, ct, ctinfo); - nf_conntrack_event_cache(IPCT_NATSEQADJ, ct); -} -EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); - -void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - u32 ctinfo, int off) -{ - const struct tcphdr *th; - - if (nf_ct_protonum(ct) != IPPROTO_TCP) - return; - - th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb)); - nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); -} -EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust); - /* Generic function for mangling variable-length address changes inside * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX * command in FTP). @@ -203,8 +125,8 @@ int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, datalen, oldlen); if (adjust && rep_len != match_len) - nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, - (int)rep_len - (int)match_len); + nf_ct_seqadj_set(ct, ctinfo, tcph->seq, + (int)rep_len - (int)match_len); return 1; } @@ -264,150 +186,6 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb, } EXPORT_SYMBOL(nf_nat_mangle_udp_packet); -/* Adjust one found SACK option including checksum correction */ -static void -sack_adjust(struct sk_buff *skb, - struct tcphdr *tcph, - unsigned int sackoff, - unsigned int sackend, - struct nf_nat_seq *natseq) -{ - while (sackoff < sackend) { - struct tcp_sack_block_wire *sack; - __be32 new_start_seq, new_end_seq; - - sack = (void *)skb->data + sackoff; - if (after(ntohl(sack->start_seq) - natseq->offset_before, - natseq->correction_pos)) - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_after); - else - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_before); - - if (after(ntohl(sack->end_seq) - natseq->offset_before, - natseq->correction_pos)) - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_after); - else - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_before); - - pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", - ntohl(sack->start_seq), new_start_seq, - ntohl(sack->end_seq), new_end_seq); - - inet_proto_csum_replace4(&tcph->check, skb, - sack->start_seq, new_start_seq, 0); - inet_proto_csum_replace4(&tcph->check, skb, - sack->end_seq, new_end_seq, 0); - sack->start_seq = new_start_seq; - sack->end_seq = new_end_seq; - sackoff += sizeof(*sack); - } -} - -/* TCP SACK sequence number adjustment */ -static inline unsigned int -nf_nat_sack_adjust(struct sk_buff *skb, - unsigned int protoff, - struct tcphdr *tcph, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dir, optoff, optend; - struct nf_conn_nat *nat = nfct_nat(ct); - - optoff = protoff + sizeof(struct tcphdr); - optend = protoff + tcph->doff * 4; - - if (!skb_make_writable(skb, optend)) - return 0; - - dir = CTINFO2DIR(ctinfo); - - while (optoff < optend) { - /* Usually: option, length. */ - unsigned char *op = skb->data + optoff; - - switch (op[0]) { - case TCPOPT_EOL: - return 1; - case TCPOPT_NOP: - optoff++; - continue; - default: - /* no partial options */ - if (optoff + 1 == optend || - optoff + op[1] > optend || - op[1] < 2) - return 0; - if (op[0] == TCPOPT_SACK && - op[1] >= 2+TCPOLEN_SACK_PERBLOCK && - ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) - sack_adjust(skb, tcph, optoff+2, - optoff+op[1], &nat->seq[!dir]); - optoff += op[1]; - } - } - return 1; -} - -/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ -int -nf_nat_seq_adjust(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff) -{ - struct tcphdr *tcph; - int dir; - __be32 newseq, newack; - s32 seqoff, ackoff; - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way, *other_way; - int res; - - dir = CTINFO2DIR(ctinfo); - - this_way = &nat->seq[dir]; - other_way = &nat->seq[!dir]; - - if (!skb_make_writable(skb, protoff + sizeof(*tcph))) - return 0; - - tcph = (void *)skb->data + protoff; - spin_lock_bh(&ct->lock); - if (after(ntohl(tcph->seq), this_way->correction_pos)) - seqoff = this_way->offset_after; - else - seqoff = this_way->offset_before; - - if (after(ntohl(tcph->ack_seq) - other_way->offset_before, - other_way->correction_pos)) - ackoff = other_way->offset_after; - else - ackoff = other_way->offset_before; - - newseq = htonl(ntohl(tcph->seq) + seqoff); - newack = htonl(ntohl(tcph->ack_seq) - ackoff); - - inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); - inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); - - pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n", - ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), - ntohl(newack)); - - tcph->seq = newseq; - tcph->ack_seq = newack; - - res = nf_nat_sack_adjust(skb, protoff, tcph, ct, ctinfo); - spin_unlock_bh(&ct->lock); - - return res; -} - /* Setup NAT on this expected conntrack so it follows master. */ /* If we fail to get a free NAT slot, we'll get dropped on confirm */ void nf_nat_follow_master(struct nf_conn *ct, diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index dac11f73868e..f9790405b7ff 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -20,6 +20,7 @@ #include #include #include +#include #include MODULE_LICENSE("GPL"); @@ -308,7 +309,7 @@ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, return; th = (struct tcphdr *)(skb->data + protoff); - nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); + nf_ct_seqadj_set(ct, ctinfo, th->seq, off); } /* Handles expected signalling connections and media streams */ diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c index be893039966d..96cac50e0d12 100644 --- a/net/netfilter/nfnetlink_queue_ct.c +++ b/net/netfilter/nfnetlink_queue_ct.c @@ -87,14 +87,14 @@ nla_put_failure: void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff) { - struct nfq_ct_nat_hook *nfq_nat_ct; + struct nfq_ct_hook *nfq_ct; - nfq_nat_ct = rcu_dereference(nfq_ct_nat_hook); - if (nfq_nat_ct == NULL) + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) return; if ((ct->status & IPS_NAT_MASK) && diff) - nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); + nfq_ct->seq_adjust(skb, ct, ctinfo, diff); } int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, -- cgit v1.2.3 From b800c3b966bcf004bd8592293a49ed5cb7ea67a9 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Tue, 27 Aug 2013 01:36:51 +0200 Subject: ipv6: drop fragmented ndisc packets by default (RFC 6980) This patch implements RFC6980: Drop fragmented ndisc packets by default. If a fragmented ndisc packet is received the user is informed that it is possible to disable the check. Cc: Fernando Gont Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 6 ++++++ include/linux/ipv6.h | 1 + include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 10 ++++++++++ net/ipv6/ndisc.c | 17 +++++++++++++++++ 5 files changed, 35 insertions(+) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index debfe857d8f9..a2be556032c9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1349,6 +1349,12 @@ mldv2_unsolicited_report_interval - INTEGER MLDv2 report retransmit will take place. Default: 1000 (1 second) +suppress_frag_ndisc - INTEGER + Control RFC 6980 (Security Implications of IPv6 Fragmentation + with IPv6 Neighbor Discovery) behavior: + 1 - (default) discard fragmented neighbor discovery packets + 0 - allow fragmented neighbor discovery packets + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 9ac5047062c8..28ea38439313 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -50,6 +50,7 @@ struct ipv6_devconf { __s32 accept_dad; __s32 force_tllao; __s32 ndisc_notify; + __s32 suppress_frag_ndisc; void *sysctl; }; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index d07ac6903e59..593b0e32d956 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -162,6 +162,7 @@ enum { DEVCONF_NDISC_NOTIFY, DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_SUPPRESS_FRAG_NDISC, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 2d6d1793bbfe..a7183fc9bbc2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -204,6 +204,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, .accept_dad = 1, + .suppress_frag_ndisc = 1, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -241,6 +242,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, .accept_dad = 1, + .suppress_frag_ndisc = 1, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -4188,6 +4190,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; + array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; } static inline size_t inet6_ifla6_size(void) @@ -5001,6 +5004,13 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "suppress_frag_ndisc", + .data = &ipv6_devconf.suppress_frag_ndisc, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { /* sentinel */ } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 04d31c2fbef1..41720feeaa64 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1519,10 +1519,27 @@ static void pndisc_redo(struct sk_buff *skb) kfree_skb(skb); } +static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb) +{ + struct inet6_dev *idev = __in6_dev_get(skb->dev); + + if (!idev) + return true; + if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED && + idev->cnf.suppress_frag_ndisc) { + net_warn_ratelimited("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n"); + return true; + } + return false; +} + int ndisc_rcv(struct sk_buff *skb) { struct nd_msg *msg; + if (ndisc_suppress_frag_ndisc(skb)) + return 0; + if (skb_linearize(skb)) return 0; -- cgit v1.2.3 From 5d261913ca3daf6c2d21d38924235667b3d07c40 Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Wed, 28 Aug 2013 23:25:05 +0200 Subject: net: add lower_dev_list to net_device and make a full mesh This patch adds lower_dev_list list_head to net_device, which is the same as upper_dev_list, only for lower devices, and begins to use it in the same way as the upper list. It also changes the way the whole adjacent device lists work - now they contain *all* of upper/lower devices, not only the first level. The first level devices are distinguished by the bool neighbour field in netdev_adjacent, also added by this patch. There are cases when a device can be added several times to the adjacent list, the simplest would be: /---- eth0.10 ---\ eth0- --- bond0 \---- eth0.20 ---/ where both bond0 and eth0 'see' each other in the adjacent lists two times. To avoid duplication of netdev_adjacent structures ref_nr is being kept as the number of times the device was added to the list. The 'full view' is achieved by adding, on link creation, all of the upper_dev's upper_dev_list devices as upper devices to all of the lower_dev's lower_dev_list devices (and to the lower_dev itself), and vice versa. On unlink they are removed using the same logic. I've tested it with thousands vlans/bonds/bridges, everything works ok and no observable lags even on a huge number of interfaces. Memory footprint for 128 devices interconnected with each other via both upper and lower (which is impossible, but for the comparison) lists would be: 128*128*2*sizeof(netdev_adjacent) = 1.5MB but in the real world we usualy have at most several devices with slaves and a lot of vlans, so the footprint will be much lower. CC: "David S. Miller" CC: Eric Dumazet CC: Jiri Pirko CC: Alexander Duyck CC: Cong Wang Signed-off-by: Veaceslav Falico Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 285 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 259 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 077363dcd860..5ccf5b73c378 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1125,6 +1125,7 @@ struct net_device { struct list_head napi_list; struct list_head unreg_list; struct list_head upper_dev_list; /* List of upper devices */ + struct list_head lower_dev_list; /* currently active device features */ diff --git a/net/core/dev.c b/net/core/dev.c index 5072e2c1a072..2aa914eee057 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4369,7 +4369,16 @@ softnet_break: struct netdev_adjacent { struct net_device *dev; + + /* upper master flag, there can only be one master device per list */ bool master; + + /* indicates that this dev is our first-level lower/upper device */ + bool neighbour; + + /* counter for the number of times this device was added to us */ + u16 ref_nr; + struct list_head list; struct rcu_head rcu; struct list_head search_list; @@ -4408,18 +4417,34 @@ static bool __netdev_search_upper_dev(struct net_device *dev, return ret; } -static struct netdev_adjacent *__netdev_find_upper(struct net_device *dev, - struct net_device *upper_dev) +static struct netdev_adjacent *__netdev_find_adj(struct net_device *dev, + struct net_device *adj_dev, + bool upper) { - struct netdev_adjacent *upper; + struct netdev_adjacent *adj; + struct list_head *dev_list; - list_for_each_entry(upper, &dev->upper_dev_list, list) { - if (upper->dev == upper_dev) - return upper; + dev_list = upper ? &dev->upper_dev_list : &dev->lower_dev_list; + + list_for_each_entry(adj, dev_list, list) { + if (adj->dev == adj_dev) + return adj; } return NULL; } +static inline struct netdev_adjacent *__netdev_find_upper(struct net_device *dev, + struct net_device *udev) +{ + return __netdev_find_adj(dev, udev, true); +} + +static inline struct netdev_adjacent *__netdev_find_lower(struct net_device *dev, + struct net_device *ldev) +{ + return __netdev_find_adj(dev, ldev, false); +} + /** * netdev_has_upper_dev - Check if device is linked to an upper device * @dev: device @@ -4496,10 +4521,149 @@ struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev) } EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu); +static int __netdev_adjacent_dev_insert(struct net_device *dev, + struct net_device *adj_dev, + bool neighbour, bool master, + bool upper) +{ + struct netdev_adjacent *adj; + + adj = __netdev_find_adj(dev, adj_dev, upper); + + if (adj) { + BUG_ON(neighbour); + adj->ref_nr++; + return 0; + } + + adj = kmalloc(sizeof(*adj), GFP_KERNEL); + if (!adj) + return -ENOMEM; + + adj->dev = adj_dev; + adj->master = master; + adj->neighbour = neighbour; + adj->ref_nr = 1; + INIT_LIST_HEAD(&adj->search_list); + + dev_hold(adj_dev); + pr_debug("dev_hold for %s, because of %s link added from %s to %s\n", + adj_dev->name, upper ? "upper" : "lower", dev->name, + adj_dev->name); + + if (!upper) { + list_add_tail_rcu(&adj->list, &dev->lower_dev_list); + return 0; + } + + /* Ensure that master upper link is always the first item in list. */ + if (master) + list_add_rcu(&adj->list, &dev->upper_dev_list); + else + list_add_tail_rcu(&adj->list, &dev->upper_dev_list); + + return 0; +} + +static inline int __netdev_upper_dev_insert(struct net_device *dev, + struct net_device *udev, + bool master, bool neighbour) +{ + return __netdev_adjacent_dev_insert(dev, udev, neighbour, master, + true); +} + +static inline int __netdev_lower_dev_insert(struct net_device *dev, + struct net_device *ldev, + bool neighbour) +{ + return __netdev_adjacent_dev_insert(dev, ldev, neighbour, false, + false); +} + +void __netdev_adjacent_dev_remove(struct net_device *dev, + struct net_device *adj_dev, bool upper) +{ + struct netdev_adjacent *adj; + + if (upper) + adj = __netdev_find_upper(dev, adj_dev); + else + adj = __netdev_find_lower(dev, adj_dev); + + if (!adj) + BUG(); + + if (adj->ref_nr > 1) { + adj->ref_nr--; + return; + } + + list_del_rcu(&adj->list); + pr_debug("dev_put for %s, because of %s link removed from %s to %s\n", + adj_dev->name, upper ? "upper" : "lower", dev->name, + adj_dev->name); + dev_put(adj_dev); + kfree_rcu(adj, rcu); +} + +static inline void __netdev_upper_dev_remove(struct net_device *dev, + struct net_device *udev) +{ + return __netdev_adjacent_dev_remove(dev, udev, true); +} + +static inline void __netdev_lower_dev_remove(struct net_device *dev, + struct net_device *ldev) +{ + return __netdev_adjacent_dev_remove(dev, ldev, false); +} + +int __netdev_adjacent_dev_insert_link(struct net_device *dev, + struct net_device *upper_dev, + bool master, bool neighbour) +{ + int ret; + + ret = __netdev_upper_dev_insert(dev, upper_dev, master, neighbour); + if (ret) + return ret; + + ret = __netdev_lower_dev_insert(upper_dev, dev, neighbour); + if (ret) { + __netdev_upper_dev_remove(dev, upper_dev); + return ret; + } + + return 0; +} + +static inline int __netdev_adjacent_dev_link(struct net_device *dev, + struct net_device *udev) +{ + return __netdev_adjacent_dev_insert_link(dev, udev, false, false); +} + +static inline int __netdev_adjacent_dev_link_neighbour(struct net_device *dev, + struct net_device *udev, + bool master) +{ + return __netdev_adjacent_dev_insert_link(dev, udev, master, true); +} + +void __netdev_adjacent_dev_unlink(struct net_device *dev, + struct net_device *upper_dev) +{ + __netdev_upper_dev_remove(dev, upper_dev); + __netdev_lower_dev_remove(upper_dev, dev); +} + + static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master) { - struct netdev_adjacent *upper; + struct netdev_adjacent *i, *j, *to_i, *to_j; + int ret = 0; ASSERT_RTNL(); @@ -4516,22 +4680,76 @@ static int __netdev_upper_dev_link(struct net_device *dev, if (master && netdev_master_upper_dev_get(dev)) return -EBUSY; - upper = kmalloc(sizeof(*upper), GFP_KERNEL); - if (!upper) - return -ENOMEM; + ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, master); + if (ret) + return ret; - upper->dev = upper_dev; - upper->master = master; - INIT_LIST_HEAD(&upper->search_list); + /* Now that we linked these devs, make all the upper_dev's + * upper_dev_list visible to every dev's lower_dev_list and vice + * versa, and don't forget the devices itself. All of these + * links are non-neighbours. + */ + list_for_each_entry(i, &upper_dev->upper_dev_list, list) { + list_for_each_entry(j, &dev->lower_dev_list, list) { + ret = __netdev_adjacent_dev_link(i->dev, j->dev); + if (ret) + goto rollback_mesh; + } + } + + /* add dev to every upper_dev's upper device */ + list_for_each_entry(i, &upper_dev->upper_dev_list, list) { + ret = __netdev_adjacent_dev_link(dev, i->dev); + if (ret) + goto rollback_upper_mesh; + } + + /* add upper_dev to every dev's lower device */ + list_for_each_entry(i, &dev->lower_dev_list, list) { + ret = __netdev_adjacent_dev_link(i->dev, upper_dev); + if (ret) + goto rollback_lower_mesh; + } - /* Ensure that master upper link is always the first item in list. */ - if (master) - list_add_rcu(&upper->list, &dev->upper_dev_list); - else - list_add_tail_rcu(&upper->list, &dev->upper_dev_list); - dev_hold(upper_dev); call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); return 0; + +rollback_lower_mesh: + to_i = i; + list_for_each_entry(i, &dev->lower_dev_list, list) { + if (i == to_i) + break; + __netdev_adjacent_dev_unlink(i->dev, upper_dev); + } + + i = NULL; + +rollback_upper_mesh: + to_i = i; + list_for_each_entry(i, &upper_dev->upper_dev_list, list) { + if (i == to_i) + break; + __netdev_adjacent_dev_unlink(dev, i->dev); + } + + i = j = NULL; + +rollback_mesh: + to_i = i; + to_j = j; + list_for_each_entry(i, &dev->lower_dev_list, list) { + list_for_each_entry(j, &upper_dev->upper_dev_list, list) { + if (i == to_i && j == to_j) + break; + __netdev_adjacent_dev_unlink(i->dev, j->dev); + } + if (i == to_i) + break; + } + + __netdev_adjacent_dev_unlink(dev, upper_dev); + + return ret; } /** @@ -4580,16 +4798,28 @@ EXPORT_SYMBOL(netdev_master_upper_dev_link); void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev) { - struct netdev_adjacent *upper; - + struct netdev_adjacent *i, *j; ASSERT_RTNL(); - upper = __netdev_find_upper(dev, upper_dev); - if (!upper) - return; - list_del_rcu(&upper->list); - dev_put(upper_dev); - kfree_rcu(upper, rcu); + __netdev_adjacent_dev_unlink(dev, upper_dev); + + /* Here is the tricky part. We must remove all dev's lower + * devices from all upper_dev's upper devices and vice + * versa, to maintain the graph relationship. + */ + list_for_each_entry(i, &dev->lower_dev_list, list) + list_for_each_entry(j, &upper_dev->upper_dev_list, list) + __netdev_adjacent_dev_unlink(i->dev, j->dev); + + /* remove also the devices itself from lower/upper device + * list + */ + list_for_each_entry(i, &dev->lower_dev_list, list) + __netdev_adjacent_dev_unlink(i->dev, upper_dev); + + list_for_each_entry(i, &upper_dev->upper_dev_list, list) + __netdev_adjacent_dev_unlink(dev, i->dev); + call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); } EXPORT_SYMBOL(netdev_upper_dev_unlink); @@ -5850,6 +6080,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, INIT_LIST_HEAD(&dev->unreg_list); INIT_LIST_HEAD(&dev->link_watch_list); INIT_LIST_HEAD(&dev->upper_dev_list); + INIT_LIST_HEAD(&dev->lower_dev_list); dev->priv_flags = IFF_XMIT_DST_RELEASE; setup(dev); -- cgit v1.2.3 From 8b5be8561b804edf6b58fc27edbccf1d45863e08 Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Wed, 28 Aug 2013 23:25:08 +0200 Subject: net: add netdev_for_each_upper_dev_rcu() The new macro netdev_for_each_upper_dev_rcu(dev, upper, iter) iterates through the dev->upper_dev_list starting from the first element, using the netdev_upper_get_next_dev_rcu(dev, &iter). Must be called under RCU read lock. CC: "David S. Miller" CC: Eric Dumazet CC: Jiri Pirko CC: Alexander Duyck CC: Cong Wang Signed-off-by: Veaceslav Falico Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5ccf5b73c378..3ad49b833eab 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2768,6 +2768,16 @@ extern int bpf_jit_enable; extern bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev); extern bool netdev_has_any_upper_dev(struct net_device *dev); +extern struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter); + +/* iterate through upper list, must be called under RCU read lock */ +#define netdev_for_each_upper_dev_rcu(dev, upper, iter) \ + for (iter = &(dev)->upper_dev_list, \ + upper = netdev_upper_get_next_dev_rcu(dev, &(iter)); \ + upper; \ + upper = netdev_upper_get_next_dev_rcu(dev, &(iter))) + extern struct net_device *netdev_master_upper_dev_get(struct net_device *dev); extern struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); extern int netdev_upper_dev_link(struct net_device *dev, -- cgit v1.2.3 From ede23fa8161c1a04aa1b3bf5447812ca14b3fef1 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 26 Aug 2013 22:45:23 -0700 Subject: drivers:net: Convert dma_alloc_coherent(...__GFP_ZERO) to dma_zalloc_coherent __GFP_ZERO is an uncommon flag and perhaps is better not used. static inline dma_zalloc_coherent exists so convert the uses of dma_alloc_coherent with __GFP_ZERO to the more common kernel style with zalloc. Remove memset from the static inline dma_zalloc_coherent and add just one use of __GFP_ZERO instead. Trivially reduces the size of the existing uses of dma_zalloc_coherent. Realign arguments as appropriate. Signed-off-by: Joe Perches Acked-by: Neil Horman Acked-by: Jesse Brandeburg Acked-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/aeroflex/greth.c | 12 ++++----- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 6 ++--- drivers/net/ethernet/broadcom/bnx2.c | 5 ++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 5 ++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 3 +-- drivers/net/ethernet/broadcom/tg3.c | 23 ++++++++--------- drivers/net/ethernet/emulex/benet/be_main.c | 18 +++++++------- drivers/net/ethernet/faraday/ftgmac100.c | 7 +++--- drivers/net/ethernet/faraday/ftmac100.c | 8 +++--- drivers/net/ethernet/ibm/emac/mal.c | 4 +-- drivers/net/ethernet/intel/e1000/e1000_ethtool.c | 8 +++--- drivers/net/ethernet/intel/ixgb/ixgb_main.c | 4 +-- drivers/net/ethernet/marvell/pxa168_eth.c | 19 +++++++------- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 6 ++--- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 16 ++++++------ drivers/net/ethernet/pasemi/pasemi_mac.c | 7 +++--- drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 29 +++++++++++----------- drivers/net/ethernet/sfc/nic.c | 5 ++-- drivers/net/ethernet/sgi/meth.c | 5 ++-- drivers/net/ethernet/tundra/tsi108_eth.c | 8 +++--- drivers/net/ethernet/xilinx/ll_temac_main.c | 12 ++++----- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 14 +++++------ drivers/net/fddi/defxx.c | 6 ++--- drivers/net/irda/ali-ircc.c | 8 +++--- drivers/net/irda/nsc-ircc.c | 8 +++--- drivers/net/irda/smsc-ircc2.c | 8 +++--- drivers/net/irda/via-ircc.c | 8 +++--- drivers/net/irda/w83977af_ir.c | 8 +++--- drivers/net/wireless/b43/dma.c | 6 ++--- drivers/net/wireless/b43legacy/dma.c | 7 +++--- include/linux/dma-mapping.h | 5 ++-- 31 files changed, 135 insertions(+), 153 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 7ff4b30d55ea..e06694555144 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1464,18 +1464,18 @@ static int greth_of_probe(struct platform_device *ofdev) } /* Allocate TX descriptor ring in coherent memory */ - greth->tx_bd_base = dma_alloc_coherent(greth->dev, 1024, - &greth->tx_bd_base_phys, - GFP_KERNEL | __GFP_ZERO); + greth->tx_bd_base = dma_zalloc_coherent(greth->dev, 1024, + &greth->tx_bd_base_phys, + GFP_KERNEL); if (!greth->tx_bd_base) { err = -ENOMEM; goto error3; } /* Allocate RX descriptor ring in coherent memory */ - greth->rx_bd_base = dma_alloc_coherent(greth->dev, 1024, - &greth->rx_bd_base_phys, - GFP_KERNEL | __GFP_ZERO); + greth->rx_bd_base = dma_zalloc_coherent(greth->dev, 1024, + &greth->rx_bd_base_phys, + GFP_KERNEL); if (!greth->rx_bd_base) { err = -ENOMEM; goto error4; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 190219e02400..98b68703c53d 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -948,8 +948,7 @@ static int bcm_enet_open(struct net_device *dev) /* allocate rx dma ring */ size = priv->rx_ring_size * sizeof(struct bcm_enet_desc); - p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, - GFP_KERNEL | __GFP_ZERO); + p = dma_zalloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL); if (!p) { ret = -ENOMEM; goto out_freeirq_tx; @@ -960,8 +959,7 @@ static int bcm_enet_open(struct net_device *dev) /* allocate tx dma ring */ size = priv->tx_ring_size * sizeof(struct bcm_enet_desc); - p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, - GFP_KERNEL | __GFP_ZERO); + p = dma_zalloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL); if (!p) { ret = -ENOMEM; goto out_free_rx_ring; diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 4148058cef81..e838a3f74b69 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -853,9 +853,8 @@ bnx2_alloc_mem(struct bnx2 *bp) bp->status_stats_size = status_blk_size + sizeof(struct statistics_block); - status_blk = dma_alloc_coherent(&bp->pdev->dev, bp->status_stats_size, - &bp->status_blk_mapping, - GFP_KERNEL | __GFP_ZERO); + status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, + &bp->status_blk_mapping, GFP_KERNEL); if (status_blk == NULL) goto alloc_mem_err; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 12202f81735c..3e77a1b1a44a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -2069,9 +2069,8 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, bool is_pf); -#define BNX2X_ILT_ZALLOC(x, y, size) \ - x = dma_alloc_coherent(&bp->pdev->dev, size, y, \ - GFP_KERNEL | __GFP_ZERO) +#define BNX2X_ILT_ZALLOC(x, y, size) \ + x = dma_zalloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL) #define BNX2X_ILT_FREE(x, y, size) \ do { \ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 38be494ffa6e..affb7646241e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -51,8 +51,7 @@ extern int int_mode; #define BNX2X_PCI_ALLOC(x, y, size) \ do { \ - x = dma_alloc_coherent(&bp->pdev->dev, size, y, \ - GFP_KERNEL | __GFP_ZERO); \ + x = dma_zalloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \ if (x == NULL) \ goto alloc_mem_err; \ DP(NETIF_MSG_HW, "BNX2X_PCI_ALLOC: Physical %Lx Virtual %p\n", \ diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 95b8995187d7..2e55ee29cf13 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -8591,10 +8591,10 @@ static int tg3_mem_rx_acquire(struct tg3 *tp) if (!i && tg3_flag(tp, ENABLE_RSS)) continue; - tnapi->rx_rcb = dma_alloc_coherent(&tp->pdev->dev, - TG3_RX_RCB_RING_BYTES(tp), - &tnapi->rx_rcb_mapping, - GFP_KERNEL | __GFP_ZERO); + tnapi->rx_rcb = dma_zalloc_coherent(&tp->pdev->dev, + TG3_RX_RCB_RING_BYTES(tp), + &tnapi->rx_rcb_mapping, + GFP_KERNEL); if (!tnapi->rx_rcb) goto err_out; } @@ -8643,10 +8643,9 @@ static int tg3_alloc_consistent(struct tg3 *tp) { int i; - tp->hw_stats = dma_alloc_coherent(&tp->pdev->dev, - sizeof(struct tg3_hw_stats), - &tp->stats_mapping, - GFP_KERNEL | __GFP_ZERO); + tp->hw_stats = dma_zalloc_coherent(&tp->pdev->dev, + sizeof(struct tg3_hw_stats), + &tp->stats_mapping, GFP_KERNEL); if (!tp->hw_stats) goto err_out; @@ -8654,10 +8653,10 @@ static int tg3_alloc_consistent(struct tg3 *tp) struct tg3_napi *tnapi = &tp->napi[i]; struct tg3_hw_status *sblk; - tnapi->hw_status = dma_alloc_coherent(&tp->pdev->dev, - TG3_HW_STATUS_SIZE, - &tnapi->status_mapping, - GFP_KERNEL | __GFP_ZERO); + tnapi->hw_status = dma_zalloc_coherent(&tp->pdev->dev, + TG3_HW_STATUS_SIZE, + &tnapi->status_mapping, + GFP_KERNEL); if (!tnapi->hw_status) goto err_out; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 50116f8ed576..39e0a7697a81 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -145,8 +145,8 @@ static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q, q->len = len; q->entry_size = entry_size; mem->size = len * entry_size; - mem->va = dma_alloc_coherent(&adapter->pdev->dev, mem->size, &mem->dma, - GFP_KERNEL | __GFP_ZERO); + mem->va = dma_zalloc_coherent(&adapter->pdev->dev, mem->size, &mem->dma, + GFP_KERNEL); if (!mem->va) return -ENOMEM; return 0; @@ -2642,8 +2642,8 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable) memset(mac, 0, ETH_ALEN); cmd.size = sizeof(struct be_cmd_req_acpi_wol_magic_config); - cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma, - GFP_KERNEL | __GFP_ZERO); + cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma, + GFP_KERNEL); if (cmd.va == NULL) return -1; @@ -3946,9 +3946,9 @@ static int be_ctrl_init(struct be_adapter *adapter) memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox)); rx_filter->size = sizeof(struct be_cmd_req_rx_filter); - rx_filter->va = dma_alloc_coherent(&adapter->pdev->dev, rx_filter->size, - &rx_filter->dma, - GFP_KERNEL | __GFP_ZERO); + rx_filter->va = dma_zalloc_coherent(&adapter->pdev->dev, + rx_filter->size, &rx_filter->dma, + GFP_KERNEL); if (rx_filter->va == NULL) { status = -ENOMEM; goto free_mbox; @@ -3994,8 +3994,8 @@ static int be_stats_init(struct be_adapter *adapter) /* BE3 and Skyhawk */ cmd->size = sizeof(struct be_cmd_req_get_stats_v1); - cmd->va = dma_alloc_coherent(&adapter->pdev->dev, cmd->size, &cmd->dma, - GFP_KERNEL | __GFP_ZERO); + cmd->va = dma_zalloc_coherent(&adapter->pdev->dev, cmd->size, &cmd->dma, + GFP_KERNEL); if (cmd->va == NULL) return -1; return 0; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 934e1ae279f0..212f44b3a773 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -778,10 +778,9 @@ static int ftgmac100_alloc_buffers(struct ftgmac100 *priv) { int i; - priv->descs = dma_alloc_coherent(priv->dev, - sizeof(struct ftgmac100_descs), - &priv->descs_dma_addr, - GFP_KERNEL | __GFP_ZERO); + priv->descs = dma_zalloc_coherent(priv->dev, + sizeof(struct ftgmac100_descs), + &priv->descs_dma_addr, GFP_KERNEL); if (!priv->descs) return -ENOMEM; diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 4658f4cc1969..8be5b40c0a12 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -732,10 +732,10 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv) { int i; - priv->descs = dma_alloc_coherent(priv->dev, - sizeof(struct ftmac100_descs), - &priv->descs_dma_addr, - GFP_KERNEL | __GFP_ZERO); + priv->descs = dma_zalloc_coherent(priv->dev, + sizeof(struct ftmac100_descs), + &priv->descs_dma_addr, + GFP_KERNEL); if (!priv->descs) return -ENOMEM; diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 856ea66c9223..dac564c25440 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -637,8 +637,8 @@ static int mal_probe(struct platform_device *ofdev) bd_size = sizeof(struct mal_descriptor) * (NUM_TX_BUFF * mal->num_tx_chans + NUM_RX_BUFF * mal->num_rx_chans); - mal->bd_virt = dma_alloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma, - GFP_KERNEL | __GFP_ZERO); + mal->bd_virt = dma_zalloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma, + GFP_KERNEL); if (mal->bd_virt == NULL) { err = -ENOMEM; goto fail_unmap; diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 82a967c95598..73a8aeefb92a 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -1019,8 +1019,8 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) txdr->size = txdr->count * sizeof(struct e1000_tx_desc); txdr->size = ALIGN(txdr->size, 4096); - txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma, - GFP_KERNEL | __GFP_ZERO); + txdr->desc = dma_zalloc_coherent(&pdev->dev, txdr->size, &txdr->dma, + GFP_KERNEL); if (!txdr->desc) { ret_val = 2; goto err_nomem; @@ -1077,8 +1077,8 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) } rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc); - rxdr->desc = dma_alloc_coherent(&pdev->dev, rxdr->size, &rxdr->dma, - GFP_KERNEL | __GFP_ZERO); + rxdr->desc = dma_zalloc_coherent(&pdev->dev, rxdr->size, &rxdr->dma, + GFP_KERNEL); if (!rxdr->desc) { ret_val = 6; goto err_nomem; diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index fce3e92f9d11..9f6b236828e6 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -718,8 +718,8 @@ ixgb_setup_tx_resources(struct ixgb_adapter *adapter) txdr->size = txdr->count * sizeof(struct ixgb_tx_desc); txdr->size = ALIGN(txdr->size, 4096); - txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma, - GFP_KERNEL | __GFP_ZERO); + txdr->desc = dma_zalloc_coherent(&pdev->dev, txdr->size, &txdr->dma, + GFP_KERNEL); if (!txdr->desc) { vfree(txdr->buffer_info); return -ENOMEM; diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index db481477bcc5..3e69febe377e 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -583,10 +583,9 @@ static int init_hash_table(struct pxa168_eth_private *pep) * table is full. */ if (pep->htpr == NULL) { - pep->htpr = dma_alloc_coherent(pep->dev->dev.parent, - HASH_ADDR_TABLE_SIZE, - &pep->htpr_dma, - GFP_KERNEL | __GFP_ZERO); + pep->htpr = dma_zalloc_coherent(pep->dev->dev.parent, + HASH_ADDR_TABLE_SIZE, + &pep->htpr_dma, GFP_KERNEL); if (pep->htpr == NULL) return -ENOMEM; } else { @@ -1024,9 +1023,9 @@ static int rxq_init(struct net_device *dev) pep->rx_desc_count = 0; size = pep->rx_ring_size * sizeof(struct rx_desc); pep->rx_desc_area_size = size; - pep->p_rx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size, - &pep->rx_desc_dma, - GFP_KERNEL | __GFP_ZERO); + pep->p_rx_desc_area = dma_zalloc_coherent(pep->dev->dev.parent, size, + &pep->rx_desc_dma, + GFP_KERNEL); if (!pep->p_rx_desc_area) goto out; @@ -1085,9 +1084,9 @@ static int txq_init(struct net_device *dev) pep->tx_desc_count = 0; size = pep->tx_ring_size * sizeof(struct tx_desc); pep->tx_desc_area_size = size; - pep->p_tx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size, - &pep->tx_desc_dma, - GFP_KERNEL | __GFP_ZERO); + pep->p_tx_desc_area = dma_zalloc_coherent(pep->dev->dev.parent, size, + &pep->tx_desc_dma, + GFP_KERNEL); if (!pep->p_tx_desc_area) goto out; /* Initialize the next_desc_ptr links in the Tx descriptors ring */ diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 50a1d4a04eb0..149355b52ad0 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3783,9 +3783,9 @@ static int myri10ge_alloc_slices(struct myri10ge_priv *mgp) for (i = 0; i < mgp->num_slices; i++) { ss = &mgp->ss[i]; bytes = mgp->max_intr_slots * sizeof(*ss->rx_done.entry); - ss->rx_done.entry = dma_alloc_coherent(&pdev->dev, bytes, - &ss->rx_done.bus, - GFP_KERNEL | __GFP_ZERO); + ss->rx_done.entry = dma_zalloc_coherent(&pdev->dev, bytes, + &ss->rx_done.bus, + GFP_KERNEL); if (ss->rx_done.entry == NULL) goto abort; bytes = sizeof(*ss->fw_stats); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index e19f1be60d5e..5a0f04c2c813 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -1491,9 +1491,9 @@ pch_gbe_alloc_rx_buffers_pool(struct pch_gbe_adapter *adapter, bufsz = adapter->rx_buffer_len; size = rx_ring->count * bufsz + PCH_GBE_RESERVE_MEMORY; - rx_ring->rx_buff_pool = dma_alloc_coherent(&pdev->dev, size, - &rx_ring->rx_buff_pool_logic, - GFP_KERNEL | __GFP_ZERO); + rx_ring->rx_buff_pool = + dma_zalloc_coherent(&pdev->dev, size, + &rx_ring->rx_buff_pool_logic, GFP_KERNEL); if (!rx_ring->rx_buff_pool) return -ENOMEM; @@ -1807,9 +1807,8 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter, tx_ring->size = tx_ring->count * (int)sizeof(struct pch_gbe_tx_desc); - tx_ring->desc = dma_alloc_coherent(&pdev->dev, tx_ring->size, - &tx_ring->dma, - GFP_KERNEL | __GFP_ZERO); + tx_ring->desc = dma_zalloc_coherent(&pdev->dev, tx_ring->size, + &tx_ring->dma, GFP_KERNEL); if (!tx_ring->desc) { vfree(tx_ring->buffer_info); return -ENOMEM; @@ -1852,9 +1851,8 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter, return -ENOMEM; rx_ring->size = rx_ring->count * (int)sizeof(struct pch_gbe_rx_desc); - rx_ring->desc = dma_alloc_coherent(&pdev->dev, rx_ring->size, - &rx_ring->dma, - GFP_KERNEL | __GFP_ZERO); + rx_ring->desc = dma_zalloc_coherent(&pdev->dev, rx_ring->size, + &rx_ring->dma, GFP_KERNEL); if (!rx_ring->desc) { vfree(rx_ring->buffer_info); return -ENOMEM; diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index f21ae7b6c766..c498181a9aa8 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -440,10 +440,9 @@ static int pasemi_mac_setup_rx_resources(const struct net_device *dev) if (pasemi_dma_alloc_ring(&ring->chan, RX_RING_SIZE)) goto out_ring_desc; - ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev, - RX_RING_SIZE * sizeof(u64), - &ring->buf_dma, - GFP_KERNEL | __GFP_ZERO); + ring->buffers = dma_zalloc_coherent(&mac->dma_pdev->dev, + RX_RING_SIZE * sizeof(u64), + &ring->buf_dma, GFP_KERNEL); if (!ring->buffers) goto out_ring_desc; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index bf3b17eaf8b8..86850dd633a1 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -448,14 +448,14 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter, *(tx_ring->hw_consumer) = 0; rq_size = SIZEOF_HOSTRQ_TX(struct qlcnic_hostrq_tx_ctx); - rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size, - &rq_phys_addr, GFP_KERNEL | __GFP_ZERO); + rq_addr = dma_zalloc_coherent(&adapter->pdev->dev, rq_size, + &rq_phys_addr, GFP_KERNEL); if (!rq_addr) return -ENOMEM; rsp_size = SIZEOF_CARDRSP_TX(struct qlcnic_cardrsp_tx_ctx); - rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size, - &rsp_phys_addr, GFP_KERNEL | __GFP_ZERO); + rsp_addr = dma_zalloc_coherent(&adapter->pdev->dev, rsp_size, + &rsp_phys_addr, GFP_KERNEL); if (!rsp_addr) { err = -ENOMEM; goto out_free_rq; @@ -865,8 +865,8 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args cmd; size_t nic_size = sizeof(struct qlcnic_info_le); - nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size, - &nic_dma_t, GFP_KERNEL | __GFP_ZERO); + nic_info_addr = dma_zalloc_coherent(&adapter->pdev->dev, nic_size, + &nic_dma_t, GFP_KERNEL); if (!nic_info_addr) return -ENOMEM; @@ -919,8 +919,8 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter, if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) return err; - nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size, - &nic_dma_t, GFP_KERNEL | __GFP_ZERO); + nic_info_addr = dma_zalloc_coherent(&adapter->pdev->dev, nic_size, + &nic_dma_t, GFP_KERNEL); if (!nic_info_addr) return -ENOMEM; @@ -972,9 +972,8 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter, size_t npar_size = sizeof(struct qlcnic_pci_info_le); size_t pci_size = npar_size * QLCNIC_MAX_PCI_FUNC; - pci_info_addr = dma_alloc_coherent(&adapter->pdev->dev, pci_size, - &pci_info_dma_t, - GFP_KERNEL | __GFP_ZERO); + pci_info_addr = dma_zalloc_coherent(&adapter->pdev->dev, pci_size, + &pci_info_dma_t, GFP_KERNEL); if (!pci_info_addr) return -ENOMEM; @@ -1074,8 +1073,8 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func, return -EIO; } - stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size, - &stats_dma_t, GFP_KERNEL | __GFP_ZERO); + stats_addr = dma_zalloc_coherent(&adapter->pdev->dev, stats_size, + &stats_dma_t, GFP_KERNEL); if (!stats_addr) return -ENOMEM; @@ -1130,8 +1129,8 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter, if (mac_stats == NULL) return -ENOMEM; - stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size, - &stats_dma_t, GFP_KERNEL | __GFP_ZERO); + stats_addr = dma_zalloc_coherent(&adapter->pdev->dev, stats_size, + &stats_dma_t, GFP_KERNEL); if (!stats_addr) return -ENOMEM; diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index b9b127743a07..78d413312052 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -33,9 +33,8 @@ int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, unsigned int len, gfp_t gfp_flags) { - buffer->addr = dma_alloc_coherent(&efx->pci_dev->dev, len, - &buffer->dma_addr, - gfp_flags | __GFP_ZERO); + buffer->addr = dma_zalloc_coherent(&efx->pci_dev->dev, len, + &buffer->dma_addr, gfp_flags); if (!buffer->addr) return -ENOMEM; buffer->len = len; diff --git a/drivers/net/ethernet/sgi/meth.c b/drivers/net/ethernet/sgi/meth.c index 9f5f35e041ac..770036bc2d87 100644 --- a/drivers/net/ethernet/sgi/meth.c +++ b/drivers/net/ethernet/sgi/meth.c @@ -212,9 +212,8 @@ static void meth_check_link(struct net_device *dev) static int meth_init_tx_ring(struct meth_private *priv) { /* Init TX ring */ - priv->tx_ring = dma_alloc_coherent(NULL, TX_RING_BUFFER_SIZE, - &priv->tx_ring_dma, - GFP_ATOMIC | __GFP_ZERO); + priv->tx_ring = dma_zalloc_coherent(NULL, TX_RING_BUFFER_SIZE, + &priv->tx_ring_dma, GFP_ATOMIC); if (!priv->tx_ring) return -ENOMEM; diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index 01bdc6ca0755..f2e76c9d736c 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -1308,13 +1308,13 @@ static int tsi108_open(struct net_device *dev) data->id, dev->irq, dev->name); } - data->rxring = dma_alloc_coherent(NULL, rxring_size, &data->rxdma, - GFP_KERNEL | __GFP_ZERO); + data->rxring = dma_zalloc_coherent(NULL, rxring_size, &data->rxdma, + GFP_KERNEL); if (!data->rxring) return -ENOMEM; - data->txring = dma_alloc_coherent(NULL, txring_size, &data->txdma, - GFP_KERNEL | __GFP_ZERO); + data->txring = dma_zalloc_coherent(NULL, txring_size, &data->txdma, + GFP_KERNEL); if (!data->txring) { pci_free_consistent(0, rxring_size, data->rxring, data->rxdma); return -ENOMEM; diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 58eb4488beff..b88121f240ca 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -243,15 +243,15 @@ static int temac_dma_bd_init(struct net_device *ndev) /* allocate the tx and rx ring buffer descriptors. */ /* returns a virtual address and a physical address. */ - lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, - sizeof(*lp->tx_bd_v) * TX_BD_NUM, - &lp->tx_bd_p, GFP_KERNEL | __GFP_ZERO); + lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent, + sizeof(*lp->tx_bd_v) * TX_BD_NUM, + &lp->tx_bd_p, GFP_KERNEL); if (!lp->tx_bd_v) goto out; - lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, - sizeof(*lp->rx_bd_v) * RX_BD_NUM, - &lp->rx_bd_p, GFP_KERNEL | __GFP_ZERO); + lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent, + sizeof(*lp->rx_bd_v) * RX_BD_NUM, + &lp->rx_bd_p, GFP_KERNEL); if (!lp->rx_bd_v) goto out; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index fb7d1c28a2ea..b2ff038d6d20 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -201,17 +201,15 @@ static int axienet_dma_bd_init(struct net_device *ndev) /* * Allocate the Tx and Rx buffer descriptors. */ - lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, - sizeof(*lp->tx_bd_v) * TX_BD_NUM, - &lp->tx_bd_p, - GFP_KERNEL | __GFP_ZERO); + lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent, + sizeof(*lp->tx_bd_v) * TX_BD_NUM, + &lp->tx_bd_p, GFP_KERNEL); if (!lp->tx_bd_v) goto out; - lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, - sizeof(*lp->rx_bd_v) * RX_BD_NUM, - &lp->rx_bd_p, - GFP_KERNEL | __GFP_ZERO); + lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent, + sizeof(*lp->rx_bd_v) * RX_BD_NUM, + &lp->rx_bd_p, GFP_KERNEL); if (!lp->rx_bd_v) goto out; diff --git a/drivers/net/fddi/defxx.c b/drivers/net/fddi/defxx.c index 4c8ddc944d51..0b40e1c46f07 100644 --- a/drivers/net/fddi/defxx.c +++ b/drivers/net/fddi/defxx.c @@ -1068,9 +1068,9 @@ static int dfx_driver_init(struct net_device *dev, const char *print_name, #endif sizeof(PI_CONSUMER_BLOCK) + (PI_ALIGN_K_DESC_BLK - 1); - bp->kmalloced = top_v = dma_alloc_coherent(bp->bus_dev, alloc_size, - &bp->kmalloced_dma, - GFP_ATOMIC | __GFP_ZERO); + bp->kmalloced = top_v = dma_zalloc_coherent(bp->bus_dev, alloc_size, + &bp->kmalloced_dma, + GFP_ATOMIC); if (top_v == NULL) return DFX_K_FAILURE; diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 3adb43ce138f..7bbd318bc93e 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -351,16 +351,16 @@ static int ali_ircc_open(int i, chipio_t *info) /* Allocate memory if needed */ self->rx_buff.head = - dma_alloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) { err = -ENOMEM; goto err_out2; } self->tx_buff.head = - dma_alloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) { err = -ENOMEM; goto err_out3; diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 9cf836b57c49..ceeb53737f86 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -430,8 +430,8 @@ static int __init nsc_ircc_open(chipio_t *info) /* Allocate memory if needed */ self->rx_buff.head = - dma_alloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) { err = -ENOMEM; goto out2; @@ -439,8 +439,8 @@ static int __init nsc_ircc_open(chipio_t *info) } self->tx_buff.head = - dma_alloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) { err = -ENOMEM; goto out3; diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index aa05dad75335..0dcdf1592f6b 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -562,14 +562,14 @@ static int smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; self->rx_buff.head = - dma_alloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) goto err_out2; self->tx_buff.head = - dma_alloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) goto err_out3; diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 2dcc60fb37f1..9abaec27f962 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -361,16 +361,16 @@ static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, unsigned int id) /* Allocate memory if needed */ self->rx_buff.head = - dma_alloc_coherent(&pdev->dev, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(&pdev->dev, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) { err = -ENOMEM; goto err_out2; } self->tx_buff.head = - dma_alloc_coherent(&pdev->dev, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(&pdev->dev, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) { err = -ENOMEM; goto err_out3; diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index bb8857a158a6..e641bb240362 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -215,16 +215,16 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, /* Allocate memory if needed */ self->rx_buff.head = - dma_alloc_coherent(NULL, self->rx_buff.truesize, - &self->rx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) { err = -ENOMEM; goto err_out1; } self->tx_buff.head = - dma_alloc_coherent(NULL, self->tx_buff.truesize, - &self->tx_buff_dma, GFP_KERNEL | __GFP_ZERO); + dma_zalloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) { err = -ENOMEM; goto err_out2; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index f7c70b3a6ea9..c51d2dc489e4 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -431,9 +431,9 @@ static int alloc_ringmemory(struct b43_dmaring *ring) u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; - ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, - ring_mem_size, &(ring->dmabase), - GFP_KERNEL | __GFP_ZERO); + ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, + ring_mem_size, &(ring->dmabase), + GFP_KERNEL); if (!ring->descbase) return -ENOMEM; diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index faeafe219c57..42eb26c99e11 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -331,10 +331,9 @@ void free_descriptor_buffer(struct b43legacy_dmaring *ring, static int alloc_ringmemory(struct b43legacy_dmaring *ring) { /* GFP flags must match the flags in free_ringmemory()! */ - ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, - B43legacy_DMA_RINGMEMSIZE, - &(ring->dmabase), - GFP_KERNEL | __GFP_ZERO); + ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, + B43legacy_DMA_RINGMEMSIZE, + &(ring->dmabase), GFP_KERNEL); if (!ring->descbase) return -ENOMEM; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 94af41858513..3a8d0a2af607 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -132,9 +132,8 @@ static inline int dma_set_seg_boundary(struct device *dev, unsigned long mask) static inline void *dma_zalloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) { - void *ret = dma_alloc_coherent(dev, size, dma_handle, flag); - if (ret) - memset(ret, 0, size); + void *ret = dma_alloc_coherent(dev, size, dma_handle, + flag | __GFP_ZERO); return ret; } -- cgit v1.2.3 From e2a240c7d3bcebf90936cc7c22c2729b3a4cec1f Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 28 Aug 2013 18:55:39 +0800 Subject: driver:net:stmmac: Disable DMA store and forward mode if platform data force_thresh_dma_mode is set. Some synopsys ip implementation doesn't support DMA store and forward mode, such as BF60x. So, set force_thresh_dma_mode to use DMA thresholds only. Update document and devicetree as well. Signed-off-by: Sonic Zhang Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 5 +++++ Documentation/networking/stmmac.txt | 3 +++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +++- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 5 +++++ include/linux/stmmac.h | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 261c563b5f06..eba0e5e59ebe 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -22,6 +22,11 @@ Required properties: - snps,pbl Programmable Burst Length - snps,fixed-burst Program the DMA to use the fixed burst mode - snps,mixed-burst Program the DMA to use the mixed burst mode +- snps,force_thresh_dma_mode Force DMA to use the threshold mode for + both tx and rx +- snps,force_sf_dma_mode Force DMA to use the Store and Forward + mode for both tx and rx. This flag is + ignored if force_thresh_dma_mode is set. Optional properties: - mac-address: 6 bytes, mac address diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index 654d2e55c8cb..457b8bbafb08 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -123,6 +123,7 @@ struct plat_stmmacenet_data { int bugged_jumbo; int pmt; int force_sf_dma_mode; + int force_thresh_dma_mode; int riwt_off; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); @@ -159,6 +160,8 @@ Where: o pmt: core has the embedded power module (optional). o force_sf_dma_mode: force DMA to use the Store and Forward mode instead of the Threshold. + o force_thresh_dma_mode: force DMA to use the Shreshold mode other than + the Store and Forward mode. o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode. o fix_mac_speed: this callback is used for modifying some syscfg registers (on ST SoCs) according to the link speed negotiated by the diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index be406911fd01..8d4ccd35a016 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1224,7 +1224,9 @@ static void free_dma_desc_resources(struct stmmac_priv *priv) */ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) { - if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { + if (priv->plat->force_thresh_dma_mode) + priv->hw->dma->dma_mode(priv->ioaddr, tc, tc); + else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { /* * In case of GMAC, SF mode can be enabled * to perform the TX COE in HW. This depends on: diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index da8be6e63096..74a89a4aef88 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -79,6 +79,11 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); + plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode"); + if (plat->force_thresh_dma_mode) { + plat->force_sf_dma_mode = 0; + pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set."); + } return 0; } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 9e495d31516e..bb5deb0feb6b 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -108,6 +108,7 @@ struct plat_stmmacenet_data { int bugged_jumbo; int pmt; int force_sf_dma_mode; + int force_thresh_dma_mode; int riwt_off; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); -- cgit v1.2.3 From 83a093b486ecfd25e7b04b82f6a388f24bd274b2 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 30 Aug 2013 18:08:44 +0200 Subject: net: etherdevice: add address inherit helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some etherdevices inherit their address from a parent or master device. The addr_assign_type should be updated along with the address in these cases. Adding a helper function to simplify this. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include/linux') diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index c623861964e4..d8b512496e50 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -198,6 +198,21 @@ static inline void eth_hw_addr_random(struct net_device *dev) eth_random_addr(dev->dev_addr); } +/** + * eth_hw_addr_inherit - Copy dev_addr from another net_device + * @dst: pointer to net_device to copy dev_addr to + * @src: pointer to net_device to copy dev_addr from + * + * Copy the Ethernet address from one net_device to another along with + * the address attributes (addr_assign_type). + */ +static inline void eth_hw_addr_inherit(struct net_device *dst, + struct net_device *src) +{ + dst->addr_assign_type = src->addr_assign_type; + memcpy(dst->dev_addr, src->dev_addr, ETH_ALEN); +} + /** * compare_ether_addr - Compare two Ethernet addresses * @addr1: Pointer to a six-byte array containing the Ethernet address -- cgit v1.2.3 From bc6fc9fa0e1686d8a06aaa14005d3dbf7978d81f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 30 Aug 2013 15:36:14 +0100 Subject: net: fix comment typo for __skb_alloc_pages() The name of the function in the comment is __skb_alloc_page() while we are actually commenting __skb_alloc_pages(). Fix this typo and make it a valid kernel doc comment. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5ac96f31d546..6f1330af1ebb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1905,8 +1905,8 @@ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC); } -/* - * __skb_alloc_page - allocate pages for ps-rx on a skb and preserve pfmemalloc data +/** + * __skb_alloc_pages - allocate pages for ps-rx on a skb and preserve pfmemalloc data * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used * @order: size of the allocation -- cgit v1.2.3 From 8b27f27797cac5ed9b2f3e63dac89a7ae70e70a7 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Mon, 2 Sep 2013 15:34:56 +0200 Subject: skb: allow skb_scrub_packet() to be used by tunnels This function was only used when a packet was sent to another netns. Now, it can also be used after tunnel encapsulation or decapsulation. Only skb_orphan() should not be done when a packet is not crossing netns. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 +- net/core/dev.c | 2 +- net/core/skbuff.c | 19 ++++++++++++------- net/ipv4/ip_tunnel.c | 4 ++-- net/ipv6/ip6_tunnel.c | 4 ++-- net/ipv6/sit.c | 4 ++-- 6 files changed, 20 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 6f1330af1ebb..2ddb48d9312c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2392,7 +2392,7 @@ extern void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); -extern void skb_scrub_packet(struct sk_buff *skb); +extern void skb_scrub_packet(struct sk_buff *skb, bool xnet); extern struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); diff --git a/net/core/dev.c b/net/core/dev.c index 6fbb0c90849b..07684e880a5d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1697,7 +1697,7 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) * call skb_scrub_packet() after it to clear pkt_type _after_ calling * eth_type_trans(). */ - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); return netif_rx(skb); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2c3d0f53d198..d81cff119f73 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3500,17 +3500,22 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, EXPORT_SYMBOL(skb_try_coalesce); /** - * skb_scrub_packet - scrub an skb before sending it to another netns + * skb_scrub_packet - scrub an skb * * @skb: buffer to clean - * - * skb_scrub_packet can be used to clean an skb before injecting it in - * another namespace. We have to clear all information in the skb that - * could impact namespace isolation. + * @xnet: packet is crossing netns + * + * skb_scrub_packet can be used after encapsulating or decapsulting a packet + * into/from a tunnel. Some information have to be cleared during these + * operations. + * skb_scrub_packet can also be used to clean a skb before injecting it in + * another namespace (@xnet == true). We have to clear all information in the + * skb that could impact namespace isolation. */ -void skb_scrub_packet(struct sk_buff *skb) +void skb_scrub_packet(struct sk_buff *skb, bool xnet) { - skb_orphan(skb); + if (xnet) + skb_orphan(skb); skb->tstamp.tv64 = 0; skb->pkt_type = PACKET_HOST; skb->skb_iif = 0; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 0a6cf0e69478..b0e74e17088f 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -462,7 +462,7 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, } if (!net_eq(tunnel->net, dev_net(tunnel->dev))) - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); gro_cells_receive(&tunnel->gro_cells, skb); return 0; @@ -615,7 +615,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, } if (!net_eq(tunnel->net, dev_net(dev))) - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); if (tunnel->err_count > 0) { if (time_before(jiffies, diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index d6e00a39274c..72372ac90159 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -830,7 +830,7 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, tstats->rx_bytes += skb->len; if (!net_eq(t->net, dev_net(t->dev))) - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); netif_rx(skb); @@ -1002,7 +1002,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, } if (!net_eq(t->net, dev_net(dev))) - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); /* * Okay, now see if we can stuff it in the buffer as-is. diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 1d1458a3b7c4..b2e44f478e14 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -622,7 +622,7 @@ static int ipip6_rcv(struct sk_buff *skb) tstats->rx_bytes += skb->len; if (!net_eq(tunnel->net, dev_net(tunnel->dev))) - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); netif_rx(skb); return 0; @@ -861,7 +861,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, } if (!net_eq(tunnel->net, dev_net(dev))) - skb_scrub_packet(skb); + skb_scrub_packet(skb, true); /* * Okay, now see if we can stuff it in the buffer as-is. -- cgit v1.2.3 From 53cf527513eed6e7170e9dceacd198f9267171b0 Mon Sep 17 00:00:00 2001 From: Joseph Gasparakis Date: Wed, 4 Sep 2013 02:13:38 -0700 Subject: vxlan: Notify drivers for listening UDP port changes This patch adds two more ndo ops: ndo_add_rx_vxlan_port() and ndo_del_rx_vxlan_port(). Drivers can get notifications through the above functions about changes of the UDP listening port of VXLAN. Also, when physical ports come up, now they can call vxlan_get_rx_port() in order to obtain the port number(s) of the existing VXLAN interface in case they already up before them. This information about the listening UDP port would be used for VXLAN related offloads. A big thank you to John Fastabend (john.r.fastabend@intel.com) for his input and his suggestions on this patch set. CC: John Fastabend CC: Stephen Hemminger Signed-off-by: Joseph Gasparakis Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++- include/linux/netdevice.h | 19 +++++++++++++ include/net/vxlan.h | 1 + 3 files changed, 87 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index bd35d2dfc50a..bf64b4191dcc 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -558,6 +558,40 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, return 1; } +/* Notify netdevs that UDP port started listening */ +static void vxlan_notify_add_rx_port(struct sock *sk) +{ + struct net_device *dev; + struct net *net = sock_net(sk); + sa_family_t sa_family = sk->sk_family; + u16 port = htons(inet_sk(sk)->inet_sport); + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->netdev_ops->ndo_add_vxlan_port) + dev->netdev_ops->ndo_add_vxlan_port(dev, sa_family, + port); + } + rcu_read_unlock(); +} + +/* Notify netdevs that UDP port is no more listening */ +static void vxlan_notify_del_rx_port(struct sock *sk) +{ + struct net_device *dev; + struct net *net = sock_net(sk); + sa_family_t sa_family = sk->sk_family; + u16 port = htons(inet_sk(sk)->inet_sport); + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->netdev_ops->ndo_del_vxlan_port) + dev->netdev_ops->ndo_del_vxlan_port(dev, sa_family, + port); + } + rcu_read_unlock(); +} + /* Add new entry to forwarding table -- assumes lock held */ static int vxlan_fdb_create(struct vxlan_dev *vxlan, const u8 *mac, union vxlan_addr *ip, @@ -909,7 +943,9 @@ static void vxlan_sock_hold(struct vxlan_sock *vs) void vxlan_sock_release(struct vxlan_sock *vs) { - struct vxlan_net *vn = net_generic(sock_net(vs->sock->sk), vxlan_net_id); + struct sock *sk = vs->sock->sk; + struct net *net = sock_net(sk); + struct vxlan_net *vn = net_generic(net, vxlan_net_id); if (!atomic_dec_and_test(&vs->refcnt)) return; @@ -918,6 +954,7 @@ void vxlan_sock_release(struct vxlan_sock *vs) hlist_del_rcu(&vs->hlist); smp_wmb(); vs->sock->sk->sk_user_data = NULL; + vxlan_notify_del_rx_port(sk); spin_unlock(&vn->sock_lock); queue_work(vxlan_wq, &vs->del_work); @@ -1983,6 +2020,34 @@ static struct device_type vxlan_type = { .name = "vxlan", }; +/* Calls the ndo_add_vxlan_port of the caller in order to + * supply the listening VXLAN udp ports. + */ +void vxlan_get_rx_port(struct net_device *dev) +{ + struct vxlan_sock *vs; + struct net *net = dev_net(dev); + struct vxlan_net *vn = net_generic(net, vxlan_net_id); + sa_family_t sa_family; + u16 port; + int i; + + if (!dev || !dev->netdev_ops || !dev->netdev_ops->ndo_add_vxlan_port) + return; + + spin_lock(&vn->sock_lock); + for (i = 0; i < PORT_HASH_SIZE; ++i) { + hlist_for_each_entry_rcu(vs, vs_head(net, i), hlist) { + port = htons(inet_sk(vs->sock->sk)->inet_sport); + sa_family = vs->sock->sk->sk_family; + dev->netdev_ops->ndo_add_vxlan_port(dev, sa_family, + port); + } + } + spin_unlock(&vn->sock_lock); +} +EXPORT_SYMBOL_GPL(vxlan_get_rx_port); + /* Initialize the device structure. */ static void vxlan_setup(struct net_device *dev) { @@ -2244,6 +2309,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, spin_lock(&vn->sock_lock); hlist_add_head_rcu(&vs->hlist, vs_head(net, port)); + vxlan_notify_add_rx_port(sk); spin_unlock(&vn->sock_lock); /* Mark socket as an encapsulation socket. */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3ad49b833eab..8ed4ae943053 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -948,6 +948,19 @@ struct netdev_phys_port_id { * Called to get ID of physical port of this device. If driver does * not implement this, it is assumed that the hw is not able to have * multiple net devices on single physical port. + * + * void (*ndo_add_vxlan_port)(struct net_device *dev, + * sa_family_t sa_family, __u16 port); + * Called by vxlan to notiy a driver about the UDP port and socket + * address family that vxlan is listnening to. It is called only when + * a new port starts listening. The operation is protected by the + * vxlan_net->sock_lock. + * + * void (*ndo_del_vxlan_port)(struct net_device *dev, + * sa_family_t sa_family, __u16 port); + * Called by vxlan to notify the driver about a UDP port and socket + * address family that vxlan is not listening to anymore. The operation + * is protected by the vxlan_net->sock_lock. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1078,6 +1091,12 @@ struct net_device_ops { bool new_carrier); int (*ndo_get_phys_port_id)(struct net_device *dev, struct netdev_phys_port_id *ppid); + void (*ndo_add_vxlan_port)(struct net_device *dev, + sa_family_t sa_family, + __u16 port); + void (*ndo_del_vxlan_port)(struct net_device *dev, + sa_family_t sa_family, + __u16 port); }; /* diff --git a/include/net/vxlan.h b/include/net/vxlan.h index e09c40b68027..2d64d3cd4999 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -36,4 +36,5 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb); +void vxlan_get_rx_port(struct net_device *netdev); #endif -- cgit v1.2.3