Commit f8bae99e authored by David S. Miller's avatar David S. Miller

Merge branch 'batman-adv/next' of git://git.open-mesh.org/linux-merge

parents 994635a1 44c4349a
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
/* return true if new_packet can be aggregated with forw_packet */ /* return true if new_packet can be aggregated with forw_packet */
static bool can_aggregate_with(const struct batman_packet *new_batman_packet, static bool can_aggregate_with(const struct batman_packet *new_batman_packet,
struct bat_priv *bat_priv,
int packet_len, int packet_len,
unsigned long send_time, unsigned long send_time,
bool directlink, bool directlink,
...@@ -37,6 +38,8 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet, ...@@ -37,6 +38,8 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet,
struct batman_packet *batman_packet = struct batman_packet *batman_packet =
(struct batman_packet *)forw_packet->skb->data; (struct batman_packet *)forw_packet->skb->data;
int aggregated_bytes = forw_packet->packet_len + packet_len; int aggregated_bytes = forw_packet->packet_len + packet_len;
struct hard_iface *primary_if = NULL;
bool res = false;
/** /**
* we can aggregate the current packet to this aggregated packet * we can aggregate the current packet to this aggregated packet
...@@ -61,6 +64,10 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet, ...@@ -61,6 +64,10 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet,
* packet * packet
*/ */
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
/* packets without direct link flag and high TTL /* packets without direct link flag and high TTL
* are flooded through the net */ * are flooded through the net */
if ((!directlink) && if ((!directlink) &&
...@@ -70,8 +77,10 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet, ...@@ -70,8 +77,10 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet,
/* own packets originating non-primary /* own packets originating non-primary
* interfaces leave only that interface */ * interfaces leave only that interface */
((!forw_packet->own) || ((!forw_packet->own) ||
(forw_packet->if_incoming->if_num == 0))) (forw_packet->if_incoming == primary_if))) {
return true; res = true;
goto out;
}
/* if the incoming packet is sent via this one /* if the incoming packet is sent via this one
* interface only - we still can aggregate */ * interface only - we still can aggregate */
...@@ -84,11 +93,16 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet, ...@@ -84,11 +93,16 @@ static bool can_aggregate_with(const struct batman_packet *new_batman_packet,
* (= secondary interface packets in general) */ * (= secondary interface packets in general) */
(batman_packet->flags & DIRECTLINK || (batman_packet->flags & DIRECTLINK ||
(forw_packet->own && (forw_packet->own &&
forw_packet->if_incoming->if_num != 0))) forw_packet->if_incoming != primary_if))) {
return true; res = true;
goto out;
}
} }
return false; out:
if (primary_if)
hardif_free_ref(primary_if);
return res;
} }
/* create a new aggregated packet and add this packet to it */ /* create a new aggregated packet and add this packet to it */
...@@ -210,6 +224,7 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv, ...@@ -210,6 +224,7 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv,
hlist_for_each_entry(forw_packet_pos, tmp_node, hlist_for_each_entry(forw_packet_pos, tmp_node,
&bat_priv->forw_bat_list, list) { &bat_priv->forw_bat_list, list) {
if (can_aggregate_with(batman_packet, if (can_aggregate_with(batman_packet,
bat_priv,
packet_len, packet_len,
send_time, send_time,
direct_link, direct_link,
......
...@@ -487,10 +487,9 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset) ...@@ -487,10 +487,9 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
} }
seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... " seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... "
"[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
"Gateway", "#", TQ_MAX_VALUE, "Nexthop", "Gateway", "#", TQ_MAX_VALUE, "Nexthop",
"outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR, "outgoingIF", SOURCE_VERSION, primary_if->net_dev->name,
primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name); primary_if->net_dev->dev_addr, net_dev->name);
rcu_read_lock(); rcu_read_lock();
......
...@@ -58,9 +58,8 @@ static int __init batman_init(void) ...@@ -58,9 +58,8 @@ static int __init batman_init(void)
register_netdevice_notifier(&hard_if_notifier); register_netdevice_notifier(&hard_if_notifier);
pr_info("B.A.T.M.A.N. advanced %s%s (compatibility version %i) " pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) "
"loaded\n", SOURCE_VERSION, REVISION_VERSION_STR, "loaded\n", SOURCE_VERSION, COMPAT_VERSION);
COMPAT_VERSION);
return 0; return 0;
} }
...@@ -184,8 +183,4 @@ MODULE_LICENSE("GPL"); ...@@ -184,8 +183,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE); MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE);
#ifdef REVISION_VERSION
MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION);
#else
MODULE_VERSION(SOURCE_VERSION); MODULE_VERSION(SOURCE_VERSION);
#endif
...@@ -27,8 +27,9 @@ ...@@ -27,8 +27,9 @@
#define DRIVER_DESC "B.A.T.M.A.N. advanced" #define DRIVER_DESC "B.A.T.M.A.N. advanced"
#define DRIVER_DEVICE "batman-adv" #define DRIVER_DEVICE "batman-adv"
#define SOURCE_VERSION "next" #ifndef SOURCE_VERSION
#define SOURCE_VERSION "2011.3.0"
#endif
/* B.A.T.M.A.N. parameters */ /* B.A.T.M.A.N. parameters */
...@@ -144,12 +145,6 @@ enum dbg_level { ...@@ -144,12 +145,6 @@ enum dbg_level {
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include "types.h" #include "types.h"
#ifndef REVISION_VERSION
#define REVISION_VERSION_STR ""
#else
#define REVISION_VERSION_STR " "REVISION_VERSION
#endif
extern struct list_head hardif_list; extern struct list_head hardif_list;
extern unsigned char broadcast_addr[]; extern unsigned char broadcast_addr[];
......
...@@ -430,9 +430,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) ...@@ -430,9 +430,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
goto out; goto out;
} }
seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
SOURCE_VERSION, REVISION_VERSION_STR, SOURCE_VERSION, primary_if->net_dev->name,
primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name); primary_if->net_dev->dev_addr, net_dev->name);
seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
"Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
......
...@@ -78,10 +78,13 @@ enum tt_query_flags { ...@@ -78,10 +78,13 @@ enum tt_query_flags {
TT_FULL_TABLE = 1 << 2 TT_FULL_TABLE = 1 << 2
}; };
/* TT_CHANGE flags */ /* TT_CLIENT flags.
enum tt_change_flags { * Flags from 1 to 1 << 7 are sent on the wire, while flags from 1 << 8 to
TT_CHANGE_DEL = 0x01, * 1 << 15 are used for local computation only */
TT_CLIENT_ROAM = 0x02 enum tt_client_flags {
TT_CLIENT_DEL = 1 << 0,
TT_CLIENT_ROAM = 1 << 1,
TT_CLIENT_NOPURGE = 1 << 8
}; };
struct batman_packet { struct batman_packet {
......
...@@ -1677,7 +1677,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) ...@@ -1677,7 +1677,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
spin_unlock_bh(&orig_node->bcast_seqno_lock); spin_unlock_bh(&orig_node->bcast_seqno_lock);
/* rebroadcast packet */ /* rebroadcast packet */
add_bcast_packet_to_list(bat_priv, skb); add_bcast_packet_to_list(bat_priv, skb, 1);
/* broadcast for me */ /* broadcast for me */
interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size);
......
...@@ -163,6 +163,7 @@ static void send_packet(struct forw_packet *forw_packet) ...@@ -163,6 +163,7 @@ static void send_packet(struct forw_packet *forw_packet)
struct hard_iface *hard_iface; struct hard_iface *hard_iface;
struct net_device *soft_iface; struct net_device *soft_iface;
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
struct hard_iface *primary_if = NULL;
struct batman_packet *batman_packet = struct batman_packet *batman_packet =
(struct batman_packet *)(forw_packet->skb->data); (struct batman_packet *)(forw_packet->skb->data);
int directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0); int directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0);
...@@ -170,19 +171,23 @@ static void send_packet(struct forw_packet *forw_packet) ...@@ -170,19 +171,23 @@ static void send_packet(struct forw_packet *forw_packet)
if (!forw_packet->if_incoming) { if (!forw_packet->if_incoming) {
pr_err("Error - can't forward packet: incoming iface not " pr_err("Error - can't forward packet: incoming iface not "
"specified\n"); "specified\n");
return; goto out;
} }
soft_iface = forw_packet->if_incoming->soft_iface; soft_iface = forw_packet->if_incoming->soft_iface;
bat_priv = netdev_priv(soft_iface); bat_priv = netdev_priv(soft_iface);
if (forw_packet->if_incoming->if_status != IF_ACTIVE) if (forw_packet->if_incoming->if_status != IF_ACTIVE)
return; goto out;
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
/* multihomed peer assumed */ /* multihomed peer assumed */
/* non-primary OGMs are only broadcasted on their interface */ /* non-primary OGMs are only broadcasted on their interface */
if ((directlink && (batman_packet->ttl == 1)) || if ((directlink && (batman_packet->ttl == 1)) ||
(forw_packet->own && (forw_packet->if_incoming->if_num > 0))) { (forw_packet->own && (forw_packet->if_incoming != primary_if))) {
/* FIXME: what about aggregated packets ? */ /* FIXME: what about aggregated packets ? */
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
...@@ -199,7 +204,7 @@ static void send_packet(struct forw_packet *forw_packet) ...@@ -199,7 +204,7 @@ static void send_packet(struct forw_packet *forw_packet)
broadcast_addr); broadcast_addr);
forw_packet->skb = NULL; forw_packet->skb = NULL;
return; goto out;
} }
/* broadcast on every interface */ /* broadcast on every interface */
...@@ -211,6 +216,10 @@ static void send_packet(struct forw_packet *forw_packet) ...@@ -211,6 +216,10 @@ static void send_packet(struct forw_packet *forw_packet)
send_packet_to_if(forw_packet, hard_iface); send_packet_to_if(forw_packet, hard_iface);
} }
rcu_read_unlock(); rcu_read_unlock();
out:
if (primary_if)
hardif_free_ref(primary_if);
} }
static void realloc_packet_buffer(struct hard_iface *hard_iface, static void realloc_packet_buffer(struct hard_iface *hard_iface,
...@@ -455,7 +464,7 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv, ...@@ -455,7 +464,7 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv,
* The skb is not consumed, so the caller should make sure that the * The skb is not consumed, so the caller should make sure that the
* skb is freed. */ * skb is freed. */
int add_bcast_packet_to_list(struct bat_priv *bat_priv, int add_bcast_packet_to_list(struct bat_priv *bat_priv,
const struct sk_buff *skb) const struct sk_buff *skb, unsigned long delay)
{ {
struct hard_iface *primary_if = NULL; struct hard_iface *primary_if = NULL;
struct forw_packet *forw_packet; struct forw_packet *forw_packet;
...@@ -492,7 +501,7 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv, ...@@ -492,7 +501,7 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv,
/* how often did we send the bcast packet ? */ /* how often did we send the bcast packet ? */
forw_packet->num_packets = 0; forw_packet->num_packets = 0;
_add_bcast_packet_to_list(bat_priv, forw_packet, 1); _add_bcast_packet_to_list(bat_priv, forw_packet, delay);
return NETDEV_TX_OK; return NETDEV_TX_OK;
packet_free: packet_free:
......
...@@ -31,7 +31,7 @@ void schedule_forward_packet(struct orig_node *orig_node, ...@@ -31,7 +31,7 @@ void schedule_forward_packet(struct orig_node *orig_node,
int directlink, int directlink,
struct hard_iface *if_outgoing); struct hard_iface *if_outgoing);
int add_bcast_packet_to_list(struct bat_priv *bat_priv, int add_bcast_packet_to_list(struct bat_priv *bat_priv,
const struct sk_buff *skb); const struct sk_buff *skb, unsigned long delay);
void send_outstanding_bat_packet(struct work_struct *work); void send_outstanding_bat_packet(struct work_struct *work);
void purge_outstanding_packets(struct bat_priv *bat_priv, void purge_outstanding_packets(struct bat_priv *bat_priv,
const struct hard_iface *hard_iface); const struct hard_iface *hard_iface);
......
...@@ -634,7 +634,7 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) ...@@ -634,7 +634,7 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
bcast_packet->seqno = bcast_packet->seqno =
htonl(atomic_inc_return(&bat_priv->bcast_seqno)); htonl(atomic_inc_return(&bat_priv->bcast_seqno));
add_bcast_packet_to_list(bat_priv, skb); add_bcast_packet_to_list(bat_priv, skb, 1);
/* a copy is stored in the bcast list, therefore removing /* a copy is stored in the bcast list, therefore removing
* the original skb. */ * the original skb. */
......
...@@ -143,8 +143,8 @@ static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry) ...@@ -143,8 +143,8 @@ static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry)
kfree_rcu(tt_global_entry, rcu); kfree_rcu(tt_global_entry, rcu);
} }
static void tt_local_event(struct bat_priv *bat_priv, uint8_t op, static void tt_local_event(struct bat_priv *bat_priv, const uint8_t *addr,
const uint8_t *addr, bool roaming) uint8_t flags)
{ {
struct tt_change_node *tt_change_node; struct tt_change_node *tt_change_node;
...@@ -153,10 +153,7 @@ static void tt_local_event(struct bat_priv *bat_priv, uint8_t op, ...@@ -153,10 +153,7 @@ static void tt_local_event(struct bat_priv *bat_priv, uint8_t op,
if (!tt_change_node) if (!tt_change_node)
return; return;
tt_change_node->change.flags = op; tt_change_node->change.flags = flags;
if (roaming)
tt_change_node->change.flags |= TT_CLIENT_ROAM;
memcpy(tt_change_node->change.addr, addr, ETH_ALEN); memcpy(tt_change_node->change.addr, addr, ETH_ALEN);
spin_lock_bh(&bat_priv->tt_changes_list_lock); spin_lock_bh(&bat_priv->tt_changes_list_lock);
...@@ -203,21 +200,20 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr) ...@@ -203,21 +200,20 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
if (!tt_local_entry) if (!tt_local_entry)
goto out; goto out;
tt_local_event(bat_priv, NO_FLAGS, addr, false);
bat_dbg(DBG_TT, bat_priv, bat_dbg(DBG_TT, bat_priv,
"Creating new local tt entry: %pM (ttvn: %d)\n", addr, "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
(uint8_t)atomic_read(&bat_priv->ttvn)); (uint8_t)atomic_read(&bat_priv->ttvn));
memcpy(tt_local_entry->addr, addr, ETH_ALEN); memcpy(tt_local_entry->addr, addr, ETH_ALEN);
tt_local_entry->last_seen = jiffies; tt_local_entry->last_seen = jiffies;
tt_local_entry->flags = NO_FLAGS;
atomic_set(&tt_local_entry->refcount, 2); atomic_set(&tt_local_entry->refcount, 2);
/* the batman interface mac address should never be purged */ /* the batman interface mac address should never be purged */
if (compare_eth(addr, soft_iface->dev_addr)) if (compare_eth(addr, soft_iface->dev_addr))
tt_local_entry->never_purge = 1; tt_local_entry->flags |= TT_CLIENT_NOPURGE;
else
tt_local_entry->never_purge = 0; tt_local_event(bat_priv, addr, tt_local_entry->flags);
hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig, hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig,
tt_local_entry, &tt_local_entry->hash_entry); tt_local_entry, &tt_local_entry->hash_entry);
...@@ -387,7 +383,9 @@ void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr, ...@@ -387,7 +383,9 @@ void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
if (!tt_local_entry) if (!tt_local_entry)
goto out; goto out;
tt_local_event(bat_priv, TT_CHANGE_DEL, tt_local_entry->addr, roaming); tt_local_event(bat_priv, tt_local_entry->addr,
tt_local_entry->flags | TT_CLIENT_DEL |
(roaming ? TT_CLIENT_ROAM : NO_FLAGS));
tt_local_del(bat_priv, tt_local_entry, message); tt_local_del(bat_priv, tt_local_entry, message);
out: out:
if (tt_local_entry) if (tt_local_entry)
...@@ -410,15 +408,15 @@ static void tt_local_purge(struct bat_priv *bat_priv) ...@@ -410,15 +408,15 @@ static void tt_local_purge(struct bat_priv *bat_priv)
spin_lock_bh(list_lock); spin_lock_bh(list_lock);
hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, hlist_for_each_entry_safe(tt_local_entry, node, node_tmp,
head, hash_entry) { head, hash_entry) {
if (tt_local_entry->never_purge) if (tt_local_entry->flags & TT_CLIENT_NOPURGE)
continue; continue;
if (!is_out_of_time(tt_local_entry->last_seen, if (!is_out_of_time(tt_local_entry->last_seen,
TT_LOCAL_TIMEOUT * 1000)) TT_LOCAL_TIMEOUT * 1000))
continue; continue;
tt_local_event(bat_priv, TT_CHANGE_DEL, tt_local_event(bat_priv, tt_local_entry->addr,
tt_local_entry->addr, false); tt_local_entry->flags | TT_CLIENT_DEL);
atomic_dec(&bat_priv->num_local_tt); atomic_dec(&bat_priv->num_local_tt);
bat_dbg(DBG_TT, bat_priv, "Deleting local " bat_dbg(DBG_TT, bat_priv, "Deleting local "
"tt entry (%pM): timed out\n", "tt entry (%pM): timed out\n",
...@@ -1335,7 +1333,7 @@ static void _tt_update_changes(struct bat_priv *bat_priv, ...@@ -1335,7 +1333,7 @@ static void _tt_update_changes(struct bat_priv *bat_priv,
int i; int i;
for (i = 0; i < tt_num_changes; i++) { for (i = 0; i < tt_num_changes; i++) {
if ((tt_change + i)->flags & TT_CHANGE_DEL) if ((tt_change + i)->flags & TT_CLIENT_DEL)
tt_global_del(bat_priv, orig_node, tt_global_del(bat_priv, orig_node,
(tt_change + i)->addr, (tt_change + i)->addr,
"tt removed by changes", "tt removed by changes",
......
...@@ -30,8 +30,7 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr); ...@@ -30,8 +30,7 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr);
void tt_local_remove(struct bat_priv *bat_priv, void tt_local_remove(struct bat_priv *bat_priv,
const uint8_t *addr, const char *message, bool roaming); const uint8_t *addr, const char *message, bool roaming);
int tt_local_seq_print_text(struct seq_file *seq, void *offset); int tt_local_seq_print_text(struct seq_file *seq, void *offset);
void tt_global_add_orig(struct bat_priv *bat_priv, void tt_global_add_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
struct orig_node *orig_node,
const unsigned char *tt_buff, int tt_buff_len); const unsigned char *tt_buff, int tt_buff_len);
int tt_global_add(struct bat_priv *bat_priv, int tt_global_add(struct bat_priv *bat_priv,
struct orig_node *orig_node, const unsigned char *addr, struct orig_node *orig_node, const unsigned char *addr,
......
...@@ -224,7 +224,7 @@ struct socket_packet { ...@@ -224,7 +224,7 @@ struct socket_packet {
struct tt_local_entry { struct tt_local_entry {
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
unsigned long last_seen; unsigned long last_seen;
char never_purge; uint16_t flags;
atomic_t refcount; atomic_t refcount;
struct rcu_head rcu; struct rcu_head rcu;
struct hlist_node hash_entry; struct hlist_node hash_entry;
...@@ -234,7 +234,7 @@ struct tt_global_entry { ...@@ -234,7 +234,7 @@ struct tt_global_entry {
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
struct orig_node *orig_node; struct orig_node *orig_node;
uint8_t ttvn; uint8_t ttvn;
uint8_t flags; /* only TT_GLOBAL_ROAM is used */ uint16_t flags; /* only TT_GLOBAL_ROAM is used */
unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */ unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */
atomic_t refcount; atomic_t refcount;
struct rcu_head rcu; struct rcu_head rcu;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment