From fa733b77929073b5ec4520da648fd82689ea96cc Mon Sep 17 00:00:00 2001 From: "Hardik S. Panchal" Date: Tue, 8 May 2018 10:16:32 +0530 Subject: [PATCH] net: Add API to update L4 protocol registrant. Change-Id: I0d01fe33a590bb3eec596de621f86537f60c7071 Signed-off-by: Hardik S. Panchal Signed-off-by: Pavithra R --- include/net/protocol.h | 4 ++++ net/ipv4/protocol.c | 26 ++++++++++++++++++++++++++ net/ipv6/protocol.c | 26 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/include/net/protocol.h b/include/net/protocol.h index 6aef8cb11cc8c..d8ca81fb1537e 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -101,12 +101,16 @@ int inet_add_protocol(const struct net_protocol *prot, unsigned char num); int inet_del_protocol(const struct net_protocol *prot, unsigned char num); int inet_add_offload(const struct net_offload *prot, unsigned char num); int inet_del_offload(const struct net_offload *prot, unsigned char num); +int inet_update_protocol(const struct net_protocol *new_prot, + unsigned char num, const struct net_protocol **old_prot); void inet_register_protosw(struct inet_protosw *p); void inet_unregister_protosw(struct inet_protosw *p); #if IS_ENABLED(CONFIG_IPV6) int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num); int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num); +int inet6_update_protocol(const struct inet6_protocol *new_prot, + unsigned char num, const struct inet6_protocol **old_prot); int inet6_register_protosw(struct inet_protosw *p); void inet6_unregister_protosw(struct inet_protosw *p); #endif diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 6913979948d73..ebfbbd9735f08 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -68,3 +68,29 @@ int inet_del_offload(const struct net_offload *prot, unsigned char protocol) return ret; } EXPORT_SYMBOL(inet_del_offload); + +int inet_update_protocol(const struct net_protocol *new_prot, + unsigned char protocol, const struct net_protocol **old_prot) +{ + int ret; + + rcu_read_lock(); + *old_prot = rcu_dereference(inet_protos[protocol]); + if (!*old_prot) { + rcu_read_unlock(); + return -1; + } + rcu_read_unlock(); + + /* + * old_prot is not protected as cmpxchg is successful only if + * old_prot matches with the value in inet_protos[protocol] + */ + ret = (cmpxchg((const struct net_protocol **)&inet_protos[protocol], + *old_prot, new_prot) == *old_prot) ? 0 : -1; + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(inet_update_protocol); diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index d4b1806bab1b4..327f7fc78dfa7 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -44,6 +44,32 @@ int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol return ret; } EXPORT_SYMBOL(inet6_del_protocol); + +int inet6_update_protocol(const struct inet6_protocol *new_prot, + unsigned char protocol, const struct inet6_protocol **old_prot) +{ + int ret; + + rcu_read_lock(); + *old_prot = rcu_dereference(inet6_protos[protocol]); + if (!*old_prot) { + rcu_read_unlock(); + return -1; + } + rcu_read_unlock(); + + /* + * old_prot is not protected as cmpxchg is successful only if + * old_prot matches with the value in inet6_protos[protocol] + */ + ret = (cmpxchg((const struct inet6_protocol **)&inet6_protos[protocol], + *old_prot, new_prot) == *old_prot) ? 0 : -1; + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(inet6_update_protocol); #endif const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS] __read_mostly; -- 2.34.1