mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
Merge branch 'netpoll-move-out-netconsole-specific-functions'
Breno Leitao says: ==================== netpoll: move out netconsole-specific functions netpoll and netconsole were created together and their code has been intermixed in net/core/netpoll.c for decades. The result is that netpoll exposes two send-side interfaces: * a generic "give me an sk_buff" path used by every stacked-device driver (bonding, team, vlan, bridge, macvlan, dsa), * a second path that takes raw bytes and builds a UDP/IP/Ethernet packet -- exclusively for netconsole. The packet builder, an skb pool allocator, and several netconsole-specific helpers all live next to the generic plumbing even though no other consumer ever touches them. Worse, every netpoll user pays for that overlap: struct netpoll carries an skb_pool and a refill work_struct that only netconsole's find_skb() ever reads from, and net-core has to review unrelated changes (TTL, hop limit, IP ID generation, source MAC selection, pool sizing) just because they happen to be coded inside netpoll. This is a waste of memory for something useless. This series splits the netconsole-specific code out: * netpoll_send_udp() and its private helpers (push_ipv6, push_ipv4, push_eth, push_udp, netpoll_udp_checksum, find_skb) move into drivers/net/netconsole.c, leaving netpoll with a single skb-only send interface that is the same for every user. The moves are one function per patch for reviewability; helpers are temporarily EXPORT_SYMBOL_GPL'd while netpoll_send_udp() is still in netpoll calling them, then those exports are dropped together once netpoll_send_udp() itself moves. The only new permanent export is zap_completion_queue(), needed because find_skb() still drains the per-CPU TX completion queue before allocating. struct netpoll is unchanged in this series; making the pool itself netconsole-private (and reclaiming the skb_pool / refill_wq fields for the rest of netpoll's users) is the natural follow-up, once this patchset lands. ==================== Link: https://patch.msgid.link/20260512-netconsole_split-v2-0-1191d14ad66d@debian.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
6bbc313bcb
|
|
@ -32,8 +32,13 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/netpoll.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include <linux/configfs.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/hex.h>
|
||||
|
|
@ -45,6 +50,7 @@
|
|||
MODULE_AUTHOR("Matt Mackall <mpm@selenic.com>");
|
||||
MODULE_DESCRIPTION("Console driver for network interfaces");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("NETDEV_INTERNAL");
|
||||
|
||||
#define MAX_PARAM_LENGTH 256
|
||||
#define MAX_EXTRADATA_ENTRY_LEN 256
|
||||
|
|
@ -1648,6 +1654,175 @@ static struct notifier_block netconsole_netdev_notifier = {
|
|||
.notifier_call = netconsole_netdev_event,
|
||||
};
|
||||
|
||||
static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve)
|
||||
{
|
||||
int count = 0;
|
||||
struct sk_buff *skb;
|
||||
|
||||
netpoll_zap_completion_queue();
|
||||
repeat:
|
||||
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
skb = skb_dequeue(&np->skb_pool);
|
||||
schedule_work(&np->refill_wq);
|
||||
}
|
||||
|
||||
if (!skb) {
|
||||
if (++count < 10) {
|
||||
netpoll_poll_dev(np->dev);
|
||||
goto repeat;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
refcount_set(&skb->users, 1);
|
||||
skb_reserve(skb, reserve);
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb,
|
||||
int len)
|
||||
{
|
||||
struct udphdr *udph;
|
||||
int udp_len;
|
||||
|
||||
udp_len = len + sizeof(struct udphdr);
|
||||
udph = udp_hdr(skb);
|
||||
|
||||
/* check needs to be set, since it will be consumed in csum_partial */
|
||||
udph->check = 0;
|
||||
if (np->ipv6)
|
||||
udph->check = csum_ipv6_magic(&np->local_ip.in6,
|
||||
&np->remote_ip.in6,
|
||||
udp_len, IPPROTO_UDP,
|
||||
csum_partial(udph, udp_len, 0));
|
||||
else
|
||||
udph->check = csum_tcpudp_magic(np->local_ip.ip,
|
||||
np->remote_ip.ip,
|
||||
udp_len, IPPROTO_UDP,
|
||||
csum_partial(udph, udp_len, 0));
|
||||
if (udph->check == 0)
|
||||
udph->check = CSUM_MANGLED_0;
|
||||
}
|
||||
|
||||
static void push_udp(struct netpoll *np, struct sk_buff *skb, int len)
|
||||
{
|
||||
struct udphdr *udph;
|
||||
int udp_len;
|
||||
|
||||
udp_len = len + sizeof(struct udphdr);
|
||||
|
||||
skb_push(skb, sizeof(struct udphdr));
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
udph = udp_hdr(skb);
|
||||
udph->source = htons(np->local_port);
|
||||
udph->dest = htons(np->remote_port);
|
||||
udph->len = htons(udp_len);
|
||||
|
||||
netpoll_udp_checksum(np, skb, len);
|
||||
}
|
||||
|
||||
static void push_eth(struct netpoll *np, struct sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
|
||||
eth = skb_push(skb, ETH_HLEN);
|
||||
skb_reset_mac_header(skb);
|
||||
ether_addr_copy(eth->h_source, np->dev->dev_addr);
|
||||
ether_addr_copy(eth->h_dest, np->remote_mac);
|
||||
if (np->ipv6)
|
||||
eth->h_proto = htons(ETH_P_IPV6);
|
||||
else
|
||||
eth->h_proto = htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
static void push_ipv4(struct netpoll *np, struct sk_buff *skb, int len)
|
||||
{
|
||||
static atomic_t ip_ident;
|
||||
struct iphdr *iph;
|
||||
int ip_len;
|
||||
|
||||
ip_len = len + sizeof(struct udphdr) + sizeof(struct iphdr);
|
||||
|
||||
skb_push(skb, sizeof(struct iphdr));
|
||||
skb_reset_network_header(skb);
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
/* iph->version = 4; iph->ihl = 5; */
|
||||
*(unsigned char *)iph = 0x45;
|
||||
iph->tos = 0;
|
||||
put_unaligned(htons(ip_len), &iph->tot_len);
|
||||
iph->id = htons(atomic_inc_return(&ip_ident));
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = 64;
|
||||
iph->protocol = IPPROTO_UDP;
|
||||
iph->check = 0;
|
||||
put_unaligned(np->local_ip.ip, &iph->saddr);
|
||||
put_unaligned(np->remote_ip.ip, &iph->daddr);
|
||||
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len)
|
||||
{
|
||||
struct ipv6hdr *ip6h;
|
||||
|
||||
skb_push(skb, sizeof(struct ipv6hdr));
|
||||
skb_reset_network_header(skb);
|
||||
ip6h = ipv6_hdr(skb);
|
||||
|
||||
/* ip6h->version = 6; ip6h->priority = 0; */
|
||||
*(unsigned char *)ip6h = 0x60;
|
||||
ip6h->flow_lbl[0] = 0;
|
||||
ip6h->flow_lbl[1] = 0;
|
||||
ip6h->flow_lbl[2] = 0;
|
||||
|
||||
ip6h->payload_len = htons(sizeof(struct udphdr) + len);
|
||||
ip6h->nexthdr = IPPROTO_UDP;
|
||||
ip6h->hop_limit = 32;
|
||||
ip6h->saddr = np->local_ip.in6;
|
||||
ip6h->daddr = np->remote_ip.in6;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
}
|
||||
|
||||
static int netpoll_send_udp(struct netpoll *np, const char *msg, int len)
|
||||
{
|
||||
int total_len, ip_len, udp_len;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
|
||||
WARN_ON_ONCE(!irqs_disabled());
|
||||
|
||||
udp_len = len + sizeof(struct udphdr);
|
||||
if (np->ipv6)
|
||||
ip_len = udp_len + sizeof(struct ipv6hdr);
|
||||
else
|
||||
ip_len = udp_len + sizeof(struct iphdr);
|
||||
|
||||
total_len = ip_len + LL_RESERVED_SPACE(np->dev);
|
||||
|
||||
skb = find_skb(np, total_len + np->dev->needed_tailroom,
|
||||
total_len - len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_copy_to_linear_data(skb, msg, len);
|
||||
skb_put(skb, len);
|
||||
|
||||
push_udp(np, skb, len);
|
||||
if (np->ipv6)
|
||||
push_ipv6(np, skb, len);
|
||||
else
|
||||
push_ipv4(np, skb, len);
|
||||
push_eth(np, skb);
|
||||
skb->dev = np->dev;
|
||||
|
||||
return (int)netpoll_send_skb(np, skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* send_udp - Wrapper for netpoll_send_udp that counts errors
|
||||
* @nt: target to send message to
|
||||
|
|
|
|||
|
|
@ -67,13 +67,13 @@ static inline void netpoll_poll_disable(struct net_device *dev) { return; }
|
|||
static inline void netpoll_poll_enable(struct net_device *dev) { return; }
|
||||
#endif
|
||||
|
||||
int netpoll_send_udp(struct netpoll *np, const char *msg, int len);
|
||||
int __netpoll_setup(struct netpoll *np, struct net_device *ndev);
|
||||
int netpoll_setup(struct netpoll *np);
|
||||
void __netpoll_free(struct netpoll *np);
|
||||
void netpoll_cleanup(struct netpoll *np);
|
||||
void do_netpoll_cleanup(struct netpoll *np);
|
||||
netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb);
|
||||
void netpoll_zap_completion_queue(void);
|
||||
|
||||
#ifdef CONFIG_NETPOLL
|
||||
static inline void *netpoll_poll_lock(struct napi_struct *napi)
|
||||
|
|
|
|||
|
|
@ -29,12 +29,10 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/udp.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/ndisc.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <trace/events/napi.h>
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
|
|
@ -53,8 +51,6 @@
|
|||
sizeof(struct udphdr) + \
|
||||
MAX_UDP_CHUNK)
|
||||
|
||||
static void zap_completion_queue(void);
|
||||
|
||||
static unsigned int carrier_timeout = 4;
|
||||
module_param(carrier_timeout, uint, 0644);
|
||||
|
||||
|
|
@ -201,7 +197,7 @@ void netpoll_poll_dev(struct net_device *dev)
|
|||
|
||||
up(&ni->dev_lock);
|
||||
|
||||
zap_completion_queue();
|
||||
netpoll_zap_completion_queue();
|
||||
}
|
||||
EXPORT_SYMBOL(netpoll_poll_dev);
|
||||
|
||||
|
|
@ -240,7 +236,7 @@ static void refill_skbs(struct netpoll *np)
|
|||
}
|
||||
}
|
||||
|
||||
static void zap_completion_queue(void)
|
||||
void netpoll_zap_completion_queue(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct softnet_data *sd = &get_cpu_var(softnet_data);
|
||||
|
|
@ -267,33 +263,7 @@ static void zap_completion_queue(void)
|
|||
|
||||
put_cpu_var(softnet_data);
|
||||
}
|
||||
|
||||
static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve)
|
||||
{
|
||||
int count = 0;
|
||||
struct sk_buff *skb;
|
||||
|
||||
zap_completion_queue();
|
||||
repeat:
|
||||
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
skb = skb_dequeue(&np->skb_pool);
|
||||
schedule_work(&np->refill_wq);
|
||||
}
|
||||
|
||||
if (!skb) {
|
||||
if (++count < 10) {
|
||||
netpoll_poll_dev(np->dev);
|
||||
goto repeat;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
refcount_set(&skb->users, 1);
|
||||
skb_reserve(skb, reserve);
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(netpoll_zap_completion_queue, "NETDEV_INTERNAL");
|
||||
|
||||
static int netpoll_owner_active(struct net_device *dev)
|
||||
{
|
||||
|
|
@ -369,31 +339,6 @@ static netdev_tx_t __netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb,
|
||||
int len)
|
||||
{
|
||||
struct udphdr *udph;
|
||||
int udp_len;
|
||||
|
||||
udp_len = len + sizeof(struct udphdr);
|
||||
udph = udp_hdr(skb);
|
||||
|
||||
/* check needs to be set, since it will be consumed in csum_partial */
|
||||
udph->check = 0;
|
||||
if (np->ipv6)
|
||||
udph->check = csum_ipv6_magic(&np->local_ip.in6,
|
||||
&np->remote_ip.in6,
|
||||
udp_len, IPPROTO_UDP,
|
||||
csum_partial(udph, udp_len, 0));
|
||||
else
|
||||
udph->check = csum_tcpudp_magic(np->local_ip.ip,
|
||||
np->remote_ip.ip,
|
||||
udp_len, IPPROTO_UDP,
|
||||
csum_partial(udph, udp_len, 0));
|
||||
if (udph->check == 0)
|
||||
udph->check = CSUM_MANGLED_0;
|
||||
}
|
||||
|
||||
netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
|
@ -411,125 +356,6 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
|
|||
}
|
||||
EXPORT_SYMBOL(netpoll_send_skb);
|
||||
|
||||
static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len)
|
||||
{
|
||||
struct ipv6hdr *ip6h;
|
||||
|
||||
skb_push(skb, sizeof(struct ipv6hdr));
|
||||
skb_reset_network_header(skb);
|
||||
ip6h = ipv6_hdr(skb);
|
||||
|
||||
/* ip6h->version = 6; ip6h->priority = 0; */
|
||||
*(unsigned char *)ip6h = 0x60;
|
||||
ip6h->flow_lbl[0] = 0;
|
||||
ip6h->flow_lbl[1] = 0;
|
||||
ip6h->flow_lbl[2] = 0;
|
||||
|
||||
ip6h->payload_len = htons(sizeof(struct udphdr) + len);
|
||||
ip6h->nexthdr = IPPROTO_UDP;
|
||||
ip6h->hop_limit = 32;
|
||||
ip6h->saddr = np->local_ip.in6;
|
||||
ip6h->daddr = np->remote_ip.in6;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
}
|
||||
|
||||
static void push_ipv4(struct netpoll *np, struct sk_buff *skb, int len)
|
||||
{
|
||||
static atomic_t ip_ident;
|
||||
struct iphdr *iph;
|
||||
int ip_len;
|
||||
|
||||
ip_len = len + sizeof(struct udphdr) + sizeof(struct iphdr);
|
||||
|
||||
skb_push(skb, sizeof(struct iphdr));
|
||||
skb_reset_network_header(skb);
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
/* iph->version = 4; iph->ihl = 5; */
|
||||
*(unsigned char *)iph = 0x45;
|
||||
iph->tos = 0;
|
||||
put_unaligned(htons(ip_len), &iph->tot_len);
|
||||
iph->id = htons(atomic_inc_return(&ip_ident));
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = 64;
|
||||
iph->protocol = IPPROTO_UDP;
|
||||
iph->check = 0;
|
||||
put_unaligned(np->local_ip.ip, &iph->saddr);
|
||||
put_unaligned(np->remote_ip.ip, &iph->daddr);
|
||||
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
static void push_udp(struct netpoll *np, struct sk_buff *skb, int len)
|
||||
{
|
||||
struct udphdr *udph;
|
||||
int udp_len;
|
||||
|
||||
udp_len = len + sizeof(struct udphdr);
|
||||
|
||||
skb_push(skb, sizeof(struct udphdr));
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
udph = udp_hdr(skb);
|
||||
udph->source = htons(np->local_port);
|
||||
udph->dest = htons(np->remote_port);
|
||||
udph->len = htons(udp_len);
|
||||
|
||||
netpoll_udp_checksum(np, skb, len);
|
||||
}
|
||||
|
||||
static void push_eth(struct netpoll *np, struct sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
|
||||
eth = skb_push(skb, ETH_HLEN);
|
||||
skb_reset_mac_header(skb);
|
||||
ether_addr_copy(eth->h_source, np->dev->dev_addr);
|
||||
ether_addr_copy(eth->h_dest, np->remote_mac);
|
||||
if (np->ipv6)
|
||||
eth->h_proto = htons(ETH_P_IPV6);
|
||||
else
|
||||
eth->h_proto = htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
int netpoll_send_udp(struct netpoll *np, const char *msg, int len)
|
||||
{
|
||||
int total_len, ip_len, udp_len;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
|
||||
WARN_ON_ONCE(!irqs_disabled());
|
||||
|
||||
udp_len = len + sizeof(struct udphdr);
|
||||
if (np->ipv6)
|
||||
ip_len = udp_len + sizeof(struct ipv6hdr);
|
||||
else
|
||||
ip_len = udp_len + sizeof(struct iphdr);
|
||||
|
||||
total_len = ip_len + LL_RESERVED_SPACE(np->dev);
|
||||
|
||||
skb = find_skb(np, total_len + np->dev->needed_tailroom,
|
||||
total_len - len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_copy_to_linear_data(skb, msg, len);
|
||||
skb_put(skb, len);
|
||||
|
||||
push_udp(np, skb, len);
|
||||
if (np->ipv6)
|
||||
push_ipv6(np, skb, len);
|
||||
else
|
||||
push_ipv4(np, skb, len);
|
||||
push_eth(np, skb);
|
||||
skb->dev = np->dev;
|
||||
|
||||
return (int)netpoll_send_skb(np, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(netpoll_send_udp);
|
||||
|
||||
|
||||
static void skb_pool_flush(struct netpoll *np)
|
||||
{
|
||||
struct sk_buff_head *skb_pool;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user