Netdev List
 help / color / mirror / Atom feed
* [PATCH net v4] raw: annotate lockless match fields in raw_v4_match()
From: Runyu Xiao @ 2026-07-16 14:29 UTC (permalink / raw)
  To: netdev
  Cc: kuniyu, edumazet, davem, dsahern, kuba, pabeni, horms,
	linux-kernel, jianhao.xu, runyu.xiao

raw_v4_match() is a lockless match helper under sk_for_each_rcu(). It
still reads inet->inet_daddr, inet->inet_rcv_saddr and
sk->sk_bound_dev_if with plain loads while bind, connect and
bind-to-device paths can update the same match fields concurrently.

Annotate only those mutable match fields in raw_v4_match(), and do so
at the point of use instead of hoisting the bound-device read before
the earlier short-circuit tests.

Also annotate the raw bind writer and the shared IPv4 datagram connect
writer used by raw sockets, so the address fields updated on bind and
connect match explicit WRITE_ONCE() updates.

This version intentionally leaves the shared disconnect-side IPv4
writers to follow-up cleanup and limits the writer changes here to the
raw bind path and the datagram connect path directly exercised by raw
sockets.

Fixes: 0daf07e52709 ("raw: convert raw sockets to RCU")
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
v4:
- drop the __udp_disconnect() annotation
- drop the inet_reset_saddr() annotation
- keep writer changes limited to raw_bind() and __ip4_datagram_connect()
- previous version: https://lore.kernel.org/r/20260611035318.1091442-1-runyu.xiao@seu.edu.cn

v3:
- drop the inet_num annotation for raw sockets
- cover inet_daddr as well
- avoid hoisting sk_bound_dev_if into a temporary variable
- annotate the matching IPv4 writer paths

v2:
- note that inet_num and sk_bound_dev_if already have WRITE_ONCE() writers
- add WRITE_ONCE() in raw_bind() for inet_rcv_saddr
- previous version: https://lore.kernel.org/r/20260601073937.1137673-1-runyu.xiao@seu.edu.cn

 net/ipv4/datagram.c |  4 ++--
 net/ipv4/raw.c      | 23 ++++++++++++++++-------
 2 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c
index 1614593b6d72..7d25519a6cdd 100644
--- a/net/ipv4/datagram.c
+++ b/net/ipv4/datagram.c
@@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int
 	}
 
 	/* Update addresses before rehashing */
-	inet->inet_daddr = fl4->daddr;
+	WRITE_ONCE(inet->inet_daddr, fl4->daddr);
 	inet->inet_dport = usin->sin_port;
 	if (!inet->inet_saddr)
 		inet->inet_saddr = fl4->saddr;
 	if (!inet->inet_rcv_saddr) {
-		inet->inet_rcv_saddr = fl4->saddr;
+		WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr);
 		if (sk->sk_prot->rehash)
 			sk->sk_prot->rehash(sk);
 	}
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 5aaf9c62c8e1..dfb294b5c794 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -120,13 +120,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num,
 		  __be32 raddr, __be32 laddr, int dif, int sdif)
 {
 	const struct inet_sock *inet = inet_sk(sk);
+	__be32 daddr, rcv_saddr;
 
-	if (net_eq(sock_net(sk), net) && inet->inet_num == num	&&
-	    !(inet->inet_daddr && inet->inet_daddr != raddr) 	&&
-	    !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&
-	    raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
-		return true;
-	return false;
+	if (!net_eq(sock_net(sk), net) || inet->inet_num != num)
+		return false;
+
+	daddr = READ_ONCE(inet->inet_daddr);
+	if (daddr && daddr != raddr)
+		return false;
+
+	rcv_saddr = READ_ONCE(inet->inet_rcv_saddr);
+	if (rcv_saddr && rcv_saddr != laddr)
+		return false;
+
+	return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if),
+				   dif, sdif);
 }
 EXPORT_SYMBOL_GPL(raw_v4_match);
 
@@ -724,7 +732,8 @@ static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr,
 					 chk_addr_ret))
 		goto out;
 
-	inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
+	inet->inet_saddr = addr->sin_addr.s_addr;
+	WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr);
 	if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
 		inet->inet_saddr = 0;  /* Use device */
 	sk_dst_reset(sk);
-- 
2.34.1

^ permalink raw reply related

* Re: Please apply 736b380e28d0 and eca856950f7c down to 6.1.y
From: Greg Kroah-Hartman @ 2026-07-16 13:35 UTC (permalink / raw)
  To: Salvatore Bonaccorso
  Cc: Wongi Lee, stable, Sasha Levin, netdev, David Ahern, Ido Schimmel,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Jungwoo Lee
In-Reply-To: <aljSrJ_4uwHERDHk@eldamar.lan>

On Thu, Jul 16, 2026 at 02:46:36PM +0200, Salvatore Bonaccorso wrote:
> Hi Greg,
> 
> On Mon, Jun 29, 2026 at 04:13:23PM +0200, Salvatore Bonaccorso wrote:
> > Hi Greg,
> > 
> > On Wed, Jun 24, 2026 at 06:44:00PM +0900, Wongi Lee wrote:
> > > On Wed, Jun 24, 2026 at 11:37:29AM +0200, Greg Kroah-Hartman wrote:
> > > > On Wed, Jun 24, 2026 at 06:30:03PM +0900, Wongi Lee wrote:
> > > > > On Wed, Jun 24, 2026 at 11:00:45AM +0200, Greg Kroah-Hartman wrote:
> > > > > > On Wed, Jun 24, 2026 at 05:14:38PM +0900, Wongi Lee wrote:
> > > > > > > Hi,
> > > > > > > 
> > > > > > > Could the following upstream commits be queued for the active stable
> > > > > > > trees?
> > > > > > > 
> > > > > > >   commit 736b380e28d0480c7bc3e022f1950f31fe53a7c5
> > > > > > >   ("ipv6: account for fraggap on the paged allocation path")
> > > > > > 
> > > > > > I do not see that commit id in Linus's tree, are you sure it is correct?
> > > > > > 
> > > > > > >   commit eca856950f7cb1a221e02b99d758409f2c5cec42
> > > > > > >   ("ipv4: account for fraggap on the paged allocation path")
> > > > > > 
> > > > > > Same here, no id of that one in Linus's tree that I can see.
> > > > > > 
> > > > > > thanks,
> > > > > > 
> > > > > > greg k-h
> > > > > 
> > > > > 
> > > > > Hi Greg,
> > > > > 
> > > > > First, sorry for confusing you.
> > > > > 
> > > > > The commit IDs are from netdev/net.git:
> > > > > 
> > > > >   736b380e28d0480c7bc3e022f1950f31fe53a7c5
> > > > >   https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=736b380e28d0
> > > > > 
> > > > >   eca856950f7cb1a221e02b99d758409f2c5cec42
> > > > >   https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=eca856950f7c
> > > > > 
> > > > > They were applied to netdev without Cc: stable@vger.kernel.org, so I
> > > > > wanted to flag them for stable handling but I send it too fast (before
> > > > > merge).
> > > > > 
> > > > > I will resend the request with the Linus tree commit ID.
> > > > 
> > > > They have to be in Linus's tree, before we can take them in a stable
> > > > release, right?
> > > > 
> > > > And why were they not originally tagged with the cc: stable?  That would
> > > > save you time in the future as it would all just happen automatically.
> > > > 
> > > > thanks,
> > > > 
> > > > greg k-h
> > > 
> > > Right, my fault.
> > > 
> > > Also I just forgot cc'ing stable when sending it. I'll apply it next time.
> > 
> > Small heads-up: Both commits are now in Linus' tree and included in
> > v7.2-rc1:
> > 
> > $ git describe --contains 736b380e28d0480c7bc3e022f1950f31fe53a7c5
> > v7.2-rc1~29^2~66^2
> > $ git describe --contains eca856950f7cb1a221e02b99d758409f2c5cec42
> > v7.2-rc1~29^2~66^2~1
> > 
> > Can you queue those as needed down to the 6.1.y stable series?
> 
> Can you still pick this as well for 6.1.y? The upstream commit
> eca856950f7cb1a221e02b99d758409f2c5cec42 does not apply cleanly to
> 6.1.y but this is just because it deletes a comment (the second hunk),
> which was never added to 6.1.y.

Now applied, thanks.

greg k-h

^ permalink raw reply

* [PATCH 3/3] mm: move __folio_alloc() to page_alloc.h
From: Brendan Jackman @ 2026-07-16 14:30 UTC (permalink / raw)
  To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Zi Yan,
	Matthew Wilcox (Oracle), Jan Kara, Joshua Hahn, Byungchul Park,
	Gregory Price, Ying Huang, Alistair Popple, Hugh Dickins,
	Baolin Wang, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
	Baoquan He, Barry Song, Youngjun Park, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Huacai Chen, WANG Xuerui,
	Thomas Gleixner, Chuck Lever, Jeff Layton, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, Trond Myklebust,
	Anna Schumaker, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: linux-kernel, linux-mm, linux-fsdevel, iommu, loongarch,
	linux-nfs, netdev, Brendan Jackman
In-Reply-To: <20260716-folio-alloc-cleanups-v1-0-5363b8e92d33@google.com>

This is no longer used outside of mm so reduce the scope.

Ulterior motive for the move to mm/: A later patch will add an
alloc_flags arg here.

Signed-off-by: Brendan Jackman <jackmanb@google.com>
---
 include/linux/gfp.h | 4 ----
 mm/page_alloc.c     | 1 -
 mm/page_alloc.h     | 4 ++++
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 572605d84e30e..17c14b8ca007d 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -203,10 +203,6 @@ static inline void arch_free_page(struct page *page, int order) { }
 static inline void arch_alloc_page(struct page *page, int order) { }
 #endif
 
-struct folio *__folio_alloc_noprof(gfp_t gfp, unsigned int order, int preferred_nid,
-		nodemask_t *nodemask);
-#define __folio_alloc(...)			alloc_hooks(__folio_alloc_noprof(__VA_ARGS__))
-
 unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid,
 				nodemask_t *nodemask, int nr_pages,
 				struct page **page_array);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 86922270df157..ff8515e173e1e 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5446,7 +5446,6 @@ struct folio *__folio_alloc_noprof(gfp_t gfp, unsigned int order, int preferred_
 				    preferred_nid, nodemask, ALLOC_DEFAULT);
 	return page_rmappable_folio(page);
 }
-EXPORT_SYMBOL(__folio_alloc_noprof);
 
 struct folio *folio_alloc_node_noprof(gfp_t gfp, unsigned int order, int nid)
 {
diff --git a/mm/page_alloc.h b/mm/page_alloc.h
index b9259deddb59d..409be31898ae7 100644
--- a/mm/page_alloc.h
+++ b/mm/page_alloc.h
@@ -258,6 +258,10 @@ struct page *__alloc_pages_noprof(gfp_t gfp, unsigned int order, int preferred_n
 		nodemask_t *nodemask, unsigned int alloc_flags);
 #define __alloc_pages(...)			alloc_hooks(__alloc_pages_noprof(__VA_ARGS__))
 
+struct folio *__folio_alloc_noprof(gfp_t gfp, unsigned int order, int preferred_nid,
+		nodemask_t *nodemask);
+#define __folio_alloc(...)			alloc_hooks(__folio_alloc_noprof(__VA_ARGS__))
+
 extern void zone_pcp_reset(struct zone *zone);
 extern void zone_pcp_disable(struct zone *zone);
 extern void zone_pcp_enable(struct zone *zone);

-- 
2.54.0


^ permalink raw reply related

* [PATCH 2/3] mm, treewide: replace __folio_alloc_node() with folio_alloc_node()
From: Brendan Jackman @ 2026-07-16 14:30 UTC (permalink / raw)
  To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Zi Yan,
	Matthew Wilcox (Oracle), Jan Kara, Joshua Hahn, Byungchul Park,
	Gregory Price, Ying Huang, Alistair Popple, Hugh Dickins,
	Baolin Wang, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
	Baoquan He, Barry Song, Youngjun Park, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Huacai Chen, WANG Xuerui,
	Thomas Gleixner, Chuck Lever, Jeff Layton, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, Trond Myklebust,
	Anna Schumaker, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: linux-kernel, linux-mm, linux-fsdevel, iommu, loongarch,
	linux-nfs, netdev, Brendan Jackman
In-Reply-To: <20260716-folio-alloc-cleanups-v1-0-5363b8e92d33@google.com>

Commit 5b584d2d22dca ("mm: remove __alloc_pages_node()") removed the __
variant of alloc_pages_node(), after users had been migrated off it,
since it just complicates the API (requiring users to handle
NUMA_NO_NODE, or risking hotplug bugs) for no real benefit.

This patch brings __folio_alloc_node() into line too for exactly the
same reasons (including the ulterior motive of freeing up the __ variant
for use as an internal API).

This time, it's done as a single patch because A) the users are fewer
and B) folio_alloc_node() does not already exist like
alloc_pages_node() did.

No functional change intended.

Suggested-by: "Vlastimil Babka (SUSE)" <vbabka@kernel.org>
Link: https://lore.kernel.org/all/ed4572f4-0074-45c5-993d-7b6533eddc31@kernel.org/
Signed-off-by: Brendan Jackman <jackmanb@google.com>
---
 drivers/iommu/iommu-pages.c        |  9 +--------
 drivers/irqchip/irq-loongarch-ir.c |  4 ++--
 include/linux/gfp.h                | 12 +++---------
 mm/filemap.c                       |  2 +-
 mm/migrate.c                       |  2 +-
 mm/page_alloc.c                    | 17 +++++++++++++++--
 net/sunrpc/svc.c                   |  2 +-
 7 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/iommu/iommu-pages.c b/drivers/iommu/iommu-pages.c
index 3bab175d85571..47e0759f0e0ae 100644
--- a/drivers/iommu/iommu-pages.c
+++ b/drivers/iommu/iommu-pages.c
@@ -56,14 +56,7 @@ void *iommu_alloc_pages_node_sz(int nid, gfp_t gfp, size_t size)
 	 */
 	order = get_order(size);
 
-	/*
-	 * __folio_alloc_node() does not handle NUMA_NO_NODE like
-	 * alloc_pages_node() did.
-	 */
-	if (nid == NUMA_NO_NODE)
-		nid = numa_mem_id();
-
-	folio = __folio_alloc_node(gfp | __GFP_ZERO, order, nid);
+	folio = folio_alloc_node(gfp | __GFP_ZERO, order, nid);
 	if (unlikely(!folio))
 		return NULL;
 
diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c
index 21c649a89a706..427f7fd3cad42 100644
--- a/drivers/irqchip/irq-loongarch-ir.c
+++ b/drivers/irqchip/irq-loongarch-ir.c
@@ -384,7 +384,7 @@ static int redirect_table_init(struct redirect_desc *irde)
 	unsigned long *bitmap;
 	struct folio *folio;
 
-	folio = __folio_alloc_node(GFP_KERNEL | __GFP_ZERO, IRD_TABLE_PAGE_ORDER, irde->node);
+	folio = folio_alloc_node(GFP_KERNEL | __GFP_ZERO, IRD_TABLE_PAGE_ORDER, irde->node);
 	if (!folio) {
 		pr_err("Node [%d] redirect table alloc pages failed!\n", irde->node);
 		return -ENOMEM;
@@ -410,7 +410,7 @@ static int redirect_queue_init(struct redirect_desc *irde)
 	struct redirect_queue *inv_queue = &irde->inv_queue;
 	struct folio *folio;
 
-	folio = __folio_alloc_node(GFP_KERNEL | __GFP_ZERO, INV_QUEUE_PAGE_ORDER, irde->node);
+	folio = folio_alloc_node(GFP_KERNEL | __GFP_ZERO, INV_QUEUE_PAGE_ORDER, irde->node);
 	if (!folio) {
 		pr_err("Node [%d] invalid queue alloc pages failed!\n", irde->node);
 		return -ENOMEM;
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index e4e974a6e5f90..572605d84e30e 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -251,15 +251,9 @@ static inline void warn_if_node_offline(int this_node, gfp_t gfp_mask)
 	dump_stack();
 }
 
-static inline
-struct folio *__folio_alloc_node_noprof(gfp_t gfp, unsigned int order, int nid)
-{
-	warn_if_node_offline(nid, gfp);
+struct folio *folio_alloc_node_noprof(gfp_t gfp, unsigned int order, int nid);
 
-	return __folio_alloc_noprof(gfp, order, nid, NULL);
-}
-
-#define  __folio_alloc_node(...)		alloc_hooks(__folio_alloc_node_noprof(__VA_ARGS__))
+#define  folio_alloc_node(...)		alloc_hooks(folio_alloc_node_noprof(__VA_ARGS__))
 
 /*
  * Allocate pages, preferring the node given as nid. When nid == NUMA_NO_NODE,
@@ -282,7 +276,7 @@ static inline struct page *alloc_pages_noprof(gfp_t gfp_mask, unsigned int order
 }
 static inline struct folio *folio_alloc_noprof(gfp_t gfp, unsigned int order)
 {
-	return __folio_alloc_node_noprof(gfp, order, numa_node_id());
+	return folio_alloc_node_noprof(gfp, order, numa_node_id());
 }
 static inline struct folio *vma_alloc_folio_noprof(gfp_t gfp, int order,
 		struct vm_area_struct *vma, unsigned long addr)
diff --git a/mm/filemap.c b/mm/filemap.c
index 0dd8e2a15d746..1cf91970a6850 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1007,7 +1007,7 @@ struct folio *filemap_alloc_folio_noprof(gfp_t gfp, unsigned int order,
 		do {
 			cpuset_mems_cookie = read_mems_allowed_begin();
 			n = cpuset_mem_spread_node();
-			folio = __folio_alloc_node_noprof(gfp, order, n);
+			folio = folio_alloc_node_noprof(gfp, order, n);
 		} while (!folio && read_mems_allowed_retry(cpuset_mems_cookie));
 
 		return folio;
diff --git a/mm/migrate.c b/mm/migrate.c
index 222c8c15f782f..b7836b02f32db 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -2682,7 +2682,7 @@ static struct folio *alloc_misplaced_dst_folio(struct folio *src,
 			__GFP_NOWARN;
 		gfp &= ~__GFP_RECLAIM;
 	}
-	return __folio_alloc_node(gfp, order, nid);
+	return folio_alloc_node(gfp, order, nid);
 }
 
 /*
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 37a3c1c00e169..86922270df157 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5435,12 +5435,25 @@ EXPORT_SYMBOL(alloc_pages_node_noprof);
 struct folio *__folio_alloc_noprof(gfp_t gfp, unsigned int order, int preferred_nid,
 		nodemask_t *nodemask)
 {
-	struct page *page = __alloc_pages_noprof(gfp | __GFP_COMP, order,
-					preferred_nid, nodemask, ALLOC_DEFAULT);
+	struct page *page;
+
+	if (preferred_nid == NUMA_NO_NODE)
+		preferred_nid = numa_mem_id();
+
+	warn_if_node_offline(preferred_nid, gfp);
+
+	page = __alloc_pages_noprof(gfp | __GFP_COMP, order,
+				    preferred_nid, nodemask, ALLOC_DEFAULT);
 	return page_rmappable_folio(page);
 }
 EXPORT_SYMBOL(__folio_alloc_noprof);
 
+struct folio *folio_alloc_node_noprof(gfp_t gfp, unsigned int order, int nid)
+{
+	return __folio_alloc_noprof(gfp, order, nid, NULL);
+}
+EXPORT_SYMBOL(folio_alloc_node_noprof);
+
 /*
  * Common helper functions. Never use with __GFP_HIGHMEM because the returned
  * address cannot represent highmem pages. Use alloc_pages and then kmap if
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index ae9ec4bf34f72..74350a2e07036 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -711,7 +711,7 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
 	rqstp->rq_server = serv;
 	rqstp->rq_pool = pool;
 
-	rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0, node);
+	rqstp->rq_scratch_folio = folio_alloc_node(GFP_KERNEL, 0, node);
 	if (!rqstp->rq_scratch_folio)
 		goto out_enomem;
 

-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/3] mm: move internal mempolicy APIs to new internal header
From: Brendan Jackman @ 2026-07-16 14:30 UTC (permalink / raw)
  To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Zi Yan,
	Matthew Wilcox (Oracle), Jan Kara, Joshua Hahn, Byungchul Park,
	Gregory Price, Ying Huang, Alistair Popple, Hugh Dickins,
	Baolin Wang, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
	Baoquan He, Barry Song, Youngjun Park, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Huacai Chen, WANG Xuerui,
	Thomas Gleixner, Chuck Lever, Jeff Layton, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, Trond Myklebust,
	Anna Schumaker, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: linux-kernel, linux-mm, linux-fsdevel, iommu, loongarch,
	linux-nfs, netdev, Brendan Jackman
In-Reply-To: <20260716-folio-alloc-cleanups-v1-0-5363b8e92d33@google.com>

There are no external users for this surface, reduce the scope.

Ulterior motive: a later patch will add an alloc_flags arg to some parts
of this.

Note it might seem like this could just go in internal.h, since it's
pretty small, but actually it will eventually need to import
page_alloc.h, we don't want to import that from internal.h so best to
proactively created this header now.

Signed-off-by: Brendan Jackman <jackmanb@google.com>
---
 MAINTAINERS         |  1 +
 include/linux/gfp.h |  9 ---------
 mm/filemap.c        |  2 ++
 mm/mempolicy.c      |  1 +
 mm/mempolicy.h      | 31 +++++++++++++++++++++++++++++++
 mm/shmem.c          |  1 +
 mm/swap_state.c     |  1 +
 7 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8813d5d7eb0c1..9cde99c259639 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17068,6 +17068,7 @@ F:	include/uapi/linux/mempolicy.h
 F:	include/linux/migrate.h
 F:	include/linux/migrate_mode.h
 F:	mm/mempolicy.c
+F:	mm/mempolicy.h
 F:	mm/migrate.c
 F:	mm/migrate_device.c
 
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 872bc53f32ec8..e4e974a6e5f90 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -11,7 +11,6 @@
 #include <linux/sched.h>
 
 struct vm_area_struct;
-struct mempolicy;
 
 /* Helper macro to avoid gfp flags if they are the default one */
 #define __default_gfp(a,b,...) b
@@ -274,8 +273,6 @@ struct page *alloc_pages_node_noprof(int nid, gfp_t gfp_mask, unsigned int order
 #ifdef CONFIG_NUMA
 struct page *alloc_pages_noprof(gfp_t gfp, unsigned int order);
 struct folio *folio_alloc_noprof(gfp_t gfp, unsigned int order);
-struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
-		struct mempolicy *mpol, pgoff_t ilx, int nid);
 struct folio *vma_alloc_folio_noprof(gfp_t gfp, int order, struct vm_area_struct *vma,
 		unsigned long addr);
 #else
@@ -287,11 +284,6 @@ static inline struct folio *folio_alloc_noprof(gfp_t gfp, unsigned int order)
 {
 	return __folio_alloc_node_noprof(gfp, order, numa_node_id());
 }
-static inline struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
-		struct mempolicy *mpol, pgoff_t ilx, int nid)
-{
-	return folio_alloc_noprof(gfp, order);
-}
 static inline struct folio *vma_alloc_folio_noprof(gfp_t gfp, int order,
 		struct vm_area_struct *vma, unsigned long addr)
 {
@@ -301,7 +293,6 @@ static inline struct folio *vma_alloc_folio_noprof(gfp_t gfp, int order,
 
 #define alloc_pages(...)			alloc_hooks(alloc_pages_noprof(__VA_ARGS__))
 #define folio_alloc(...)			alloc_hooks(folio_alloc_noprof(__VA_ARGS__))
-#define folio_alloc_mpol(...)			alloc_hooks(folio_alloc_mpol_noprof(__VA_ARGS__))
 #define vma_alloc_folio(...)			alloc_hooks(vma_alloc_folio_noprof(__VA_ARGS__))
 
 #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
diff --git a/mm/filemap.c b/mm/filemap.c
index 1dbb4c6f824e9..0dd8e2a15d746 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -52,6 +52,7 @@
 
 #include <asm/tlbflush.h>
 #include "internal.h"
+#include "mempolicy.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/filemap.h>
@@ -63,6 +64,7 @@
 
 #include <asm/mman.h>
 
+#include "mempolicy.h"
 #include "swap.h"
 
 /*
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 5720f7f54d942..31c4e9f49b5e9 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -119,6 +119,7 @@
 #include <linux/memory.h>
 
 #include "internal.h"
+#include "mempolicy.h"
 #include "page_alloc.h"
 
 /* Internal flags */
diff --git a/mm/mempolicy.h b/mm/mempolicy.h
new file mode 100644
index 0000000000000..d80fe4c559786
--- /dev/null
+++ b/mm/mempolicy.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * mm-internal API for mempolicy.c. Public API lives in
+ * include/linux/mempolicy.h.
+ */
+#ifndef __MM_MEMPOLICY_H
+#define __MM_MEMPOLICY_H
+
+#include <linux/gfp.h>
+#include <linux/mempolicy.h>
+
+#ifdef CONFIG_NUMA
+struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
+		struct mempolicy *mpol, pgoff_t ilx, int nid);
+#else
+static inline struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
+		struct mempolicy *mpol, pgoff_t ilx, int nid)
+{
+	return folio_alloc_noprof(gfp, order);
+}
+#endif
+
+#define folio_alloc_mpol(...)			alloc_hooks(folio_alloc_mpol_noprof(__VA_ARGS__))
+
+unsigned long alloc_pages_bulk_mempolicy_noprof(gfp_t gfp,
+				unsigned long nr_pages,
+				struct page **page_array);
+#define  alloc_pages_bulk_mempolicy(...)				\
+	alloc_hooks(alloc_pages_bulk_mempolicy_noprof(__VA_ARGS__))
+
+#endif /* __MM_MEMPOLICY_H */
diff --git a/mm/shmem.c b/mm/shmem.c
index 5071177059a96..69f561332bb93 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -41,6 +41,7 @@
 #include <linux/swapfile.h>
 #include <linux/iversion.h>
 #include <linux/unicode.h>
+#include "mempolicy.h"
 #include "swap.h"
 
 static struct vfsmount *shm_mnt __ro_after_init;
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 5be825911e645..8ccd03c39a407 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -24,6 +24,7 @@
 #include <linux/shmem_fs.h>
 #include <linux/sysctl.h>
 #include "internal.h"
+#include "mempolicy.h"
 #include "swap_table.h"
 #include "swap.h"
 

-- 
2.54.0


^ permalink raw reply related

* [PATCH 0/3] mm: yet more cleanups for page_alloc APIs
From: Brendan Jackman @ 2026-07-16 14:30 UTC (permalink / raw)
  To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Zi Yan,
	Matthew Wilcox (Oracle), Jan Kara, Joshua Hahn, Byungchul Park,
	Gregory Price, Ying Huang, Alistair Popple, Hugh Dickins,
	Baolin Wang, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
	Baoquan He, Barry Song, Youngjun Park, Joerg Roedel (AMD),
	Will Deacon, Robin Murphy, Huacai Chen, WANG Xuerui,
	Thomas Gleixner, Chuck Lever, Jeff Layton, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, Trond Myklebust,
	Anna Schumaker, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: linux-kernel, linux-mm, linux-fsdevel, iommu, loongarch,
	linux-nfs, netdev, Brendan Jackman

Based on mm-new.

Align some APIs with recent cleanups [0][1], and hide more mm-internal APIs
from public headers.

Part of this was also proposed by Vlastimil right as I was writing it:
https://lore.kernel.org/all/ed4572f4-0074-45c5-993d-7b6533eddc31@kernel.org/

And just like with my other recent patches, this has the ulterior motive
of enabling later incoming patches to add alloc_flags arguments to these
internal functions. (But, I do think these are justified as cleanups in
their own right).

[0]: https://lore.kernel.org/all/20260715-spin-trylock-followup-v3-0-fc4d246f705d@google.com/
[1]: https://lore.kernel.org/all/20260703-alloc-trylock-v5-0-c87b714e19d3@google.com/

Signed-off-by: Brendan Jackman <jackmanb@google.com>
---
Brendan Jackman (3):
      mm: move internal mempolicy APIs to new internal header
      mm, treewide: replace __folio_alloc_node() with folio_alloc_node()
      mm: move __folio_alloc() to page_alloc.h

 MAINTAINERS                        |  1 +
 drivers/iommu/iommu-pages.c        |  9 +--------
 drivers/irqchip/irq-loongarch-ir.c |  4 ++--
 include/linux/gfp.h                | 25 +++----------------------
 mm/filemap.c                       |  4 +++-
 mm/mempolicy.c                     |  1 +
 mm/mempolicy.h                     | 31 +++++++++++++++++++++++++++++++
 mm/migrate.c                       |  2 +-
 mm/page_alloc.c                    | 18 +++++++++++++++---
 mm/page_alloc.h                    |  4 ++++
 mm/shmem.c                         |  1 +
 mm/swap_state.c                    |  1 +
 net/sunrpc/svc.c                   |  2 +-
 13 files changed, 65 insertions(+), 38 deletions(-)
---
base-commit: 512c82e906fe2c26023e307c7951d53e90910b57
change-id: 20260716-folio-alloc-cleanups-0fe319b881c2

Best regards,
--  
Brendan Jackman <jackmanb@google.com>


^ permalink raw reply

* Re: [PATCH net-next] net: mana: Add debug knob to skip TX timeout recovery reset
From: Simon Horman @ 2026-07-16 14:27 UTC (permalink / raw)
  To: Aditya Garg
  Cc: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
	edumazet, kuba, pabeni, kotaranov, ernis, dipayanroy, ssengar,
	gargaditya, linux-hyperv, netdev, linux-kernel, linux-rdma
In-Reply-To: <20260710132229.2851441-1-gargaditya@linux.microsoft.com>

On Fri, Jul 10, 2026 at 06:22:29AM -0700, Aditya Garg wrote:
> Add a per-port debugfs boolean "tx_timeout_skip_reset" that, when
> enabled, makes mana_tx_timeout() log the TX timeout and return without
> queueing the per-port detach/attach recovery work.
> 
> This is a debug-only aid for bringup and qualification: skipping the
> recovery reset keeps the device and queue state intact so a TX timeout
> can be correlated with hardware telemetry. The knob defaults to false,
> so production recovery behaviour is unchanged.
> 
> Signed-off-by: Aditya Garg <gargaditya@linux.microsoft.com>
> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> Reviewed-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>

Reviewed-by: Simon Horman <horms@kernel.org>


^ permalink raw reply

* Re: net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Jonathan Corbet @ 2026-07-16 14:27 UTC (permalink / raw)
  To: Markus Elfring, Dan Carpenter, netdev, kernel-janitors
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	MD Danish Anwar, Michael Grzeschik, Paolo Abeni,
	Uwe Kleine-König, Vadim Fedorenko, Yibo Dong, LKML
In-Reply-To: <320cb00c-28f8-4871-9ec5-d9b86cca8bc9@web.de>

Markus Elfring <Markus.Elfring@web.de> writes:

> Does your understanding of programming language details differ from the view
> by Jonathan Corbet?

I'm not sure why you keep dragging me into this - I have not opined on
this particular situation, which is definitely *not* a dereference.

jon

^ permalink raw reply

* [RFC PATCH net-next v0 1/6] net: add GeoNetworking protocol
From: Simon Dietz @ 2026-07-16 13:58 UTC (permalink / raw)
  To: netdev
  Cc: edumazet, davem, kuniyu, andrew+netdev, simon.dietz, dietz23838,
	linux-wireless, johannes
In-Reply-To: <20260716135902.2895237-1-simon.dietz@plantwatch.de>

Implement the GeoNetworking / ETSI ITS-G5 ('net/gn') protocol which
is based on 802.11p wifi and used for vehicle2x applications. It is
standardized by the ETSI and used by some car manufacturers
(especially in europe). It enables ad-hoc, multi-hop geographical
communication and routing among vehicles (and road- or railside
infrastructure).

Most work of this implementation has been done by the bachelor
project 2018/2019 of the operating systems and middleware group of
the Hasso Plattner Institute, University of Potsdam, which the author
was part of.

The project was supervised by:
- Jossekin Beilharz
- Lukas Pirl
- Prof. Andreas Polze

The code is published under the GPL v2 at this location:
https://gitlab.com/hpi-potsdam/osm/g5-on-linux/linux-geonetworking/

The original code base was based on linux version 5.1. This patch
includes the original codebase rebased to the current net-next code
base so that it compiles under current net-next (as required by
submitting patch guidelines in order not to break git bisect).

Further commits of this patch set will fix remaining issues, bugs,
logic errors and kernel code style violations.

This patch set is not merge-ready, yet. E.g. network namespaces are
not supported at all, documentation is missing, ...

Purpose of this RFC is to determine if GeoNetworking support in the
linux kernel is desirable and if so what steps would have to be done
next.

Signed-off-by: Simon Dietz <simon.dietz@plantwatch.de>
---
 include/linux/gn.h                  |  335 +++++
 include/linux/gn_routing.h          |   63 +
 include/linux/socket.h              |    6 +-
 include/uapi/linux/gn.h             |   88 ++
 include/uapi/linux/if_ether.h       |    1 +
 net/Kconfig                         |    2 +
 net/Makefile                        |    1 +
 net/gn/Kconfig                      |    4 +
 net/gn/Makefile                     |    8 +
 net/gn/gn_proc.c                    |   89 ++
 net/gn/gn_prot.c                    | 1857 +++++++++++++++++++++++++++
 net/gn/gn_routing.c                 |  500 ++++++++
 net/gn/sysctl_net_gn.c              |   33 +
 security/selinux/hooks.c            |    5 +-
 security/selinux/include/classmap.h |    3 +-
 15 files changed, 2991 insertions(+), 4 deletions(-)
 create mode 100644 include/linux/gn.h
 create mode 100644 include/linux/gn_routing.h
 create mode 100644 include/uapi/linux/gn.h
 create mode 100644 net/gn/Kconfig
 create mode 100644 net/gn/Makefile
 create mode 100644 net/gn/gn_proc.c
 create mode 100644 net/gn/gn_prot.c
 create mode 100644 net/gn/gn_routing.c
 create mode 100644 net/gn/sysctl_net_gn.c

diff --git a/include/linux/gn.h b/include/linux/gn.h
new file mode 100644
index 000000000000..bcef5c169c0f
--- /dev/null
+++ b/include/linux/gn.h
@@ -0,0 +1,335 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_GN_H__
+#define __LINUX_GN_H__
+
+#include <net/sock.h>
+#include <uapi/linux/gn.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/bitfield.h>
+#include <asm/byteorder.h>
+#include <linux/etherdevice.h>
+
+#define GN_MAX_HEADER_SZ 88
+#define GN_MAXSZ (ETH_DATA_LEN - GN_MAX_HEADER_SZ)
+#define GN_MAXHOPS 255 /* 8 bits of hop counter */
+
+#define GN_VERSION 1
+#define GN_BROADCAST_ADDR 0xffffffffffffULL
+/* itsGnDefaultHopLimit: Default hop limit (for BH RHL/CH MHL) */
+#define DEFAULT_HOP_LIMIT 10
+
+/* time until location table entry becomes invalid */
+#define GN_LOC_TE_LIFETIME 20000
+/* time between two beacons */
+#define GN_BEACON_RETRANSMIT_TIME 3000
+/* time between retransmits of an unanswered location service request */
+#define GN_LS_RETRANSMIT_TIME 1000
+
+/* sizes in KiB */
+#define GN_UC_BUF_SIZE 256
+#define GN_BC_BUF_SIZE 1024
+#define GN_CBF_BUF_SIZE 256
+
+#define GN_UC_BUF 0
+#define GN_BC_BUF 1
+#define GN_CBF_BUF 2
+
+#define BH_NH_ANY 0
+#define BH_NH_COMMON_HEADER 1
+#define BH_NH_SECURED_PACKET 2
+
+/* itsGnDefaultPacketLifetime: Default packet lifetime in seconds */
+// FIXME Has to be encoded
+#define BH_LT_DEFAULT 60
+
+#define CH_NH_ANY 0
+#define CH_NH_BTPA 1
+#define CH_NH_BTPB 2
+#define CH_NH_IPV6 3
+
+#define CH_FLAG_MOBILE (1 << 7)
+
+/* itsGnDefaultTrafficClass: Default traffic class */
+#define CH_TC_DEFAULT 0
+
+#define CH_HT_BEACON 1
+#define CH_HT_GUC 2
+#define CH_HT_GAC 3
+#define CH_HT_GBC 4
+#define CH_HT_TSB 5
+#define CH_HT_LS 6
+
+#define CH_HST_GABC_CIRCLE 0
+#define CH_HST_GABC_RECT 1
+#define CH_HST_GABC_ELIP 2
+
+#define CH_HST_TSB_SINGLE_HOP 0
+#define CH_HST_TSB_MULTI_HOP 1
+
+#define CH_HST_LS_REQUEST 0
+#define CH_HST_LS_REPLY 1
+
+#define CH_HST_UNSPECIFIED 0
+#define CH_HST_BEACON 1
+
+#define CH_PL_EMPTY 0L
+#define BEACON_MHL 1
+
+#define GN_BASE_HEADER_SIZE (sizeof(struct gn_basic_header) + sizeof(struct gn_common_header))
+#define GN_SET_BTP(skb, btp_h, extended_header_type) do { \
+	skb_set_transport_header(skb, GN_BASE_HEADER_SIZE + sizeof(extended_header_type)); \
+	btp_h = (struct btp_header *) skb_transport_header(skb); \
+	} while (0)
+
+/**
+ *	struct gn_iface - GeoNetworking interface
+ *	@dev - Network device associated with this interface
+ *	@address - Our address
+ *	@local_sn - Current sequence number
+ */
+struct gn_iface {
+	struct net_device	*dev;
+	gn_address_t	address;
+	struct gn_position pos;
+	atomic_t local_sn;
+	struct hlist_node hnode;
+	struct rcu_head rcu;
+};
+
+struct gn_sock {
+	/* struct sock has to be the first member of gn_sock */
+	struct sock	sk;
+	struct gn_scope scope;
+	gn_address_t src_addr;
+	gn_address_t dst_addr;
+	u16 src_port;
+	u16 dst_port;
+	u8 protocol;
+};
+
+static inline struct gn_sock *gn_sk(struct sock *sk)
+{
+	return (struct gn_sock *)sk;
+}
+
+struct btp_header {
+	__be16 dst_port;
+	__be16 src_port;
+} __attribute__ ((packed));
+
+enum ITS_TYPE {
+	UNKNOWN,
+	PEDESTRIAN,
+	CYCLIST,
+	MOPED,
+	MOTORCYCLE,
+	PASSENGER_CAR,
+	BUS,
+	LIGHT_TRUCK,
+	HEAVY_TRUCK,
+	TAILER,
+	SPECIAL_VEHICLE,
+	TRAM,
+	ROAD_SIDE_UNIT
+};
+
+#define GN_ADDRESS_M BIT(63)
+#define GN_ADDRESS_ST GENMASK(62, 58)
+#define GN_ADDRESS_RES GENMASK(57, 48)
+#define GN_ADDRESS_MID GENMASK(47, 0)
+
+typedef __be16 gn_spai_t;
+
+#define GN_LPV_PAI BIT(15)
+#define GN_LPV_S GENMASK(14, 0)
+
+struct gn_lpv {
+	gn_address_t addr;
+	__be32 tst;
+	__be32 lat;
+	__be32 lon;
+	gn_spai_t spai;
+	__be16 h; //signed
+} __attribute__ ((packed));
+
+struct gn_spv {
+	gn_address_t addr;
+	__be32 tst;
+	__be32 lat; //short
+	__be32 lon;
+} __attribute__ ((packed));
+
+/*version: Identifies the version of the GeoNetworking protocol
+ *
+ *nh: Identifies the type of header immediately following the GeoNetworking Basic Header
+ *reserved: Reserved. Set to 0
+ *
+ *lt: Lifetime field.
+ *Indicates the maximum tolerable time a packet may be buffered until it reaches its destination Bit 0 to Bit 5:
+ *LT sub-field Multiplier Bit 6 to Bit 7: LT sub-field Base Encoded as specified in clause 9.6.4
+ *
+ *rhl:   Decremented by 1 by each GeoAdhoc router that forwards the packet. The packet shall not be forwarded if RHL is decremented to zero
+ */
+
+struct gn_basic_header {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+	__u8 nh : 4;
+	__u8 version : 4;
+
+#elif __BIG_ENDIAN_BITFIELD
+	__u8 version : 4;
+	__u8 nh : 4;
+#else
+#error "please fix asm/byteorder.h"
+#endif
+	__u8 reserved;
+	__u8 lt;
+	__u8 rhl;
+} __attribute__ ((packed));
+
+struct gn_common_header {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+	__u8 reserved : 4;
+	__u8 nh : 4;
+	__u8 hst : 4;
+	__u8 ht : 4;
+#elif __BIG_ENDIAN_BITFIELD
+	__u8 nh : 4;
+	__u8 reserved : 4;
+	__u8 ht : 4;
+	__u8 hst : 4;
+#else
+#error "please fix asm/byteorder.h"
+#endif
+
+	__u8 tc;
+	__u8 flags;
+	__be16 pl;
+	__u8 mhl;
+	__u8 reserved2;
+} __attribute__ ((packed));
+
+/*there are 6 types of headers:
+ * 1) GUC packet header (clause 9.8.2).
+ * 2) TSB packet header (clause 9.8.3).
+ * 3) SHB packet header (clause 9.8.4).
+ * 4) GBC and GAC packet headers (clause 9.8.5).
+ * 5) BEACON packet header (clause 9.8.6).
+ * 6) LS Request and LS Reply packet headers (clause 9.8.7 and clause 9.8.8).
+ */
+
+struct gn_guc_header {
+	__be16 sn;
+	__be16 reserved;
+	struct gn_lpv sopv;
+	struct gn_spv depv;
+} __attribute__ ((packed));
+
+
+struct gn_tsb_header {
+	__be16 sn;
+	__be16 reserved;
+	struct gn_lpv sopv;
+} __attribute__ ((packed));
+
+
+struct gn_shb_header {
+	struct gn_lpv sopv;
+	__be32 mdd;
+} __attribute__ ((packed));
+
+
+//gac and gbc have the same structure
+//TODO: typedef
+struct gn_gxc_header {
+	__be16 sn;
+	__be16 reserved;
+	// TODO changed!! -> struct gn_spv sopv;
+	struct gn_lpv sopv;
+	__be32 gap_lat; //signed
+	__be32 gap_lon; //signed
+	__be16 da;
+	__be16 db;
+	__be16 angle;
+	__be16 reserved2;
+} __attribute__ ((packed));
+
+struct gn_beacon_header {
+	struct gn_lpv sopv;
+} __attribute__ ((packed));
+
+struct gn_ls_request_header {
+	__be16 sn;
+	__be16 reserved;
+	struct gn_lpv sopv;
+	gn_address_t addr;
+} __attribute__ ((packed));
+
+//same structure as gn_guc_header, left in for abstraction
+struct gn_ls_reply_header {
+	__be16 sn;
+	__be16 reserved;
+	struct gn_lpv sopv;
+	struct gn_spv depv;
+} __attribute__ ((packed));
+
+struct gn_header {
+	struct gn_basic_header gb_h;
+	struct gn_common_header gc_h;
+	union {
+		struct gn_guc_header guc_h;
+		union {
+			struct gn_gxc_header gac_h;
+			struct gn_gxc_header gbc_h;
+		};
+		struct gn_tsb_header tsb_h;
+		struct gn_shb_header shb_h;
+		struct gn_beacon_header beacon_h;
+		struct gn_ls_request_header ls_request_h;
+		struct gn_ls_reply_header ls_reply_h;
+		__be16 sn;
+	};
+} __attribute__ ((packed));
+
+/* Inter module exports */
+
+u32 gn_tai_to_gn(ktime_t tai_time);
+u32 gn_timestamp_now(void);
+
+void gn_fill_sopv(struct gn_iface *gnif, struct gn_lpv *sopv, gn_address_t addr);
+
+extern struct hlist_head gn_sockets;
+extern rwlock_t gn_sockets_lock;
+
+extern struct hlist_head gn_interfaces;
+extern spinlock_t gn_interfaces_lock;
+
+#ifdef CONFIG_SYSCTL
+extern int gn_register_sysctl(void);
+extern void gn_unregister_sysctl(void);
+#else
+static inline int gn_register_sysctl(void)
+{
+	return 0;
+}
+static inline void gn_unregister_sysctl(void)
+{
+}
+#endif
+
+#ifdef CONFIG_PROC_FS
+extern int gn_proc_init(void);
+extern void gn_proc_exit(void);
+#else
+static inline int gn_proc_init(void)
+{
+	return 0;
+}
+static inline void gn_proc_exit(void)
+{
+}
+#endif /* CONFIG_PROC_FS */
+
+#endif /* __LINUX_GN_H__ */
diff --git a/include/linux/gn_routing.h b/include/linux/gn_routing.h
new file mode 100644
index 000000000000..44f098a0136c
--- /dev/null
+++ b/include/linux/gn_routing.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_GN_ROUTING_H__
+#define __LINUX_GN_ROUTING_H__
+
+#include <linux/gn.h>
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define GN_MAX_PDR_EMA_BETA 90
+#define GN_MAX_PDR 100
+#define GN_DPL_SIZE 8
+#define GN_LSB_SIZE 16	// this is the number of entries per entry (has to be a power of 2 atm)
+			// ETSI sets a maximum of 1024 octets/bytes, which is not trivial to maintain
+
+#define GN_NON_AREA_FORWARDING_UNSPECIFIED	0
+#define GN_NON_AREA_FORWARDING_GREEDY		1
+#define GN_NON_AREA_FORWARDING_CBF		2
+#define GN_NON_AREA_FORWARDING GN_NON_AREA_FORWARDING_GREEDY
+
+#define GN_AREA_FORWARDING_UNSPECIFIED	0
+#define GN_AREA_FORWARDING_SIMPLE	1
+#define GN_AREA_FORWARDING_CBF		2
+#define GN_AREA_FORWARDING_ADVANCED	3
+#define GN_AREA_FORWARDING GN_AREA_FORWARDING_SIMPLE
+
+#define GN_QUEUE_DIRECT		0
+#define GN_QUEUE_LS_PENDING	1
+#define GN_QUEUE_LS_STALE	2
+#define GN_QUEUE_ERROR		3
+
+#define GN_FORWARD_BROADCAST	0
+#define GN_FORWARD_NEXT_HOP	1
+#define GN_FORWARD_BUFFER	2
+#define GN_FORWARD_DISCARD	3
+
+struct gn_dpd_buf {
+	u16 sn[GN_DPL_SIZE];
+	u8 head;
+};
+
+struct loc_te {
+	gn_address_t addr;
+	u8 ll_address[ETH_ALEN];
+	struct gn_lpv pv;
+	struct gn_dpd_buf dpl;
+	struct sk_buff_head lsb;
+	u32 tst_addr;
+	u32 pdr;
+	struct hlist_node hnode;
+	u8 ls_pending: 1;
+	u8 is_neighbour: 1;
+};
+
+s64 gn_F(struct gn_coord self, struct gn_geo_scope scope);
+int gn_gxc_forward(struct gn_iface *gnif, s64 f, u8 *addr, struct gn_lpv *depv);
+
+int gn_query_ll_address(gn_address_t addr, u8 *ll_address);
+int gn_ls_queue(gn_address_t dest_addr, struct sk_buff *skb);
+void gn_ls_flush(gn_address_t dest_addr);
+int gn_update_location_table(struct gn_lpv *pv, bool make_neighbour, const u8 *ll_address, const __be16 *sn);
+
+#endif // __LINUX_GN_ROUTING_H__
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 2a8d7b14f1d1..17d8c466f120 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -254,8 +254,8 @@ struct ucred {
 #define AF_MCTP		45	/* Management component
 				 * transport protocol
 				 */
-
-#define AF_MAX		46	/* For now.. */
+#define AF_GN		46	/* ETSI ITS GeoNetworking */
+#define AF_MAX		47	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -306,6 +306,7 @@ struct ucred {
 #define PF_SMC		AF_SMC
 #define PF_XDP		AF_XDP
 #define PF_MCTP		AF_MCTP
+#define PF_GN		AF_GN
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
@@ -400,6 +401,7 @@ struct ucred {
 #define SOL_MCTP	285
 #define SOL_SMC		286
 #define SOL_VSOCK	287
+#define SOL_GN		288
 
 /* IPX options */
 #define IPX_TYPE	1
diff --git a/include/uapi/linux/gn.h b/include/uapi/linux/gn.h
new file mode 100644
index 000000000000..355737d483b5
--- /dev/null
+++ b/include/uapi/linux/gn.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _UAPI__LINUX_GN_H__
+#define _UAPI__LINUX_GN_H__
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/socket.h>
+
+#ifndef __KERNEL__
+#include <sys/time.h>
+#else
+#include <linux/time.h>
+#endif
+
+/*
+ * GeoNetworking structures
+ *
+ */
+#define GNPORT_ANY	0
+#define GNPORT_FIRST	1
+#define GNPORT_RESERVED	3000
+#define GNPORT_LAST	65535
+
+#define GNADDR_BROADCAST 0x0000FFFFFFFFFFFFLLU
+
+/* Valid protocols */
+#define GN_PROTO_ANY	0
+#define GN_PROTO_BTP_A	1
+#define GN_PROTO_BTP_B	2
+#define GN_PROTO_INET6	3
+#define GN_PROTO_MAX	GN_PROTO_INET6
+
+/* protocol-specific ioctls */
+#define SIOCGNSPOSITION			(SIOCPROTOPRIVATE + 0)
+
+/* gn_position flags */
+#define GN_POSITION_MOBILE		(1 << 0)
+
+/* {get,set}sockopt options */
+#define GN_SCOPE 1
+
+#define GN_SCOPE_UNSPECIFIED		0
+#define GN_SCOPE_TOPOLOGICAL		1
+#define GN_SCOPE_GEOGRAPHICAL		2
+#define GN_SCOPE_GEOGRAPHICAL_ANYCAST	3
+#define GN_SCOPE_MAX			GN_SCOPE_GEOGRAPHICAL_ANYCAST
+
+#define GN_SHAPE_UNSPECIFIED	0
+#define GN_SHAPE_CIRCLE		1
+#define GN_SHAPE_ELLIPSE	2
+#define GN_SHAPE_RECTANGLE	3
+
+typedef __be64 gn_address_t;
+
+struct gn_coord {
+	__s32 lat;
+	__s32 lon;
+};
+
+struct gn_geo_scope {
+	struct gn_coord coord;
+	__u16 angle;
+	__u16 a, b;
+	__u8 shape;
+};
+
+struct gn_scope {
+	__u8 scope_type;
+	union {
+		__u8 topo_hops;
+		struct gn_geo_scope geo_scope;
+	};
+};
+
+struct gn_position {
+	struct timespec64 tst;
+	struct gn_coord coord;
+	__u8 flags;
+};
+
+struct sockaddr_gn {
+	__kernel_sa_family_t sgn_family;
+	gn_address_t sgn_addr;
+	__u16 sgn_port;
+} __attribute__((packed));
+
+#endif /* _UAPI__LINUX_GN_H__ */
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 1ffac52c39df..e7b0baa0d15b 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -113,6 +113,7 @@
 #define ETH_P_FIP	0x8914		/* FCoE Initialization Protocol */
 #define ETH_P_80221	0x8917		/* IEEE 802.21 Media Independent Handover Protocol */
 #define ETH_P_HSR	0x892F		/* IEC 62439-3 HSRv1	*/
+#define ETH_P_GN	0x8947		/* ETSI-ITS Network Header */
 #define ETH_P_NSH	0x894F		/* Network Service Header */
 #define ETH_P_LOOPBACK	0x9000		/* Ethernet loopback packet, per IEEE 802.3 */
 #define ETH_P_QINQ1	0x9100		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
diff --git a/net/Kconfig b/net/Kconfig
index e38477393551..b49b1e04b0da 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -26,6 +26,8 @@ menuconfig NET
 
 if NET
 
+source "net/gn/Kconfig"
+
 config WANT_COMPAT_NETLINK_MESSAGES
 	bool
 	help
diff --git a/net/Makefile b/net/Makefile
index 5b2dd7f07a85..bd3be868b2d8 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -75,3 +75,4 @@ obj-$(CONFIG_MPTCP)		+= mptcp/
 obj-$(CONFIG_MCTP)		+= mctp/
 obj-$(CONFIG_NET_HANDSHAKE)	+= handshake/
 obj-$(CONFIG_NET_SHAPER)	+= shaper/
+obj-$(CONFIG_GN)		+= gn/
diff --git a/net/gn/Kconfig b/net/gn/Kconfig
new file mode 100644
index 000000000000..4857e43f02c5
--- /dev/null
+++ b/net/gn/Kconfig
@@ -0,0 +1,4 @@
+config GN
+    tristate "Enable GeoNetworking support"
+    default y
+    select LLC
diff --git a/net/gn/Makefile b/net/gn/Makefile
new file mode 100644
index 000000000000..04cf4548654a
--- /dev/null
+++ b/net/gn/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Linux GeoNetworking layer.
+#
+
+obj-$(CONFIG_GN) += gn.o
+gn-y			:= gn_prot.o gn_routing.o
+gn-$(CONFIG_PROC_FS)	+= gn_proc.o
+gn-$(CONFIG_SYSCTL)	+= sysctl_net_gn.o
diff --git a/net/gn/gn_proc.c b/net/gn/gn_proc.c
new file mode 100644
index 000000000000..75e126f9e494
--- /dev/null
+++ b/net/gn/gn_proc.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ *	gn_proc.c - proc support for GeoNetworking
+ *
+ *	Copyright(c) Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ *	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.
+ */
+
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <linux/gn.h>
+#include <linux/export.h>
+
+static void *gn_seq_socket_start(struct seq_file *seq, loff_t *pos)
+	__acquires(gn_sockets_lock)
+{
+	read_lock_bh(&gn_sockets_lock);
+	return seq_hlist_start_head(&gn_sockets, *pos);
+}
+
+static void *gn_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	return seq_hlist_next(v, &gn_sockets, pos);
+}
+
+static void gn_seq_socket_stop(struct seq_file *seq, void *v)
+	__releases(gn_sockets_lock)
+{
+	read_unlock_bh(&gn_sockets_lock);
+}
+
+static int gn_seq_socket_show(struct seq_file *seq, void *v)
+{
+	struct sock *s;
+	struct gn_sock *gn;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_printf(seq, "Type Local_addr  Remote_addr Tx_queue "
+				"Rx_queue St UID\n");
+		goto out;
+	}
+
+	s = sk_entry(v);
+	gn = gn_sk(s);
+
+	seq_printf(seq,
+		   "%02X   %08llX:%04X  %08llX:%04X  %08X:%08X "
+		   "%02X\n",
+		   s->sk_type, be64_to_cpu(gn->src_addr), gn->src_port,
+		   be64_to_cpu(gn->dst_addr), gn->dst_port,
+		   sk_wmem_alloc_get(s), sk_rmem_alloc_get(s), s->sk_state);
+	//from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
+out:
+	return 0;
+}
+
+static const struct seq_operations gn_seq_socket_ops = {
+	.start = gn_seq_socket_start,
+	.next = gn_seq_socket_next,
+	.stop = gn_seq_socket_stop,
+	.show = gn_seq_socket_show,
+};
+
+int __init gn_proc_init(void)
+{
+	if (!proc_mkdir("gn", init_net.proc_net))
+		return -ENOMEM;
+	pr_debug("gn: inserting gn/socket into procfs");
+	if (!proc_create_seq("gn/socket", 0444, init_net.proc_net,
+			     &gn_seq_socket_ops))
+		goto out;
+
+	return 0;
+out:
+	remove_proc_subtree("gn", init_net.proc_net);
+	return -ENOMEM;
+}
+
+void gn_proc_exit(void)
+{
+	remove_proc_subtree("gn", init_net.proc_net);
+}
diff --git a/net/gn/gn_prot.c b/net/gn/gn_prot.c
new file mode 100644
index 000000000000..d63c74807f8c
--- /dev/null
+++ b/net/gn/gn_prot.c
@@ -0,0 +1,1857 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * GeoNetworking
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+#include <linux/if_arp.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <net/sock.h>
+#include <net/datalink.h>
+#include <net/psnap.h>
+#include <linux/gn.h>
+#include <linux/gn_routing.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/export.h>
+#include <linux/etherdevice.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/random.h>
+#include <linux/atomic.h>
+#include <linux/limits.h>
+#include <linux/if_ether.h>
+#include <linux/bug.h>
+
+struct datalink_proto *gn_dl;
+static const struct proto_ops gn_dgram_ops;
+
+/**************************************************************************\
+*                                                                          *
+* Handlers for the socket list.                                            *
+*                                                                          *
+\**************************************************************************/
+
+HLIST_HEAD(gn_sockets);
+DEFINE_RWLOCK(gn_sockets_lock);
+
+static inline void __gn_insert_socket(struct sock *sk)
+{
+	sk_add_node(sk, &gn_sockets);
+}
+
+static inline void gn_remove_socket(struct sock *sk)
+{
+	write_lock_bh(&gn_sockets_lock);
+	sk_del_node_init(sk);
+	write_unlock_bh(&gn_sockets_lock);
+}
+
+#define from_timer(var, callback_timer, timer_fieldname) \
+	container_of(callback_timer, typeof(*var), timer_fieldname)
+
+static void gn_destroy_timer(struct timer_list *t)
+{
+	struct sock *sk = from_timer(sk, t, sk_timer);
+
+	if (sk_has_allocations(sk)) {
+		sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME;
+		add_timer(&sk->sk_timer);
+	} else {
+		sock_put(sk);
+	}
+}
+
+static inline void gn_destroy_socket(struct sock *sk)
+{
+	gn_remove_socket(sk);
+	skb_queue_purge(&sk->sk_receive_queue);
+
+	if (sk_has_allocations(sk)) {
+		timer_setup(&sk->sk_timer, gn_destroy_timer, 0);
+		sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME;
+		add_timer(&sk->sk_timer);
+	} else {
+		sock_put(sk);
+	}
+}
+
+/**************************************************************************\
+*                                                                          *
+* Handling for system calls applied via the various interfaces to an       *
+* GeoNetworking socket object.                                             *
+*                                                                          *
+\**************************************************************************/
+
+static struct proto gn_proto = {
+	.name = "GN",
+	.owner = THIS_MODULE,
+	.obj_size = sizeof(struct gn_sock),
+};
+
+HLIST_HEAD(gn_interfaces);
+DEFINE_SPINLOCK(gn_interfaces_lock);
+
+void gn_activate_beacon(void);
+
+/**
+ * gn_iface - add device to the interface the socketaddress is bound to
+ */
+static struct gn_iface *gn_if_add_device(struct net_device *dev,
+					 struct sockaddr_gn *sa)
+{
+	bool was_empty;
+	struct gn_iface *gnif,
+		*new_gnif = kzalloc(sizeof(struct gn_iface), GFP_KERNEL);
+
+	if (!new_gnif)
+		return NULL;
+
+	new_gnif->address = sa->sgn_addr;
+	new_gnif->dev = dev;
+
+	pr_info("Add interface %s with address %llx", dev->name,
+		new_gnif->address);
+
+	spin_lock_bh(&gn_interfaces_lock);
+	hlist_for_each_entry_rcu(gnif, &gn_interfaces, hnode) {
+		if (gnif->dev == dev) {
+			// Replace existing interface address
+			hlist_replace_rcu(&gnif->hnode, &new_gnif->hnode);
+			spin_unlock_bh(&gn_interfaces_lock);
+			kfree_rcu(gnif, rcu);
+			return new_gnif;
+		}
+	}
+	was_empty = hlist_empty(&gn_interfaces);
+
+	// No existing interface address
+	hlist_add_head_rcu(&new_gnif->hnode, &gn_interfaces);
+	spin_unlock_bh(&gn_interfaces_lock);
+
+	if (was_empty)
+		gn_activate_beacon();
+
+	return new_gnif;
+}
+
+static void gn_if_drop_device(struct net_device *dev)
+{
+	struct gn_iface *gnif;
+
+	spin_lock_bh(&gn_interfaces_lock);
+	hlist_for_each_entry_rcu(gnif, &gn_interfaces, hnode) {
+		if (gnif->dev == dev) {
+			hlist_del_rcu(&gnif->hnode);
+			kfree_rcu(gnif, rcu);
+		}
+	}
+	spin_unlock_bh(&gn_interfaces_lock);
+}
+
+/*
+ * find the interface to which the socketaddress is bound
+ */
+static struct gn_iface *gn_find_interface(gn_address_t addr)
+{
+	struct gn_iface *gnif;
+	bool found = false;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(gnif, &gn_interfaces, hnode) {
+		if (gnif->address == addr) {
+			found = true;
+			break;
+		}
+	}
+	rcu_read_unlock();
+	return found ? gnif : NULL;
+}
+
+/*
+ * find an interface by the device it belongs to
+ */
+static struct gn_iface *gn_find_interface_by_dev(struct net_device *dev)
+{
+	struct gn_iface *gnif;
+	bool found = false;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(gnif, &gn_interfaces, hnode) {
+		if (gnif->dev == dev) {
+			found = true;
+			break;
+		}
+	}
+	rcu_read_unlock();
+	return found ? gnif : NULL;
+}
+
+/*
+ * A device event has occurred. Watch for devices going down and
+ * delete our use of them (iface and route).
+ */
+static int gn_device_event(struct notifier_block *this, unsigned long event,
+			   void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+	if (!net_eq(dev_net(dev), &init_net))
+		return NOTIFY_DONE;
+
+	if (dev->type != ARPHRD_ETHER)
+		return NOTIFY_DONE;
+
+	if (event == NETDEV_DOWN)
+		gn_if_drop_device(dev);
+
+	return NOTIFY_DONE;
+}
+
+/*
+ * Convert TAI timestamp in milliseconds to GN timestamp
+ */
+u32 gn_tai_to_gn(ktime_t tai_time)
+{
+	/* unix timestamp of 01/01/2004 00:00:00 UTC and 32 leap seconds between TAI and UNIX*/
+	const ktime_t tai_offset = ms_to_ktime(1072915200000LLU + 32000LLU);
+
+	WARN_ONCE(ktime_before(tai_time, tai_offset),
+		  "timestamp %lld out of bounds", tai_time);
+	/* truncate timestamp (GN timestamp has 32 bits) */
+	return (u32)ktime_sub(tai_time, tai_offset);
+}
+
+u32 gn_timestamp_now(void)
+{
+	return gn_tai_to_gn(ktime_get_clocktai());
+}
+
+/*
+ * Create a socket. Initialise the socket, blank the addresses
+ * set the state.
+ */
+static int gn_create(struct net *net, struct socket *sock, int protocol,
+		     int kern)
+{
+	struct sock *sk;
+	int rc;
+
+	rc = -EAFNOSUPPORT;
+	//if (!net_eq(net, &init_net))
+	//	goto out;
+	// TODO: find out, why necessary
+
+	rc = -ESOCKTNOSUPPORT;
+	if (sock->type != SOCK_DGRAM)
+		goto out;
+
+	if (protocol < GN_PROTO_ANY || protocol > GN_PROTO_MAX)
+		goto out;
+
+	// TODO We don't support IPv6 atm
+	if (protocol == GN_PROTO_INET6)
+		goto out;
+
+	rc = -ENOMEM;
+	sk = sk_alloc(net, PF_GN, GFP_KERNEL, &gn_proto, kern);
+	if (!sk)
+		goto out;
+	rc = 0;
+	sock->ops = &gn_dgram_ops;
+	sock_init_data(sock, sk);
+	if (protocol == GN_PROTO_ANY)
+		gn_sk(sk)->protocol = GN_PROTO_BTP_A;
+	else
+		gn_sk(sk)->protocol = protocol;
+
+	/* Checksums on by default */
+//	sock_set_flag(sk, SOCK_ZAPPED);
+out:
+	return rc;
+}
+
+/* Free a socket. No work needed */
+static int gn_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk) {
+		sock_hold(sk);
+		lock_sock(sk);
+
+		sock_orphan(sk);
+		sock->sk = NULL;
+		gn_destroy_socket(sk);
+
+		release_sock(sk);
+		sock_put(sk);
+	}
+	return 0;
+}
+
+static int gn_pick_and_bind_port(struct sock *sk, struct sockaddr_gn *sgn)
+{
+	int retval;
+
+	write_lock_bh(&gn_sockets_lock);
+
+	for (sgn->sgn_port = GNPORT_RESERVED; sgn->sgn_port < GNPORT_LAST;
+	     sgn->sgn_port++) {
+		struct sock *s;
+
+		sk_for_each(s, &gn_sockets) {
+			struct gn_sock *gn = gn_sk(s);
+
+			if (gn->src_port == sgn->sgn_port &&
+			    gn->src_addr == sgn->sgn_addr)
+				goto try_next_port;
+		}
+
+		/* Wheee, it's free, assign and insert. */
+		__gn_insert_socket(sk);
+		gn_sk(sk)->src_port = sgn->sgn_port;
+		retval = 0;
+		goto out;
+
+try_next_port:;
+	}
+
+	retval = -EBUSY;
+out:
+	write_unlock_bh(&gn_sockets_lock);
+	return retval;
+}
+
+static struct sock *gn_find_or_insert_socket(struct sock *sk,
+					     struct sockaddr_gn *sgn)
+{
+	struct sock *s;
+	struct gn_sock *gn;
+
+	write_lock_bh(&gn_sockets_lock);
+	sk_for_each(s, &gn_sockets) {
+		gn = gn_sk(s);
+
+		if (gn->src_port == sgn->sgn_port)
+			goto found;
+	}
+	s = NULL;
+	gn = gn_sk(sk);
+	gn->src_addr = sgn->sgn_addr;
+	gn->src_port = sgn->sgn_port;
+	pr_info("gn: Add socket addr=%llx port=%d", gn->src_addr,
+		gn->src_port);
+	__gn_insert_socket(sk); /* Wheee, it's free, assign and insert. */
+found:
+	write_unlock_bh(&gn_sockets_lock);
+	return s;
+}
+
+static int gn_autobind(struct sock *sock)
+{
+	//BUG();
+	return -1;
+}
+
+/* Set the address 'our end' of the connection */
+static int gn_bind(struct socket *sock, struct sockaddr_unsized *uaddr,
+		   int addr_len)
+{
+	// struct sockaddr_gn *addr = (struct sockaddr_gn *)uaddr; TODO: remove this legacy line
+	DECLARE_SOCKADDR(struct sockaddr_gn *, addr, uaddr);
+	struct sock *sk = sock->sk;
+	struct gn_sock *gn = gn_sk(sk);
+	int err;
+
+	if (!sock_flag(sk, SOCK_ZAPPED) ||
+	    addr_len != sizeof(struct sockaddr_gn))
+		return -EINVAL;
+
+	if (addr->sgn_family != AF_GN)
+		return -EAFNOSUPPORT;
+
+	lock_sock(sk);
+	// FIXME: Ensure that addr->sgn_addr belongs to one of our interfaces
+	gn->src_addr = addr->sgn_addr;
+
+	if (addr->sgn_port == GNPORT_ANY) {
+		err = gn_pick_and_bind_port(sk, addr);
+
+		if (err < 0)
+			goto out;
+	} else {
+		err = -EADDRINUSE;
+		if (gn_find_or_insert_socket(sk, addr))
+			goto out;
+	}
+	sock_reset_flag(sk, SOCK_ZAPPED);
+	err = 0;
+
+out:
+	release_sock(sk);
+	return err;
+}
+
+/* Set the address we talk to */
+static int gn_connect(struct socket *sock, struct sockaddr_unsized *uaddr,
+		      int addr_len, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct gn_sock *gn = gn_sk(sk);
+	struct sockaddr_gn *addr;
+	int err;
+
+	sk->sk_state = TCP_CLOSE;
+	sock->state = SS_UNCONNECTED;
+
+	if (addr_len != sizeof(*addr))
+		return -EINVAL;
+
+	addr = (struct sockaddr_gn *)uaddr;
+
+	if (addr->sgn_family != AF_GN)
+		return -EAFNOSUPPORT;
+
+	lock_sock(sk);
+	err = -EBUSY;
+	if (sock_flag(sk, SOCK_ZAPPED))
+		if (gn_autobind(sk) < 0)
+			goto out;
+
+	//TODO: Routing
+
+	gn->dst_port = addr->sgn_port;
+	gn->dst_addr = addr->sgn_addr;
+
+	sock->state = SS_CONNECTED;
+	sk->sk_state = TCP_ESTABLISHED;
+	err = 0;
+out:
+	release_sock(sk);
+	return err;
+}
+
+static struct sock *gn_search_socket(struct sockaddr_gn *tosgn,
+				     struct gn_iface *gnif)
+{
+	struct sock *s;
+	struct gn_sock *gn;
+
+	read_lock_bh(&gn_sockets_lock);
+	sk_for_each(s, &gn_sockets) {
+		gn = gn_sk(s);
+
+		if (gn->src_port != tosgn->sgn_port)
+			continue;
+		// TODO ????
+		if (gnif == NULL || gn->src_addr == gnif->address)
+			goto out;
+	}
+	s = NULL;
+out:
+	read_unlock_bh(&gn_sockets_lock);
+	return s;
+}
+
+/*
+static int gn_dupl_addr_detect(unsigned long long local_llc, gn_address_t local_addr,
+				unsigned long long rcv_llc, gn_address_t rcv_addr)
+{
+	return 0;
+}
+*/
+
+static u16 gn_if_next_sn(struct gn_iface *gnif)
+{
+	return atomic_inc_return(&gnif->local_sn) % USHRT_MAX;
+}
+
+void gn_fill_sopv(struct gn_iface *gnif, struct gn_lpv *sopv, gn_address_t addr)
+{
+	// TODO Speed/Heading
+
+	ktime_t tst = timespec64_to_ktime(gnif->pos.tst);
+	// fill empty timestamp with current timestamp
+	if (tst == 0)
+		sopv->tst = cpu_to_be32(gn_timestamp_now());
+	else
+		sopv->tst = cpu_to_be32(gn_tai_to_gn(tst));
+	sopv->lon = cpu_to_be32(gnif->pos.coord.lon);
+	sopv->lat = cpu_to_be32(gnif->pos.coord.lat);
+	sopv->addr = addr;
+	sopv->spai = cpu_to_be16(FIELD_PREP(GN_LPV_PAI, 0) |
+				 FIELD_PREP(GN_LPV_S, 0));
+	sopv->h = cpu_to_be16(0);
+}
+
+static void gn_fill_bh_ch(struct gn_iface *gnif, struct gn_header *gh,
+			  u8 packet_type, u8 packet_subtype, u8 rhl,
+			  u8 next_header, u16 payload_size)
+{
+	const u8 mhl = DEFAULT_HOP_LIMIT;
+	/* rhl should never be greater than mhl */
+	if (WARN_ON(rhl > mhl))
+		rhl = DEFAULT_HOP_LIMIT;
+
+	gh->gb_h.version = GN_VERSION;
+	gh->gb_h.nh = BH_NH_COMMON_HEADER;
+	gh->gb_h.reserved = 0;
+	gh->gb_h.lt = BH_LT_DEFAULT;
+	gh->gb_h.rhl = rhl;
+
+	gh->gc_h.nh = next_header;
+	gh->gc_h.reserved = 0;
+	gh->gc_h.reserved2 = 0;
+	gh->gc_h.ht = packet_type;
+	gh->gc_h.hst = packet_subtype;
+	gh->gc_h.tc = CH_TC_DEFAULT;
+	gh->gc_h.pl = payload_size;
+	if (packet_type == CH_HT_BEACON)
+		gh->gc_h.mhl = BEACON_MHL;
+	else
+		gh->gc_h.mhl = DEFAULT_HOP_LIMIT;
+
+	if (gnif->pos.flags & GN_POSITION_MOBILE)
+		gh->gc_h.flags = CH_FLAG_MOBILE;
+	else
+		gh->gc_h.flags = 0;
+}
+
+static void gn_fill_bh_ch_nopayload(struct gn_iface *gnif,
+				    struct gn_header *gn_h, u8 packet_type,
+				    u8 packet_subtype, u8 rhl)
+{
+	gn_fill_bh_ch(gnif, gn_h, packet_type, packet_subtype, rhl, CH_NH_ANY,
+		      0);
+}
+
+static void gn_location_service_req(struct gn_iface *gnif, gn_address_t saddr,
+				    gn_address_t daddr)
+{
+	int size = 0;
+	struct sk_buff *skb;
+	struct gn_basic_header *gb_h;
+	struct gn_common_header *gc_h;
+	struct gn_ls_request_header *gls_h;
+
+	size += gn_dl->header_length;
+	size += gnif->dev->hard_header_len;
+	size += sizeof(struct gn_basic_header);
+	size += sizeof(struct gn_common_header);
+	size += sizeof(struct gn_ls_request_header);
+
+	skb = netdev_alloc_skb(gnif->dev, size);
+	skb_reserve(skb, gn_dl->header_length);
+	skb_reserve(skb, gnif->dev->hard_header_len);
+	skb_reserve(skb, sizeof(struct gn_basic_header));
+	skb_reserve(skb, sizeof(struct gn_common_header));
+	skb_reserve(skb, sizeof(struct gn_ls_request_header));
+
+	gls_h = skb_push(skb, sizeof(struct gn_ls_request_header));
+	gc_h = skb_push(skb, sizeof(struct gn_common_header));
+	gb_h = skb_push(skb, sizeof(struct gn_basic_header));
+
+	gn_fill_bh_ch_nopayload(gnif, (struct gn_header *)gb_h, CH_HT_LS,
+				CH_HST_LS_REQUEST, DEFAULT_HOP_LIMIT);
+
+	gls_h->sn = cpu_to_be16(gn_if_next_sn(gnif));
+	gls_h->reserved = 0;
+	gls_h->addr = daddr;
+	gn_fill_sopv(gnif, &gls_h->sopv, saddr);
+
+	gn_dl->request(gn_dl, skb, gnif->dev->broadcast);
+}
+
+static void gn_location_service_reply(struct gn_spv *depv,
+				      struct gn_iface *gnif, gn_address_t addr,
+				      u64 llc)
+{
+	int size;
+	struct sk_buff *skb;
+	struct gn_basic_header *gb_h;
+	struct gn_common_header *gc_h;
+	struct gn_ls_reply_header *gls_h;
+
+	size = 0;
+	size += gn_dl->header_length;
+	size += gnif->dev->hard_header_len;
+	size += sizeof(struct gn_basic_header);
+	size += sizeof(struct gn_common_header);
+	size += sizeof(struct gn_ls_reply_header);
+
+	skb = netdev_alloc_skb(gnif->dev, size);
+	skb_reserve(skb, gn_dl->header_length);
+	skb_reserve(skb, gnif->dev->hard_header_len);
+	skb_reserve(skb, sizeof(struct gn_basic_header));
+	skb_reserve(skb, sizeof(struct gn_common_header));
+	skb_reserve(skb, sizeof(struct gn_ls_reply_header));
+
+	gls_h = skb_push(skb, sizeof(struct gn_ls_reply_header));
+	gc_h = skb_push(skb, sizeof(struct gn_common_header));
+	gb_h = skb_push(skb, sizeof(struct gn_basic_header));
+
+	gn_fill_bh_ch_nopayload(gnif, (struct gn_header *)gb_h, CH_HT_LS,
+				CH_HST_LS_REPLY, DEFAULT_HOP_LIMIT);
+
+	gls_h->sn = cpu_to_be16(gn_if_next_sn(gnif));
+	gls_h->reserved = 0;
+	gn_fill_sopv(gnif, &gls_h->sopv, addr);
+	memcpy(&gls_h->depv, depv, sizeof(*depv));
+
+	gn_dl->request(gn_dl, skb, gnif->dev->broadcast);
+}
+
+static int gn_pass_payload_sock(struct sockaddr_gn *tosgn, struct sk_buff *skb)
+{
+	struct sock *sock;
+
+	sock = gn_search_socket(tosgn, NULL);
+	if (!sock)
+		return NET_RX_DROP;
+	if (sock_queue_rcv_skb(sock, skb) < 0)
+		return NET_RX_DROP;
+	return NET_RX_SUCCESS;
+}
+
+static int gn_process_guc_packet(struct sk_buff *skb)
+{
+	struct gn_header *gh;
+	struct btp_header *btp_h;
+	struct sockaddr_gn tosgn;
+	struct gn_iface *gnif;
+
+	gh = (struct gn_header *)skb_network_header(skb);
+	GN_SET_BTP(skb, btp_h, struct gn_guc_header);
+
+	skb_reset_transport_header(skb);
+
+	tosgn.sgn_family = PF_GN;
+	tosgn.sgn_addr = gh->guc_h.depv.addr;
+	tosgn.sgn_port = be16_to_cpu(btp_h->dst_port);
+
+	if (gn_update_location_table(&gh->guc_h.sopv, false, NULL,
+				     &gh->guc_h.sn))
+		goto drop;
+
+	gnif = gn_find_interface(tosgn.sgn_addr);
+	if (!gnif)
+		goto drop;
+
+	if (gn_pass_payload_sock(&tosgn, skb) != NET_RX_SUCCESS)
+		goto drop;
+
+	return NET_RX_SUCCESS;
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static u8 gn_decode_shape(u8 hst)
+{
+	switch (hst) {
+	case CH_HST_GABC_CIRCLE:
+		return GN_SHAPE_CIRCLE;
+	case CH_HST_GABC_RECT:
+		return GN_SHAPE_RECTANGLE;
+	case CH_HST_GABC_ELIP:
+		return GN_SHAPE_ELLIPSE;
+	default:
+		// invalid shape
+		return GN_SHAPE_UNSPECIFIED;
+	};
+}
+
+static u8 gn_encode_shape(u8 shape)
+{
+	switch (shape) {
+	case GN_SHAPE_CIRCLE:
+		return CH_HST_GABC_CIRCLE;
+	case GN_SHAPE_RECTANGLE:
+		return CH_HST_GABC_RECT;
+	case GN_SHAPE_ELLIPSE:
+		return CH_HST_GABC_ELIP;
+	default:
+		return CH_HST_UNSPECIFIED;
+	};
+}
+
+static struct gn_geo_scope gn_decode_geo_scope(struct gn_header *gh)
+{
+	struct gn_geo_scope scope = {
+		.a = be16_to_cpu(gh->gbc_h.da),
+		.b = be16_to_cpu(gh->gbc_h.db),
+		.angle = be16_to_cpu(gh->gbc_h.angle),
+		.coord.lat = be32_to_cpu(gh->gbc_h.gap_lat),
+		.coord.lon = be32_to_cpu(gh->gbc_h.gap_lon),
+		.shape = gn_decode_shape(gh->gc_h.hst),
+	};
+	return scope;
+}
+
+static int gn_process_gxc_packet(struct sk_buff *skb)
+{
+	struct gn_header *gh;
+	struct btp_header *btp_h;
+	struct sockaddr_gn tosgn;
+	struct gn_iface *gnif;
+	s64 f_value;
+	struct gn_geo_scope scope;
+	bool run_dpd;
+	unsigned long long next_hop_addr;
+
+	next_hop_addr = GN_BROADCAST_ADDR;
+
+	gh = (struct gn_header *)skb_network_header(skb);
+	GN_SET_BTP(skb, btp_h, struct gn_gxc_header);
+
+	tosgn.sgn_family = PF_GN;
+	tosgn.sgn_addr = gh->gbc_h.sopv.addr;
+	tosgn.sgn_port = be16_to_cpu(btp_h->dst_port);
+
+	// FIXME This is not really the right place to find the interface
+	gnif = gn_find_interface_by_dev(skb->dev);
+
+	scope = gn_decode_geo_scope(gh);
+	if (scope.shape == GN_SHAPE_UNSPECIFIED)
+		goto drop;
+
+	f_value = gn_F(gnif->pos.coord, scope);
+
+	if (f_value >= 0) {
+		// GeoAdhoc router is outside specified area
+		switch (GN_AREA_FORWARDING) {
+		case GN_AREA_FORWARDING_UNSPECIFIED:
+		case GN_AREA_FORWARDING_SIMPLE:
+			run_dpd = true;
+			break; //TODO: validate still working
+		default:
+			run_dpd = false;
+			break; //TODO: validate still working
+		}
+	} else {
+		// GeoAdhoc router is inside specified area
+		switch (GN_NON_AREA_FORWARDING) {
+		case GN_NON_AREA_FORWARDING_UNSPECIFIED:
+		case GN_NON_AREA_FORWARDING_GREEDY:
+			run_dpd = true;
+			break; //TODO: validate still working
+		default:
+			run_dpd = false;
+		}
+	}
+	if (gn_update_location_table(&gh->gbc_h.sopv, false, NULL,
+				     run_dpd ? &gh->gbc_h.sn : NULL))
+		goto drop;
+	// Forwarding
+	if (gh->gb_h.rhl > 0 && ((gh->gc_h.ht == CH_HT_GAC && f_value >= 0) ||
+				 gh->gc_h.ht == CH_HT_GBC)) {
+		struct sk_buff *forward_skb;
+		struct gn_basic_header *fwd_gb_h;
+		u8 dest_addr[ETH_ALEN];
+		int rc;
+
+		forward_skb = skb_copy(skb, GFP_ATOMIC);
+		if (!forward_skb) {
+			pr_warn("dropping packet");
+			goto drop;
+		}
+		fwd_gb_h = (struct gn_basic_header *)skb_transport_header(forward_skb);
+		fwd_gb_h->rhl--;
+
+		rc = gn_gxc_forward(gnif, f_value, dest_addr, &gh->gbc_h.sopv);
+		switch (rc) {
+		case GN_FORWARD_NEXT_HOP:
+			gn_dl->request(gn_dl, forward_skb, dest_addr);
+			break;
+		case GN_FORWARD_BROADCAST:
+			gn_dl->request(gn_dl, forward_skb, skb->dev->broadcast);
+			break;
+		case GN_FORWARD_BUFFER:
+			pr_warn("forwarding buffers not implemented");
+			break;
+		case GN_FORWARD_DISCARD:
+			break;
+		default:
+			WARN_ONCE(1, "internal error: unexpected return value");
+			goto drop;
+		}
+	}
+
+	// Local delivery
+	if (f_value >= 0 &&
+	    gn_pass_payload_sock(&tosgn, skb) != NET_RX_SUCCESS) {
+		goto drop;
+	}
+
+	return NET_RX_SUCCESS;
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static int gn_process_shb_packet(struct sk_buff *skb, const u8 *ll_address)
+{
+	struct gn_header *gh;
+	struct btp_header *btp_h;
+	struct sockaddr_gn tosgn;
+
+	gh = (struct gn_header *)skb_network_header(skb);
+	GN_SET_BTP(skb, btp_h, struct gn_shb_header);
+
+	// TODO 3. execute DAD
+
+	// 4. update PV in the LocTE
+	if (gn_update_location_table(&gh->shb_h.sopv, true, ll_address, NULL))
+		goto drop;
+
+	// 7. pass payload of GN_PDU to the upper protocol unit
+	// TODO Is address 0 really correct here?
+	tosgn.sgn_family = PF_GN;
+	tosgn.sgn_addr = 0;
+	tosgn.sgn_port = be16_to_cpu(btp_h->dst_port);
+
+	if (gn_pass_payload_sock(&tosgn, skb) != NET_RX_SUCCESS)
+		goto drop;
+
+	// TODO 8. flush packet buffers
+
+	return NET_RX_SUCCESS;
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static int gn_process_tsb_packet(struct sk_buff *skb)
+{
+	struct gn_header *gh;
+	struct btp_header *btp_h;
+	struct sockaddr_gn tosgn;
+
+	gh = (struct gn_header *)skb_network_header(skb);
+	GN_SET_BTP(skb, btp_h, struct gn_tsb_header);
+
+	// TODO 3. execute DAD
+
+	if (gn_update_location_table(&gh->tsb_h.sopv, false, NULL,
+				     &gh->tsb_h.sn))
+		goto drop;
+
+	if (gh->gb_h.rhl > 0) {
+		// Forward packet
+		struct sk_buff *forward_skb;
+		struct gn_header *fwd_gh;
+
+		forward_skb = skb_copy(skb, GFP_ATOMIC);
+		if (!forward_skb) {
+			pr_warn("Dropping packet");
+			goto drop;
+		}
+
+		fwd_gh = (struct gn_header *)skb_network_header(forward_skb);
+		fwd_gh->gb_h.rhl--;
+
+		gn_dl->request(gn_dl, forward_skb, skb->dev->broadcast);
+	}
+
+	// 7. pass payload of GN_PDU to the upper protocol unit
+	tosgn.sgn_family = PF_GN;
+	tosgn.sgn_addr = 0;
+	tosgn.sgn_port = be16_to_cpu(btp_h->dst_port);
+
+	if (gn_pass_payload_sock(&tosgn, skb) != NET_RX_SUCCESS)
+		goto drop;
+
+	// TODO 8. flush packet buffers
+	//   --> flush ls-buffer & uc/bc-buffer
+
+	return NET_RX_SUCCESS;
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static int gn_process_beacon_packet(struct sk_buff *skb, const u8 *llc)
+{
+	struct gn_header *gh = (struct gn_header *)skb_network_header(skb);
+
+	if (gn_update_location_table(&gh->beacon_h.sopv, true, llc, NULL)) {
+		pr_info("LocT update failure");
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	} else {
+		return NET_RX_SUCCESS;
+	}
+}
+
+static int gn_process_ls_packet(struct sk_buff *skb)
+{
+	struct gn_header *gh;
+	gn_address_t dest_addr;
+	struct gn_iface *gnif;
+
+	gh = (struct gn_header *)skb_network_header(skb);
+
+	if (gh->gc_h.hst == CH_HST_LS_REQUEST) {
+		struct gn_ls_request_header *gls_req_h = &gh->ls_request_h;
+
+		// 2. execute dad
+		// 3. update loc_te
+		dest_addr = gls_req_h->addr;
+		if (gn_update_location_table(&gls_req_h->sopv, false, NULL,
+					     &gls_req_h->sn))
+			goto drop;
+		// 4. find out if the packet has to be answered or forwarded
+		gnif = gn_find_interface(dest_addr);
+		if (gnif) {
+			// has to be answered
+			// FIXME Dubious cast
+			gn_location_service_reply((struct gn_spv *)
+				&gls_req_h->sopv, gnif, dest_addr, 0);
+		} else {
+			// has to be forwarded like a tsb
+			//5. try to flush own forward buffer
+			gn_ls_flush(gls_req_h->sopv.addr);
+			//6. forward like a tsb - (omitted atm, since forwarding of tsb
+			// is processed on another branch)
+		}
+	} else if (gh->gc_h.hst == CH_HST_LS_REPLY) {
+		struct gn_ls_reply_header *gls_rep_h = &gh->ls_reply_h;
+		// 2. execute dad
+		// 3. update loc_te
+		if (gn_update_location_table(&gls_rep_h->sopv, false, NULL,
+					     &gls_rep_h->sn))
+			goto drop;
+		dest_addr = gls_rep_h->depv.addr;
+
+		//4. flush forward buffer
+		gn_ls_flush(gls_rep_h->sopv.addr);
+		//5. find out if the packet has to be forwarded
+		if (!gn_find_interface(dest_addr)) {
+			// FIXME Forwarding
+			; //Packet is not for this router, it has to be forwarded like a guc
+			//omitted atm, since F(x,y) needed
+		}
+	} else {
+		WARN_ONCE(1, "called gn_process_ls_packet on non-LS packet");
+		goto drop;
+	}
+	kfree_skb(skb);
+	return NET_RX_SUCCESS;
+drop:
+	pr_info("Packet was dropped.");
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+/**	gn_rcv() - Receive a packet (in skb) from device dev
+ *	@skb - packet received
+ *	@dev - network device where the packet comes from
+ *	@pt - packet type
+ *
+ *	Receive a packet (in skb) from device dev. This has come from the SNAP
+ *	decoder, and on entry skb->network_header is the GN Basic Header.
+ *	The physical headers have been extracted.
+ */
+static int gn_rcv(struct sk_buff *skb, struct net_device *dev,
+		  struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct gn_header *gh;
+	struct ethhdr *eth;
+
+	if (!net_eq(dev_net(dev), &init_net))
+		goto drop;
+
+	if (dev->type != ARPHRD_ETHER)
+		goto drop;
+
+	skb = skb_share_check(skb, GFP_ATOMIC);
+
+	if (!skb)
+		goto drop;
+
+	eth = (struct ethhdr *)skb_mac_header(skb);
+	skb_reset_network_header(skb);
+
+	if (!pskb_may_pull(skb, GN_BASE_HEADER_SIZE))
+		goto drop;
+
+	// Basic Header processing
+	gh = (struct gn_header *)skb_network_header(skb);
+
+	if (gh->gb_h.version != GN_VERSION ||
+	    gh->gb_h.nh != BH_NH_COMMON_HEADER) {
+		pr_warn("corrupt packet");
+		goto drop;
+	}
+
+	/*
+	 * Retrieve information from socketbuffer an insert it into according headers;
+	 * this is necessary to determine if the packet is ours and find the matching socket,
+	 * or if the packet has to be forwarded
+	 */
+	// Common Header processing
+
+	//skb_trim(skb, min_t(unsigned int, skb->len, gc_h->pl + sizeof(*gb_h) + sizeof(*gc_h)));
+
+	if (gh->gc_h.mhl < gh->gb_h.rhl)
+		goto drop;
+
+	switch (gh->gc_h.ht) {
+	case CH_HT_GUC:
+		return gn_process_guc_packet(skb);
+	case CH_HT_GAC:
+	case CH_HT_GBC:
+		return gn_process_gxc_packet(skb);
+	case CH_HT_TSB:
+		if (gh->gc_h.hst == CH_HST_TSB_SINGLE_HOP)
+			return gn_process_shb_packet(skb, eth->h_source);
+		else if (gh->gc_h.hst == CH_HST_TSB_MULTI_HOP)
+			return gn_process_tsb_packet(skb);
+		else
+			goto drop;
+		break;
+	case CH_HT_BEACON:
+		return gn_process_beacon_packet(skb, eth->h_source);
+	case CH_HT_LS:
+		return gn_process_ls_packet(skb);
+	default:
+		goto drop;
+	}
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+static int gn_fill_guc_header(struct gn_guc_header *guc_h,
+			      struct gn_iface *gnif, gn_address_t dest_addr,
+			      struct gn_spv *depv)
+{
+	memset(guc_h, 0, sizeof(*guc_h));
+	//TODO: fill with data
+	guc_h->sn = cpu_to_be16(gn_if_next_sn(gnif));
+	gn_fill_sopv(gnif, &guc_h->sopv, gnif->address);
+	guc_h->depv.addr = dest_addr;
+	guc_h->depv.tst = htonl(0);
+	guc_h->depv.lat = htonl(0);
+	guc_h->depv.lon = htonl(0);
+	return 0;
+}
+
+static int gn_fill_gxc_header(struct gn_gxc_header *gxc_h,
+			      struct gn_iface *gnif, struct gn_sock *gn)
+{
+	WARN_ON_ONCE(gn->scope.scope_type != GN_SCOPE_GEOGRAPHICAL &&
+		     gn->scope.scope_type != GN_SCOPE_GEOGRAPHICAL_ANYCAST);
+
+	memset(gxc_h, 0, sizeof(*gxc_h));
+	gxc_h->sn = cpu_to_be16(gn_if_next_sn(gnif));
+	gn_fill_sopv(gnif, &gxc_h->sopv, gnif->address);
+
+	gxc_h->gap_lat = cpu_to_be32(gn->scope.geo_scope.coord.lat);
+	gxc_h->gap_lon = cpu_to_be32(gn->scope.geo_scope.coord.lon);
+	gxc_h->angle = cpu_to_be16(gn->scope.geo_scope.angle);
+	switch (gn->scope.geo_scope.shape) {
+	case GN_SHAPE_CIRCLE:
+		gxc_h->da = cpu_to_be16(gn->scope.geo_scope.a);
+		break;
+	case GN_SHAPE_ELLIPSE:
+	case GN_SHAPE_RECTANGLE:
+		gxc_h->da = cpu_to_be16(gn->scope.geo_scope.a);
+		gxc_h->db = cpu_to_be16(gn->scope.geo_scope.b);
+		break;
+	default:
+	case GN_SHAPE_UNSPECIFIED:
+		WARN_ONCE(1, "unknown shape type");
+		break;
+	}
+	return 0;
+}
+
+static int gn_fill_shb_header(struct gn_shb_header *shb_h,
+			      struct gn_iface *gnif)
+{
+	memset(shb_h, 0, sizeof(*shb_h));
+	gn_fill_sopv(gnif, &shb_h->sopv, gnif->address);
+
+	// prefill media dependent data field with empty
+	shb_h->mdd = htonl(0);
+	return 0;
+}
+
+static int gn_fill_tsb_header(struct gn_tsb_header *tsb_h,
+			      struct gn_iface *gnif)
+{
+	// FIXME gn_fill_sopv doesn't actuall fill anything
+	// (We need to set the sopv based on the socket)
+	memset(tsb_h, 0, sizeof(*tsb_h));
+	tsb_h->sn = cpu_to_be16(gn_if_next_sn(gnif));
+	gn_fill_sopv(gnif, &tsb_h->sopv, gnif->address);
+	return 0;
+}
+
+/**
+ * gn_sendmsg - always called if sendmsg() is called on a socket with an address
+ * family matching AF_GN
+ * @msghdr - contains information, including the payload of the packet
+ * @len - the size of the payload
+ */
+static int gn_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+	int packet_type, packet_subtype, btp_type;
+	int err = -EINVAL;
+	struct sock *sk = sock->sk;
+	struct gn_sock *gn = gn_sk(sk);
+
+	/*
+	 * usgn will contain address and port of the destination
+	 */
+	DECLARE_SOCKADDR(struct sockaddr_gn *, usgn, msg->msg_name);
+	int flags = msg->msg_flags;
+	struct sockaddr_gn local_sgn;
+	struct sk_buff *skb;
+	struct net_device *dev;
+	struct btp_header *btp_h;
+	void *gp_h;
+	struct gn_common_header *gc_h;
+	struct gn_basic_header *gb_h;
+	struct gn_iface *gnif;
+	u32 size;
+	u32 eh_size;
+	u8 rhl;
+	struct gn_spv depv = { 0 };
+
+	if (flags & ~(MSG_DONTWAIT | MSG_CMSG_COMPAT)) {
+		pr_warn("unsupported sendmsg flags");
+		return -EINVAL;
+	}
+
+	if (len > GN_MAXSZ) {
+		pr_err("message too long (got %zu, maximum %d)", len, GN_MAXSZ);
+		return -EMSGSIZE;
+	}
+	if (usgn) {
+		err = -EBUSY;
+		if (sock_flag(sk, SOCK_ZAPPED)) {
+			pr_err("socket zapped and autobind not implemented");
+			if (gn_autobind(sk) < 0)
+				goto out;
+		}
+		err = -EINVAL;
+		if (msg->msg_namelen < sizeof(*usgn) ||
+		    usgn->sgn_family != AF_GN) {
+			pr_info("incorrect address family");
+			goto out;
+		}
+		//appletalk makes another check here
+	} else {
+		err = -ENOTCONN;
+		if (sk->sk_state != TCP_ESTABLISHED)
+			goto out;
+		usgn = &local_sgn;
+		usgn->sgn_family = AF_GN;
+		usgn->sgn_port = gn->dst_port;
+		usgn->sgn_addr = gn->dst_addr;
+	}
+	/*
+	 * Before the packet can be send, we have determine if the gn_address of the source
+	 * is bound to an interface. The interface provides the device for sending the packet.
+	 */
+	gnif = gn_find_interface(gn->src_addr);
+
+	if (!gnif) {
+		pr_info("Could not find an interface");
+		err = -EFAULT;
+		goto out;
+	}
+	dev = gnif->dev;
+
+	release_sock(sk);
+
+	packet_subtype = CH_HST_UNSPECIFIED;
+	if (0 /* is usgn unicast address? */) {
+		packet_type = CH_HT_GUC;
+	} else {
+		switch (gn->scope.scope_type) {
+		case GN_SCOPE_GEOGRAPHICAL:
+			packet_type = CH_HT_GBC;
+			packet_subtype =
+				gn_encode_shape(gn->scope.geo_scope.shape);
+			break;
+		case GN_SCOPE_GEOGRAPHICAL_ANYCAST:
+			packet_type = CH_HT_GAC;
+			packet_subtype =
+				gn_encode_shape(gn->scope.geo_scope.shape);
+			break;
+		case GN_SCOPE_TOPOLOGICAL:
+			packet_type = CH_HT_TSB;
+			if (gn->scope.topo_hops <= 1)
+				packet_subtype = CH_HST_TSB_SINGLE_HOP;
+			else
+				packet_subtype = CH_HST_TSB_MULTI_HOP;
+			break;
+		case GN_SCOPE_UNSPECIFIED:
+			packet_type = CH_HT_TSB;
+			packet_subtype = CH_HST_TSB_SINGLE_HOP;
+			break;
+		default:
+			WARN_ONCE(1, "internal error");
+			return -EINVAL;
+		}
+	}
+
+	switch (gn->protocol) {
+	case GN_PROTO_BTP_A:
+		btp_type = CH_NH_BTPA;
+		break;
+	case GN_PROTO_BTP_B:
+		btp_type = CH_NH_BTPB;
+		break;
+	default:
+		WARN_ONCE(1, "internal error");
+		return -EINVAL;
+	}
+
+	// Determine extended (header type specific) header size
+	switch (packet_type) {
+	case CH_HT_GUC:
+		eh_size = sizeof(struct gn_guc_header);
+		break;
+	case CH_HT_GAC:
+	case CH_HT_GBC:
+		eh_size = sizeof(struct gn_gxc_header);
+		break;
+	case CH_HT_TSB:
+		if (packet_subtype == CH_HST_TSB_SINGLE_HOP) {
+			eh_size = sizeof(struct gn_shb_header);
+		} else if (packet_subtype == CH_HST_TSB_MULTI_HOP) {
+			eh_size = sizeof(struct gn_tsb_header);
+		} else {
+			WARN_ONCE(1, "internal error");
+			return -EINVAL;
+		}
+		break;
+	default:
+		WARN_ONCE(1, "internal error");
+		return -EINVAL;
+	}
+
+	/*
+	 * Find out, how much memory has to be allocated for the packet and allocate it.
+	 */
+	size = gn_dl->header_length;
+	size += dev->hard_header_len;
+	size += sizeof(struct gn_basic_header);
+	size += sizeof(struct gn_common_header);
+	size += eh_size;
+	size += sizeof(struct btp_header);
+	size += len;
+
+	skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err);
+	lock_sock(sk);
+	if (!skb) {
+		err = -ENOMEM;
+		goto out;
+	}
+	/*
+	 * Reserve memory in the socketbuffer for datalink, hardware and our headers
+	 */
+	skb_reserve(skb, gn_dl->header_length);
+	skb_reserve(skb, dev->hard_header_len);
+	skb_reserve(skb, sizeof(struct gn_basic_header));
+	skb_reserve(skb, sizeof(struct gn_common_header));
+	skb_reserve(skb, eh_size);
+	skb_reserve(skb, sizeof(struct btp_header));
+
+	skb->dev = dev;
+	/*
+	 * Copy userdata from socket into the payload of the packet
+	 */
+	err = memcpy_from_msg(skb_put(skb, len), msg, len);
+	if (err) {
+		pr_info("could not extract from msg");
+		kfree_skb(skb);
+		err = -EFAULT;
+		goto out;
+	}
+
+	/*
+	 * Get positions of headers and fill them
+	 */
+	btp_h = skb_push(skb, sizeof(struct btp_header));
+	gp_h = skb_push(skb, eh_size);
+	gc_h = skb_push(skb, sizeof(struct gn_common_header));
+	gb_h = skb_push(skb, sizeof(struct gn_basic_header));
+
+	switch (btp_type) {
+	case CH_NH_BTPA:
+		btp_h->src_port = htons(gn->src_port);
+		btp_h->dst_port = htons(usgn->sgn_port);
+		break;
+	case CH_NH_BTPB:
+		btp_h->dst_port = htons(usgn->sgn_port);
+		btp_h->src_port = 0;
+		break;
+	default:
+		WARN_ONCE(1, "internal error");
+		err = -EINVAL;
+		goto out;
+	}
+
+	switch (packet_type) {
+	case CH_HT_GUC:
+		gn_fill_guc_header((struct gn_guc_header *)gp_h, gnif,
+				   usgn->sgn_addr, &depv);
+		break;
+	case CH_HT_GAC:
+	case CH_HT_GBC:
+		gn_fill_gxc_header((struct gn_gxc_header *)gp_h, gnif, gn);
+		break;
+	case CH_HT_TSB:
+		if (packet_subtype == CH_HST_TSB_SINGLE_HOP) {
+			rhl = 1;
+			gn_fill_shb_header((struct gn_shb_header *)gp_h, gnif);
+		} else if (packet_subtype == CH_HST_TSB_MULTI_HOP) {
+			//at this point it is safe to assume that a topological scope is used
+			rhl = gn->scope.topo_hops;
+			gn_fill_tsb_header((struct gn_tsb_header *)gp_h, gnif);
+		} else {
+			WARN_ONCE(1, "internal error");
+			err = -EINVAL;
+			goto out;
+		}
+		break;
+	}
+
+	// FIXME btp_type
+	gn_fill_bh_ch(gnif, (struct gn_header *)gb_h, packet_type,
+		      packet_subtype, rhl, btp_type,
+		      htons(len + sizeof(struct btp_header)));
+
+	// TODO Properly fill DEPV when sending GUC
+	if (packet_type == CH_HT_GUC) {
+		u8 ll_address[ETH_ALEN];
+		int queue_rc = gn_ls_queue(usgn->sgn_addr, skb);
+
+		switch (queue_rc) {
+		case GN_QUEUE_LS_STALE:
+			// We need to perform a LS request
+			gn_location_service_req(gnif, gn->src_addr,
+						usgn->sgn_addr);
+			break;
+		case GN_QUEUE_LS_PENDING:
+			// LS request is pending, we're done
+			break;
+		case GN_QUEUE_DIRECT:
+			// The destination is known, we can send the packet directly
+			if (gn_query_ll_address(usgn->sgn_addr, ll_address)) {
+				gn_dl->request(gn_dl, skb, dev->broadcast);
+			} else {
+				// FIXME Use actual next hop address
+				gn_dl->request(gn_dl, skb, ll_address);
+			}
+			break;
+		case GN_QUEUE_ERROR:
+		default:
+			err = -ENOMEM;
+			goto out;
+		}
+	} else {
+		// Not a unicast packet
+		gn_dl->request(gn_dl, skb, dev->broadcast);
+	}
+
+	//TODO: Route depv
+
+	//	gn_fill_depv(&depv, sgn_addr.s_mid);
+
+	//gn_dl->request(gn_dl, skb, (char *)&usgn->sgn_addr.s_mid);
+
+	err = 0;
+out:
+	release_sock(sk);
+	return err;
+}
+
+/**
+ * gn_recvmsg - called, when recv() is called on a socket with AF = AF_GN.
+ * Copies the data of a received packet into msg.
+ * @sock - socket, on which recv() was called
+ * @msg - buffer used to retrieve packetdata
+ * @size - size of msg
+ * @flags - flags for receiving
+ *
+ */
+
+static int gn_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
+		      int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int err = 0;
+	u16 copied = 0;
+	u32 offset;
+
+	struct gn_header *gh;
+
+	/* It is necessary to find the actual length of the payload, which,
+	 * in case of an unsecured package, resides in the commonheader and
+	 * still has to be calculated without the length of the btp-header
+	 * (4byte).
+	 * Furthermore, the datapointer probably has to be set to the
+	 * beginning of the actual payload
+	 */
+	skb = skb_recv_datagram(sk, flags & MSG_DONTWAIT, &err);
+	lock_sock(sk);
+	if (!skb)
+		goto out;
+
+	//TODO: Appletalk checks for RAW-Socket, still have to find out why exactly
+	gh = (struct gn_header *)skb_network_header(skb);
+
+	copied = be16_to_cpu(gh->gc_h.pl);
+	offset = skb->transport_header - skb->network_header +
+		 sizeof(struct btp_header);
+	copied -= sizeof(struct btp_header);
+
+	//TODO: are there trunctuated packets?
+	if (copied > size) {
+		copied = size;
+		msg->msg_flags |= MSG_TRUNC;
+	}
+
+	err = skb_copy_datagram_msg(skb, offset, msg, copied);
+
+	skb_free_datagram(sk, skb);
+
+out:
+	release_sock(sk);
+	return err ?: copied;
+}
+
+/*
+ * Geonetworking timer callbacks
+ */
+static int gn_send_beacon(struct gn_iface *gnif)
+{
+	//TODO: Media dependent procedures
+	unsigned int size;
+	struct gn_basic_header *gb_h;
+	struct gn_common_header *gc_h;
+	struct gn_beacon_header *gbe_h;
+	struct sk_buff *skb;
+
+	size = gn_dl->header_length;
+	size += gnif->dev->hard_header_len;
+	size += sizeof(struct gn_basic_header);
+	size += sizeof(struct gn_common_header);
+	size += sizeof(struct gn_beacon_header);
+
+	skb = netdev_alloc_skb(gnif->dev, size);
+	if (!skb)
+		goto out;
+
+	skb_reserve(skb, size);
+	skb->dev = gnif->dev;
+
+	gbe_h = skb_push(skb, sizeof(struct gn_beacon_header));
+	gc_h = skb_push(skb, sizeof(struct gn_common_header));
+	gb_h = skb_push(skb, sizeof(struct gn_basic_header));
+
+	gn_fill_bh_ch_nopayload(gnif, (struct gn_header *)gb_h, CH_HT_BEACON,
+				CH_HST_BEACON, 1);
+
+	gn_fill_sopv(gnif, &gbe_h->sopv, gnif->address);
+
+	gn_dl->request(gn_dl, skb, gnif->dev->broadcast);
+
+out:
+	return 0;
+}
+
+static void gn_send_beacons(struct timer_list *tl)
+{
+	struct gn_iface *gnif;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(gnif, &gn_interfaces, hnode) {
+		gn_send_beacon(gnif);
+	}
+	rcu_read_unlock();
+
+	mod_timer(tl, jiffies + msecs_to_jiffies(GN_BEACON_RETRANSMIT_TIME));
+}
+
+static DEFINE_TIMER(gn_beacon_timer, gn_send_beacons);
+
+void gn_activate_beacon(void)
+{
+	mod_timer(&gn_beacon_timer,
+		  jiffies + msecs_to_jiffies(GN_BEACON_RETRANSMIT_TIME));
+}
+
+static int validate_geo_scope(struct gn_geo_scope *scope)
+{
+	if (scope->angle > 360)
+		return 1;
+
+	if (scope->a == 0)
+		return 1;
+
+	int is_not_null = scope->b != 0 ? 1 : 0;
+
+	switch (scope->shape) {
+	case GN_SHAPE_CIRCLE:
+		return is_not_null;
+	case GN_SHAPE_RECTANGLE:
+	case GN_SHAPE_ELLIPSE:
+		return 0;
+	default:
+	case GN_SHAPE_UNSPECIFIED:
+		return 1;
+	}
+	return 0;
+}
+
+static int validate_scope(struct gn_scope *scope)
+{
+	if (scope->scope_type < GN_SCOPE_TOPOLOGICAL ||
+	    scope->scope_type > GN_SCOPE_MAX)
+		return 1;
+	switch (scope->scope_type) {
+	case GN_SCOPE_TOPOLOGICAL:
+		return 0;
+	case GN_SCOPE_GEOGRAPHICAL:
+	case GN_SCOPE_GEOGRAPHICAL_ANYCAST:
+		return validate_geo_scope(&scope->geo_scope);
+	default:
+		return 1;
+	}
+	return 0;
+}
+
+static int gn_setsockopt(struct socket *sock, int level, int optname,
+			 sockptr_t optval, unsigned int optlen)
+{
+	struct gn_scope opt;
+	struct sock *sk = sock->sk;
+	struct gn_sock *gn = gn_sk(sk);
+
+	int rc = -ENOPROTOOPT;
+
+	if (level != SOL_GN || optname != GN_SCOPE)
+		goto out;
+
+	rc = -EINVAL;
+	if (optlen < sizeof(struct gn_scope))
+		goto out;
+
+	rc = -EFAULT;
+	if (copy_from_sockptr(&opt, optval, sizeof(struct gn_scope)))
+		goto out;
+
+	rc = -EINVAL;
+	if (validate_scope(&opt))
+		goto out;
+
+	lock_sock(sk);
+	memcpy(&gn->scope, &opt, sizeof(struct gn_scope));
+	release_sock(sk);
+
+	rc = 0;
+out:
+	return rc;
+}
+
+static int gn_getsockopt(struct socket *sock, int level, int optname,
+			 char __user *optval, int __user *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct gn_sock *gn = gn_sk(sk);
+	int len, rc = -ENOPROTOOPT;
+
+	if (level != SOL_GN || optname != GN_SCOPE)
+		goto out;
+
+	rc = -EFAULT;
+	if (get_user(len, optlen))
+		goto out;
+
+	len = min_t(unsigned int, len, sizeof(struct gn_sock));
+
+	rc = -EINVAL;
+	if (len < 0)
+		goto out;
+
+	rc = -EFAULT;
+	if (put_user(len, optlen))
+		goto out;
+
+	lock_sock(sk);
+	rc = copy_to_user(optval, &gn->scope, len) ? -EFAULT : 0;
+	release_sock(sk);
+out:
+	return rc;
+}
+
+/*
+ * validate a gn_position coming from userspace
+ */
+static int gn_validate_pos(struct gn_position *pos)
+{
+	// TODO validation
+	return 0;
+}
+
+/*
+ * Geonetworking ioctl calls.
+ */
+static int gn_if_ioctl(unsigned int cmd, void __user *argp)
+{
+	struct sockaddr_gn *sa;
+	struct net_device *dev;
+	struct ifreq gnreq;
+	struct gn_position pos;
+
+	struct gn_iface *gnif = NULL;
+
+	if (copy_from_user(&gnreq, argp, sizeof(gnreq)))
+		return -EFAULT;
+
+	dev = __dev_get_by_name(&init_net, gnreq.ifr_name);
+	if (!dev)
+		return -ENODEV;
+
+	sa = (struct sockaddr_gn *)&gnreq.ifr_addr;
+
+	switch (cmd) {
+	case SIOCGIFBRDADDR:
+		gnif = gn_find_interface_by_dev(dev);
+		if (!gnif)
+			return -EADDRNOTAVAIL;
+		sa->sgn_family = AF_GN;
+		sa->sgn_addr = GNADDR_BROADCAST;
+		sa->sgn_port = GNPORT_ANY;
+		break;
+	case SIOCDIFADDR:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		if (sa->sgn_family != AF_GN)
+			return -EINVAL;
+		gn_if_drop_device(dev);
+		break;
+	case SIOCGIFADDR:
+		gnif = gn_find_interface_by_dev(dev);
+		if (!gnif)
+			return -EADDRNOTAVAIL;
+		sa->sgn_family = AF_GN;
+		sa->sgn_addr = gnif->address;
+		sa->sgn_port = GNPORT_ANY;
+		break;
+	case SIOCSIFADDR:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		if (sa->sgn_family != AF_GN)
+			return -EINVAL;
+		if (dev->type != ARPHRD_ETHER)
+			return -EINVAL;
+		// FIXME gn_if_add_device: check if exists
+		gnif = gn_if_add_device(dev, sa);
+		if (!gnif)
+			return -ENOMEM;
+		return 0;
+	case SIOCGNSPOSITION:
+		gnif = gn_find_interface_by_dev(dev);
+		if (!gnif)
+			return -EADDRNOTAVAIL;
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		if (copy_from_user(&pos, gnreq.ifr_data,
+				   sizeof(struct gn_position)))
+			return -EFAULT;
+		if (gn_validate_pos(&pos))
+			return -EINVAL;
+		// FIXME Timestamp conversion/handling
+		memcpy(&gnif->pos, &pos, sizeof(struct gn_position));
+		return 0;
+	default:
+		BUG();
+	}
+	return copy_to_user(argp, &gnreq, sizeof(gnreq)) ? -EFAULT : 0;
+}
+
+static int gn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	int rc = -ENOIOCTLCMD;
+	struct sock *sk = sock->sk;
+	void __user *argp = (void __user *)arg;
+
+	switch (cmd) {
+	/* Protocol layer */
+	case TIOCOUTQ: {
+		long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
+
+		if (amount < 0)
+			amount = 0;
+		rc = put_user(amount, (int __user *)argp);
+		break;
+	}
+	case TIOCINQ: {
+		/*
+		 * These two are safe on a single CPU system as only
+		 * user tasks fiddle here
+		 */
+		struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
+		long amount = 0;
+
+		if (skb)
+			amount = skb->len - sizeof(struct gn_header);
+		rc = put_user(amount, (int __user *)argp);
+		break;
+	}
+	case SIOCGSTAMP:
+		//rc = sock_get_timestamp(sk, argp);
+		break;
+	case SIOCGSTAMPNS:
+		//rc = sock_get_timestampns(sk, argp);
+		break;
+	case SIOCGIFBRDADDR:
+	case SIOCDIFADDR:
+	case SIOCGIFADDR:
+	case SIOCSIFADDR:
+	case SIOCGNSPOSITION:
+		rtnl_lock();
+		rc = gn_if_ioctl(cmd, argp);
+		rtnl_unlock();
+		break;
+	}
+
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static int gn_compat_ioctl(struct socket *sock, unsigned int cmd,
+			   unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+#endif
+
+static const struct net_proto_family gn_family_ops = {
+	.family = PF_GN,
+	.create = gn_create,
+	.owner = THIS_MODULE,
+};
+
+static const struct proto_ops gn_dgram_ops = {
+	.family = PF_GN,
+	.owner = THIS_MODULE,
+	.release = gn_release,
+	.bind = gn_bind,
+	.connect = gn_connect,
+	.socketpair = sock_no_socketpair,
+	.accept = sock_no_accept,
+	.getname = sock_no_getname,
+	.poll = datagram_poll,
+	.ioctl = gn_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = gn_compat_ioctl,
+#endif
+	.listen = sock_no_listen,
+	.shutdown = sock_no_shutdown,
+	.setsockopt = gn_setsockopt,
+	.getsockopt = gn_getsockopt,
+	.sendmsg = gn_sendmsg,
+	.recvmsg = gn_recvmsg,
+	.mmap = sock_no_mmap,
+};
+
+static struct notifier_block gn_notifier = {
+	.notifier_call = gn_device_event,
+};
+
+static struct packet_type gn_packet_type __read_mostly = {
+	.type = cpu_to_be16(ETH_P_GN),
+	.func = gn_rcv,
+};
+
+/*
+ * SNAP-ID for Geonetworking 0x8947
+ * TODO: while this implementation works between two OpenRSUs,
+ * endianness still has to be determined to guarantee interoperability
+ */
+static unsigned char gn_snap_id[] = { 0x00, 0x00, 0x00, 0x89, 0x47 };
+
+static const char gn_err_snap[] __initconst = KERN_CRIT
+	"Unable to register GeoNetworking with SNAP.\n";
+
+/* Called by proto.c on kernel start up */
+static int __init gn_init(void)
+{
+	int rc;
+
+	rc = proto_register(&gn_proto, 0);
+	if (rc)
+		goto out;
+
+	rc = sock_register(&gn_family_ops);
+	if (rc)
+		goto out_proto;
+
+	gn_dl = register_snap_client(gn_snap_id, gn_rcv);
+	if (!gn_dl)
+		printk(gn_err_snap);
+
+	dev_add_pack(&gn_packet_type);
+
+	rc = register_netdevice_notifier(&gn_notifier);
+	if (rc)
+		goto out_sock;
+
+	rc = gn_proc_init();
+	if (rc)
+		goto out_nd;
+#ifdef CONFIG_SYSCTL
+	rc = gn_register_sysctl();
+	if (rc)
+		goto out_proc;
+#endif
+
+out:
+	return rc;
+out_proc:
+	gn_proc_exit();
+out_nd:
+	unregister_netdevice_notifier(&gn_notifier);
+out_sock:
+	dev_remove_pack(&gn_packet_type);
+	unregister_snap_client(gn_dl);
+	sock_unregister(PF_GN);
+out_proto:
+	proto_unregister(&gn_proto);
+	goto out;
+}
+module_init(gn_init);
+
+static void __exit gn_exit(void)
+{
+#ifdef CONFIG_SYSCTL
+	gn_unregister_sysctl();
+#endif /* CONFIG_SYSCTL */
+	gn_proc_exit();
+	unregister_netdevice_notifier(&gn_notifier);
+	dev_remove_pack(&gn_packet_type);
+	unregister_snap_client(gn_dl);
+	sock_unregister(PF_GN);
+	proto_unregister(&gn_proto);
+}
+module_exit(gn_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mr Noname <email.here@domain");
+MODULE_DESCRIPTION("GeoNetworking protocol\n");
+MODULE_ALIAS_NETPROTO(PF_GN);
diff --git a/net/gn/gn_routing.c b/net/gn/gn_routing.c
new file mode 100644
index 000000000000..980a59fa81e6
--- /dev/null
+++ b/net/gn/gn_routing.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+#include <linux/types.h>
+#include <linux/bitfield.h>
+#include <linux/hashtable.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <net/datalink.h>
+
+#include <linux/gn_routing.h>
+#include <linux/gn.h>
+
+extern struct datalink_proto *gn_dl;
+
+static DEFINE_HASHTABLE(gn_loc_t, 3);
+static DEFINE_SPINLOCK(gn_loc_t_lock);
+
+// ###### values in meters
+#define EARTH_RADIUS 6378388ULL
+#define PI 31415926ULL // * 10^7
+#define METER_PER_LON 111317ULL
+#define RAD_PER_DEGREE 174533ULL // * 10^7 - same as long and lat from PV
+
+#define GN_LT_JIFFIES msecs_to_jiffies(GN_LOC_TE_LIFETIME)
+#define GN_TST_VALID(tst) time_after(jiffies, (tst) + GN_LT_JIFFIES)
+
+/***************************************************************************\
+*                                                                           *
+* GeoNetworking routing                                                     *
+*                                                                           *
+\***************************************************************************/
+
+static u16 *gn_dpd_find(struct gn_dpd_buf *buf, u16 sn)
+{
+	int i = 0;
+
+	for (; i < GN_DPL_SIZE; i++) {
+		if (buf->sn[i] == sn)
+			return &buf->sn[i];
+	}
+	return NULL;
+}
+
+static void gn_dpd_insert(struct gn_dpd_buf *buf, u16 sn)
+{
+	buf->sn[buf->head] = sn;
+	buf->head = (buf->head + 1) % GN_DPL_SIZE;
+}
+
+static u32 pdr(u32 old_pdr, u32 delta)
+{
+	if (!delta)
+		delta = 1;
+	return (GN_MAX_PDR_EMA_BETA * old_pdr) / 100 +
+	       (((100 - GN_MAX_PDR_EMA_BETA) * (100 / delta)) / 100);
+}
+
+// table is * 1000 | p1 * 100 entry
+static int cos_table[] = {
+	100000, 99995,	99980,	99955,	99920,	99875,	99820,	99755,	99680,
+	99595,	99500,	99396,	99281,	99156,	99022,	98877,	98723,	98558,
+	98384,	98200,	98007,	97803,	97590,	97367,	97134,	96891,	96639,
+	96377,	96106,	95824,	95534,	95233,	94924,	94604,	94275,	93937,
+	93590,	93233,	92866,	92491,	92106,	91712,	91309,	90897,	90475,
+	90045,	89605,	89157,	88699,	88233,	87758,	87274,	86782,	86281,
+	85771,	85252,	84726,	84190,	83646,	83094,	82534,	81965,	81388,
+	80803,	80210,	79608,	78999,	78382,	77757,	77125,	76484,	75836,
+	75181,	74517,	73847,	73169,	72484,	71791,	71091,	70385,	69671,
+	68950,	68222,	67488,	66746,	65998,	65244,	64483,	63715,	62941,
+	62161,	61375,	60582,	59783,	58979,	58168,	57352,	56530,	55702,
+	54869,	54030,	53186,	52337,	51482,	50622,	49757,	48887,	48012,
+	47133,	46249,	45360,	44466,	43568,	42666,	41759,	40849,	39934,
+	39015,	38092,	37166,	36236,	35302,	34365,	33424,	32480,	31532,
+	30582,	29628,	28672,	27712,	26750,	25785,	24818,	23848,	22875,
+	21901,	20924,	19945,	18964,	17981,	16997,	16010,	15023,	14033,
+	13042,	12050,	11057,	10063,	9067,	8071,	7074,	6076,	5077,
+	4079,	3079,	2079,	1080,	0,	-920,	-1920,	-2920,	-3919,
+	-4918,	-5917,	-6915,	-7912,	-8909,	-9904,	-10899, -11892, -12884,
+	-13875, -14865, -15853, -16840, -17825, -18808, -19789, -20768, -21745,
+	-22720, -23693, -24663, -25631, -26596, -27559, -28519, -29476, -30430,
+	-31381, -32329, -33274, -34215, -35153, -36087, -37018, -37945, -38868,
+	-39788, -40703, -41615, -42522, -43425, -44323, -45218, -46107, -46992,
+	-47873, -48748, -49619, -50485, -51345, -52201, -53051, -53896, -54736,
+	-55570, -56399, -57221, -58039, -58850, -59656, -60455, -61249, -62036,
+	-62817, -63592, -64361, -65123, -65879, -66628, -67370, -68106, -68834,
+	-69556, -70271, -70979, -71680, -72374, -73060, -73739, -74411, -75075,
+	-75732, -76382, -77023, -77657, -78283, -78901, -79512, -80114, -80709,
+	-81295, -81873, -82444, -83005, -83559, -84104, -84641, -85169, -85689,
+	-86200, -86703, -87197, -87682, -88158, -88626, -89085, -89534, -89975,
+	-90407, -90830, -91244, -91648, -92044, -92430, -92807, -93175, -93533,
+	-93883, -94222, -94553, -94873, -95185, -95486, -95779, -96061, -96334,
+	-96598, -96852, -97096, -97330, -97555, -97770, -97975, -98170, -98356,
+	-98531, -98697, -98853, -98999, -99135, -99262, -99378, -99484, -99581,
+	-99667, -99744, -99810, -99867, -99914, -99950, -99977, -99993, -100000
+};
+
+/* icos() - look up in cos_table for a rad value.
+ * @rad : the rad value.* 10^7
+ *
+ * Return : the cosine value * 100000
+ */
+static int icos(__s64 rad)
+{
+	if (rad > PI)
+		rad = PI - (rad - PI);
+	return cos_table[rad / 100000];
+}
+
+/* degree_to_rad() - convert a degree value to a rad value.
+* @a : the degree value as 1/10 micro degree (10^7).
+*
+* Return : the rad value * 10^7.
+*/
+static __s64 degree_to_rad(__s64 a)
+{
+	return (((RAD_PER_DEGREE * a) / 10000000ULL)) % (PI * 2ULL);
+}
+
+/* diff() - calculate the difference beween a and b.
+* @a : value a.
+* @b : value b.
+*
+* Return : the difference
+*/
+static __s32 diff(__s32 a, __s32 b)
+{
+	__s32 r = a - b;
+
+	return r < 0 ? r * -1 : r;
+}
+
+/* get_distance() - calculate the distance of to position vectors
+ * center/self: positionvectors, whose distance is calculated
+ * @x : after execute includes the meter on X-axes.
+ * @y : after execute includes the meter on Y-axes.
+ *
+ * the calculation based on pythagoras.
+*/
+static struct gn_coord gn_coord_diff(struct gn_coord lhs, struct gn_coord rhs)
+{
+	struct gn_coord c;
+	s32 lat;
+
+	lat = degree_to_rad((lhs.lat + rhs.lat) / 2);
+
+	c.lat = (METER_PER_LON * icos(lat) * diff(rhs.lon, lhs.lon)) /
+		(10000000ULL * 100000ULL);
+	c.lon = (METER_PER_LON * diff(rhs.lat, lhs.lat)) / 10000000ULL;
+	return c;
+}
+
+static inline struct gn_coord pv_to_coord(struct gn_lpv *pv)
+{
+	struct gn_coord c = {
+		.lat = be32_to_cpu(pv->lat),
+		.lon = be32_to_cpu(pv->lon),
+	};
+	return c;
+}
+
+static inline u64 dist(struct gn_coord c)
+{
+	return c.lat * c.lat + c.lon * c.lon;
+}
+
+/* gn_F() - decides if self is inside or at the border of the geographical area.
+ * @center: The position vector of the Package sender.
+ * @self: The own position vector.
+ * @t : The Type of geographical area.
+ * @r : The radius of area. Only use for circle.
+ * @a : The width of area. Only use for rectangel and elipse.
+ * @b : The height of area. Only use for rectangel and elipse.
+ * @angel : ???
+ *
+ * Return:  if the result is > 0 self is inside area.
+			if the result is 0 self is on border of area.
+			otherwise self is outside of area.
+ */
+__s64 gn_F(struct gn_coord self, struct gn_geo_scope scope)
+{
+	s32 a2, b2, x2, y2;
+	s64 result = -1;
+	struct gn_coord coord_diff = gn_coord_diff(self, scope.coord);
+
+	// TODO Scope angle!
+	a2 = scope.a * scope.a;
+	b2 = scope.b * scope.b;
+	x2 = coord_diff.lat * coord_diff.lat;
+	y2 = coord_diff.lon * coord_diff.lon;
+
+	switch (scope.shape) {
+	case GN_SHAPE_CIRCLE:
+		result = a2 - (x2 + y2);
+		break;
+	case GN_SHAPE_RECTANGLE:
+		result = a2 - x2;
+		if (result > (b2 - y2))
+			result = b2 - y2;
+		break;
+	case GN_SHAPE_ELLIPSE:
+		if (scope.a == 0 || scope.b == 0) {
+			WARN_ONCE(1, "internal error: input out of range");
+			break;
+		}
+		result = 1000;
+		result -= (y2 * 1000) / a2;
+		result -= (x2 * 1000) / b2;
+		break;
+	}
+
+	return result;
+}
+
+static int greedy_forward(struct gn_iface *gnif, u8 *addr, struct gn_lpv *depv)
+{
+	struct loc_te *curr;
+	int bkt, rc;
+	u8 *found_addr = NULL;
+
+	u64 min_dist, curr_dist, ego_dist;
+	struct gn_coord dest_coord = pv_to_coord(depv);
+
+	ego_dist = dist(gn_coord_diff(dest_coord, gnif->pos.coord));
+	min_dist = ego_dist;
+	spin_lock_bh(&gn_loc_t_lock);
+	hash_for_each(gn_loc_t, bkt, curr, hnode) {
+		if (curr->is_neighbour) {
+			curr_dist = dist(gn_coord_diff(dest_coord,
+						       pv_to_coord(&curr->pv)));
+			if (curr_dist < min_dist) {
+				min_dist = curr_dist;
+				found_addr = curr->ll_address;
+			}
+		}
+	}
+
+	// FIXME traffic class check here
+	if (found_addr) {
+		ether_addr_copy(addr, found_addr);
+		rc = GN_FORWARD_NEXT_HOP;
+	} else {
+		rc = GN_FORWARD_BROADCAST;
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+
+	return rc;
+}
+
+int gn_gxc_forward(struct gn_iface *gnif, s64 f, u8 *addr, struct gn_lpv *depv)
+{
+	if (f >= 0) {
+		// Area forwarding
+		switch (GN_AREA_FORWARDING) {
+		case GN_AREA_FORWARDING_SIMPLE:
+			return GN_FORWARD_BROADCAST;
+		default:
+			pr_warn("non-simple area forwarding not implemented");
+			return GN_FORWARD_BROADCAST;
+		}
+	} else {
+		// Non-area forwarding
+		switch (GN_NON_AREA_FORWARDING) {
+		case GN_NON_AREA_FORWARDING_GREEDY:
+			return greedy_forward(gnif, addr, depv);
+		default:
+			return GN_FORWARD_BROADCAST;
+		}
+	}
+}
+
+static void debug_loc_te(void)
+{
+	struct loc_te *entry;
+	int bucket;
+
+	spin_lock_bh(&gn_loc_t_lock);
+	if (hash_empty(gn_loc_t)) {
+		spin_unlock_bh(&gn_loc_t_lock);
+		return;
+	}
+
+	pr_info("Printing location table");
+	hash_for_each(gn_loc_t, bucket, entry, hnode) {
+		pr_info("LOC_TE(%p) tst=%x addr=%llx ll_addr=%llx is_neighbour=%x ls_pending=%x",
+			entry, entry->tst_addr, be64_to_cpu(entry->addr),
+			be64_to_cpu(entry->ll_address), entry->is_neighbour,
+			entry->ls_pending);
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+}
+
+static void gn_prune(void)
+{
+	struct loc_te *entry;
+	int bucket;
+
+	spin_lock_bh(&gn_loc_t_lock);
+	hash_for_each(gn_loc_t, bucket, entry, hnode) {
+		if (GN_TST_VALID(entry->tst_addr)) {
+			pr_info("pruning entry addr=%llx", entry->addr);
+			skb_queue_purge(&entry->lsb);
+			hash_del(&entry->hnode);
+			kfree(entry);
+		}
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+}
+
+/* update_location_table() - update location table
+ * @pv: the position vector which indicate an entry.
+ *
+ * Return: 0 on success, 1 on error, 2 if packet is duplicate.
+ *
+ * Update an entry, which indicated by @spv. If no entry found its will be add a new one.
+ * And all entries will be check with the update function.
+ */
+int gn_update_location_table(struct gn_lpv *pv, bool make_neighbour,
+			     const u8 *ll_address, const __be16 *sn)
+{
+	// FIXME Use timestamp associated with incoming skb
+	// TODO (Clause C.2): Only update PV if incoming PV is newer than stored PV
+	struct loc_te *entry;
+	bool found = false;
+
+	spin_lock_bh(&gn_loc_t_lock);
+	hash_for_each_possible(gn_loc_t, entry, hnode, pv->addr) {
+		if (entry->addr != pv->addr)
+			continue;
+
+		found = true;
+
+		pr_info("updating entry addr=%llx", pv->addr);
+
+		entry->pdr = pdr(entry->pdr,
+				 jiffies_to_msecs(jiffies) -
+					 jiffies_to_msecs(entry->tst_addr));
+		entry->tst_addr = jiffies;
+		entry->is_neighbour = entry->is_neighbour || make_neighbour;
+		memcpy(&entry->pv, pv, sizeof(*pv));
+		if (sn) {
+			// Perform DPD
+			if (gn_dpd_find(&entry->dpl, be16_to_cpu(*sn))) {
+				pr_info("received duplicate packet");
+				spin_unlock_bh(&gn_loc_t_lock);
+				return 2;
+			} else {
+				gn_dpd_insert(&entry->dpl, be16_to_cpu(*sn));
+			}
+		}
+		break;
+	}
+
+	if (!found) {
+		entry = kzalloc(sizeof(struct loc_te), GFP_ATOMIC);
+		if (!entry) {
+			spin_unlock_bh(&gn_loc_t_lock);
+			return 1;
+		}
+		pr_info("adding entry addr=%llx", pv->addr);
+		entry->addr = pv->addr;
+		entry->tst_addr = jiffies;
+		entry->is_neighbour = make_neighbour;
+		memcpy(&entry->pv, pv, sizeof(*pv));
+		skb_queue_head_init(&entry->lsb);
+		if (make_neighbour) {
+			BUG_ON(!ll_address);
+			ether_addr_copy(entry->ll_address, ll_address);
+		}
+		if (sn)
+			gn_dpd_insert(&entry->dpl, be16_to_cpu(*sn));
+
+		hash_add(gn_loc_t, &entry->hnode, entry->addr);
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+
+	gn_prune();
+
+	debug_loc_te();
+
+	return 0;
+}
+
+static void __ls_queue(struct sk_buff_head *q, struct sk_buff *skb)
+{
+	struct sk_buff *curr;
+	u32 qlen = skb_queue_len(q);
+
+	while (qlen-- > GN_LSB_SIZE) {
+		curr = skb_dequeue(q);
+		if (curr)
+			kfree_skb(curr);
+	}
+
+	skb_queue_tail(q, skb);
+}
+
+/* ls_queue() - queue packet for delivery if destination is not a neighbour
+ * @dest_addr: destination address
+ * @skb: the packet
+ *
+ * If the destination address is not a known neighbour ẃith recent activity,
+ * queue the packet
+ *
+ * Return: GN_QUEUE_DIRECT if the destination is a neighbour, GN_QUEUE_LS_PENDING if the
+ * destination is unknown, but a LS request is pending and the packet is queued,
+ * GN_QUEUE_LS_STALE if the destination is unknown or its entry is stale and
+ * we should send a LS query, GN_QUEUE_ERROR on error
+ */
+int gn_ls_queue(gn_address_t dest_addr, struct sk_buff *skb)
+{
+	struct loc_te *entry;
+	int rc = -1;
+
+	spin_lock_bh(&gn_loc_t_lock);
+	hash_for_each_possible(gn_loc_t, entry, hnode, dest_addr) {
+		if (entry->addr != dest_addr)
+			continue;
+
+		if (GN_TST_VALID(entry->tst_addr)) {
+			// Entry is valid, no need for LS
+			rc = GN_QUEUE_DIRECT;
+		} else if (entry->ls_pending == 1) {
+			// Entry is stale, but we already sent a LS request
+			rc = GN_QUEUE_LS_PENDING;
+		} else {
+			// Entry is stale, perform LS request
+			rc = GN_QUEUE_LS_STALE;
+
+			entry->ls_pending = 1;
+			__ls_queue(&entry->lsb, skb);
+		}
+
+		break;
+	}
+	if (rc == -1) {
+		entry = kzalloc(sizeof(struct loc_te), GFP_ATOMIC);
+		if (!entry) {
+			spin_unlock_bh(&gn_loc_t_lock);
+			return GN_QUEUE_ERROR;
+		}
+
+		rc = GN_QUEUE_LS_STALE;
+		entry->addr = dest_addr;
+		entry->ls_pending = 1;
+		skb_queue_head_init(&entry->lsb);
+		__ls_queue(&entry->lsb, skb);
+
+		hash_add(gn_loc_t, &entry->hnode, entry->addr);
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+
+	return rc;
+}
+
+void gn_ls_flush(gn_address_t dest_addr)
+{
+	struct loc_te *entry;
+	struct sk_buff *tmp_skb;
+
+	spin_lock_bh(&gn_loc_t_lock);
+	hash_for_each_possible(gn_loc_t, entry, hnode, dest_addr) {
+		if (entry->addr != dest_addr)
+			continue;
+		if (!entry->ls_pending)
+			break;
+
+		while ((tmp_skb = skb_dequeue(&entry->lsb)) != NULL) {
+			// FIXME forwarding each packet according to its type is necessary
+			// FIXME decrease packet lifetime according to time spent in queue
+			gn_dl->request(gn_dl, tmp_skb, tmp_skb->dev->broadcast);
+		}
+		entry->ls_pending = 0;
+		break;
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+}
+
+int gn_query_ll_address(gn_address_t query_addr, u8 *ll_address)
+{
+	struct loc_te *entry;
+	int rc = 1;
+
+	spin_lock_bh(&gn_loc_t_lock);
+	hash_for_each_possible(gn_loc_t, entry, hnode, query_addr) {
+		if (entry->addr != query_addr)
+			continue;
+		if (!entry->is_neighbour || !GN_TST_VALID(entry->tst_addr))
+			break;
+		if (is_zero_ether_addr(entry->ll_address))
+			break;
+
+		rc = 0;
+		ether_addr_copy(ll_address, entry->ll_address);
+		break;
+	}
+	spin_unlock_bh(&gn_loc_t_lock);
+
+	return rc;
+}
diff --git a/net/gn/sysctl_net_gn.c b/net/gn/sysctl_net_gn.c
new file mode 100644
index 000000000000..12bb2cb64135
--- /dev/null
+++ b/net/gn/sysctl_net_gn.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sysctl_net_gn.c: sysctl interface to net GeoNetworking subsystem
+ */
+
+#include <linux/sysctl.h>
+#include <net/sock.h>
+#include <linux/gn.h>
+
+static int dummy_var = 1234;
+
+static struct ctl_table gn_table[] = { {
+	.procname = "gn-dummy-var",
+	.data = &dummy_var,
+	.maxlen = sizeof(int),
+	.mode = 0644,
+	.proc_handler = proc_dointvec,
+} };
+
+static struct ctl_table_header *gn_table_header;
+
+int __init gn_register_sysctl(void)
+{
+	gn_table_header = register_net_sysctl(&init_net, "net/gn", gn_table);
+	if (!gn_table_header)
+		return -ENOMEM;
+	return 0;
+}
+
+void gn_unregister_sysctl(void)
+{
+	unregister_net_sysctl_table(gn_table_header);
+}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1a713d96206f..38876505cec3 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1318,7 +1318,10 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
 			return SECCLASS_XDP_SOCKET;
 		case PF_MCTP:
 			return SECCLASS_MCTP_SOCKET;
-#if PF_MAX > 46
+		case PF_GN:
+			return SECCLASS_GN_SOCKET;
+
+#if PF_MAX > 47
 #error New address family defined, please update this function.
 #endif
 		}
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 90cb61b16425..dfa07a586060 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -174,6 +174,7 @@ const struct security_class_mapping secclass_map[] = {
 	    "map_create_as", "prog_load_as", NULL } },
 	{ "xdp_socket", { COMMON_SOCK_PERMS, NULL } },
 	{ "mctp_socket", { COMMON_SOCK_PERMS, NULL } },
+	{ "gn_socket",	{ COMMON_SOCK_PERMS, NULL } },
 	{ "perf_event",
 	  { "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } },
 	{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
@@ -187,7 +188,7 @@ const struct security_class_mapping secclass_map[] = {
 #ifdef __KERNEL__ /* avoid this check when building host programs */
 #include <linux/socket.h>
 
-#if PF_MAX > 46
+#if PF_MAX > 47
 #error New address family defined, please update secclass_map.
 #endif
 #endif
-- 
2.55.0


^ permalink raw reply related

* [PATCH nf-next] ipvs: use type-safe allocation helpers in ip_vs_rht_alloc
From: Subasri S @ 2026-07-16 14:07 UTC (permalink / raw)
  To: Simon Horman, Julian Anastasov, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, lvs-devel, netfilter-devel, coreteam,
	linux-kernel, Subasri S

As per Documentation/process/deprecated.rst, open-coded kmalloc
assignments for struct objects are deprecated. Replace
kzalloc(sizeof(*ptr), GFP_KERNEL) with kzalloc_obj() and
kvmalloc_array(n, sizeof(*ptr), GFP_KERNEL) with kvmalloc_objs()
in ip_vs_rht_alloc().

Compile tested with CONFIG_IP_VS=y and runtime tested using
tools/testing/selftests/net/netfilter/ipvs.sh on x86_64/QEMU.

Signed-off-by: Subasri S <subasris1210@gmail.com>
---
 net/netfilter/ipvs/ip_vs_core.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 35cbe821c259..74047bf1e32d 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -176,7 +176,7 @@ void ip_vs_rht_rcu_free(struct rcu_head *head)
 
 struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
 {
-	struct ip_vs_rht *t = kzalloc(sizeof(*t), GFP_KERNEL);
+	struct ip_vs_rht *t = kzalloc_obj(*t);
 	int i;
 
 	if (!t)
@@ -186,7 +186,7 @@ struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
 
 		scounts = min(scounts, buckets);
 		scounts = min(scounts, ml);
-		t->seqc = kvmalloc_array(scounts, sizeof(*t->seqc), GFP_KERNEL);
+		t->seqc = kvmalloc_objs(*t->seqc, scounts);
 		if (!t->seqc)
 			goto err;
 		for (i = 0; i < scounts; i++)
@@ -194,8 +194,7 @@ struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
 
 		if (locks) {
 			locks = min(locks, scounts);
-			t->lock = kvmalloc_array(locks, sizeof(*t->lock),
-						 GFP_KERNEL);
+			t->lock = kvmalloc_objs(*t->lock, locks);
 			if (!t->lock)
 				goto err;
 			for (i = 0; i < locks; i++)
@@ -203,7 +202,7 @@ struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
 		}
 	}
 
-	t->buckets = kvmalloc_array(buckets, sizeof(*t->buckets), GFP_KERNEL);
+	t->buckets = kvmalloc_objs(*t->buckets, buckets);
 	if (!t->buckets)
 		goto err;
 	for (i = 0; i < buckets; i++)

---
base-commit: f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e
change-id: 20260714-nf-next-ipvs-deprecated-232667142d84

Best regards,
-- 
Subasri S <subasris1210@gmail.com>


^ permalink raw reply related

* [RFC PATCH net-next v0 0/6] net: add GeoNetworking protocol
From: Simon Dietz @ 2026-07-16 13:58 UTC (permalink / raw)
  To: netdev
  Cc: edumazet, davem, kuniyu, andrew+netdev, simon.dietz, dietz23838,
	linux-wireless, johannes

Implement the GeoNetworking / ETSI ITS-G5 ('net/gn') protocol which
is based on 802.11p wifi and used for vehicle2x applications. It is
standardized by the ETSI and used by some car manufacturers
(especially in europe). It enables ad-hoc, multi-hop geographical
communication and routing among vehicles (and road- or railside
infrastructure).

Most work of this implementation has been done by the bachelor
project 2018/2019 of the operating systems and middleware group of
the Hasso Plattner Institute, University of Potsdam, which the author
was part of.

The project was supervised by:
- Jossekin Beilharz
- Lukas Pirl
- Prof. Andreas Polze

The code is published under the GPL v2 at this location:
https://gitlab.com/hpi-potsdam/osm/g5-on-linux/linux-geonetworking/

The original code base was based on linux version 5.1. The first patch
includes the original codebase rebased to the current net-next code
base so that it compiles under current net-next (as required by
submitting patch guidelines in order not to break git bisect). I've
backed up a not squashed version of this patch series if a more
compartmentalized history would be desired.

The subsequent patches address bounds, null, empty checks, memory
safety, locking, code style and ETSI standard conformity improvements.

This patch series is not merge-ready, yet. E.g. network namespaces are
not supported at all, documentation is missing, ...

Purpose of this RFC is to determine if GeoNetworking support in the
linux kernel is desirable and if so what steps would have to be done
next. There is ongoing research on the topic vehicle2x, which may
benefit from GeoNetworking support in the Linux kernel.

Sorry for the previous submission which was only partially transmitted

Greetings,
Simon

Simon Dietz (6):
  net: add GeoNetworking protocol
  net: fix GeoNetworking
  net: further fix GeoNetworking
  net: even further fix GeoNetworking
  net: add ppc64 support for GeoNetworking
  net: apply RCS to GeoNetworking

 include/linux/gn.h                  |  349 +++++
 include/linux/gn_routing.h          |   68 +
 include/linux/socket.h              |    6 +-
 include/uapi/linux/gn.h             |   84 ++
 include/uapi/linux/if_ether.h       |    1 +
 net/Kconfig                         |    2 +
 net/Makefile                        |    1 +
 net/gn/Kconfig                      |    4 +
 net/gn/Makefile                     |    8 +
 net/gn/gn_proc.c                    |   86 ++
 net/gn/gn_prot.c                    | 2012 +++++++++++++++++++++++++++
 net/gn/gn_routing.c                 |  632 +++++++++
 net/gn/sysctl_net_gn.c              |   33 +
 security/selinux/hooks.c            |    5 +-
 security/selinux/include/classmap.h |    3 +-
 15 files changed, 3290 insertions(+), 4 deletions(-)
 create mode 100644 include/linux/gn.h
 create mode 100644 include/linux/gn_routing.h
 create mode 100644 include/uapi/linux/gn.h
 create mode 100644 net/gn/Kconfig
 create mode 100644 net/gn/Makefile
 create mode 100644 net/gn/gn_proc.c
 create mode 100644 net/gn/gn_prot.c
 create mode 100644 net/gn/gn_routing.c
 create mode 100644 net/gn/sysctl_net_gn.c

-- 
2.55.0


^ permalink raw reply

* Re: [PATCH net-next v2 2/2] net: phy: add DAPU Telecom DAP8210R(I) Gigabit Ethernet PHY driver
From: Andrew Lunn @ 2026-07-16 13:55 UTC (permalink / raw)
  To: Artem Shimko
  Cc: netdev, Heiner Kallweit, Russell King, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-kernel, devicetree
In-Reply-To: <20260716113805.593215-3-a.shimko.dev@gmail.com>

> +	/* Wait for reset self-clear */
> +	do {
> +		fsleep(20);
> +		ret = dap8211r_read_ext(phydev, DAP8211R_PHY_CON);
> +		if (ret < 0)
> +			return ret;
> +	} while (!(ret & DAP8211R_PHY_SW_RST) && --retries);

This does not have the usual problem, but it is still better to use
something from iopoll.h.

	Andrew

^ permalink raw reply

* [PATCH net v3] net/smc: order the CDC receive path against buffer publication
From: Bryam Vargas via B4 Relay @ 2026-07-16 13:50 UTC (permalink / raw)
  To: D. Wythe, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	Mahanta Jambigi, Dust Li, Wen Gu, David S. Miller,
	Sidraya Jayagond, Wenjia Zhang, Tony Lu
  Cc: netdev, Simon Horman, linux-s390, linux-rdma, linux-kernel

From: Bryam Vargas <hexlabsecurity@proton.me>

The SMC CDC receive handlers dereference conn->rmb_desc, and on the
SMC-D DMB-nocopy path conn->sndbuf_desc, but both are published after the
connection is already reachable to a peer: rmb_desc once the connection
is in the link group's token tree, the nocopy ghost sndbuf_desc later
still, in smcd_buf_attach() after the ISM receive tasklet is armed. A CDC
in that window hits a handler with the buffer unset -- a NULL dereference
and host DoS -- or, on a weakly ordered CPU, non-NULL but not yet
initialised. Both are also published before the receive state
(bytes_to_rcv, sndbuf_space), so an early CDC's accounting can be
overwritten by setup.

Initialise the receive state first and publish both buffers last with
smp_store_release(), consuming them with smp_load_acquire() and bailing
while unset, as the handlers already do for a killed connection. Gate the
whole sndbuf consumer trigger on the send buffer, not just the nocopy
accounting: smc_tx_prepared_sends() and smc_tx_pending() dereference it
too. Conforming peers are unaffected.

Closes: https://sashiko.dev/#/patchset/20260714-b4-disp-835288a6-v2-1-581555ef2145@proton.me?part=1
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
v3:
 - Publish rmb_desc and the ghost sndbuf_desc after the receive state is
   initialised, not before. The earlier revision released the pointer first,
   which let an early CDC's accounting be overwritten by setup.
 - Gate the whole sndbuf consumer trigger on sndbuf_desc, not only the nocopy
   accounting: smc_tx_prepared_sends() and smc_tx_pending() dereference it too.
 The v2 review raised both.
v2: https://lore.kernel.org/all/20260714-b4-disp-835288a6-v2-1-581555ef2145@proton.me/
v1: https://lore.kernel.org/all/20260711-b4-disp-c36a9798-v1-1-340b0c6053fb@proton.me/

herd7 models both orderings. Plain accesses allow the "pointer published, buffer
stale" outcome and flag a data race; release/acquire forbid it. A publish-order
litmus shows the lost update is allowed with the store released first and never
with it released last. af_smc runs over an RDMA fabric or an ISM device, so the
weak-memory arm is model-level; litmus tests and reproducer on request.

No Fixes: tag -- the rmb_desc ordering is foundational (predates the git history
here); the sndbuf_desc hunks additionally cover the later SMC-D DMB-nocopy path.
Happy to split rmb/sndbuf for a cleaner stable backport.
---
 net/smc/smc_cdc.c  | 50 +++++++++++++++++++++++++++++++++++++++++---------
 net/smc/smc_core.c | 26 ++++++++++++++++++++++----
 2 files changed, 63 insertions(+), 13 deletions(-)

diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c
index 32d6d03df321..ea61b1e75c72 100644
--- a/net/smc/smc_cdc.c
+++ b/net/smc/smc_cdc.c
@@ -332,8 +332,20 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
 {
 	union smc_host_cursor cons_old, prod_old;
 	struct smc_connection *conn = &smc->conn;
+	struct smc_buf_desc *sndbuf_desc;
 	int diff_cons, diff_prod, diff_tx;
 
+	/* Acquire the send buffer once, pairing with the smp_store_release() in
+	 * __smc_buf_create()/smcd_buf_attach().  On the SMC-D DMB-nocopy path
+	 * the ghost sndbuf_desc is attached only after the connection is already
+	 * reachable to the ISM device, so it can still be unset here; every
+	 * sndbuf_desc consumer below (the nocopy accounting and the sndbuf
+	 * consumer trigger, which dereferences it via smc_tx_prepared_sends())
+	 * is skipped while it is NULL to avoid a NULL deref and a load of an
+	 * uninitialised buffer.
+	 */
+	sndbuf_desc = smp_load_acquire(&conn->sndbuf_desc);
+
 	smc_curs_copy(&prod_old, &conn->local_rx_ctrl.prod, conn);
 	smc_curs_copy(&cons_old, &conn->local_rx_ctrl.cons, conn);
 	smc_cdc_msg_to_host(&conn->local_rx_ctrl, cdc, conn);
@@ -351,14 +363,17 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
 
 		/* if local sndbuf shares the same memory region with
 		 * peer RMB, then update tx_curs_fin and sndbuf_space
-		 * here since peer has already consumed the data.
+		 * here since peer has already consumed the data.  The ghost
+		 * sndbuf_desc (acquired above) may still be unset in the SMC-D
+		 * DMB-nocopy setup window, so skip the update while it is NULL.
 		 */
 		if (conn->lgr->is_smcd &&
-		    smc_ism_support_dmb_nocopy(conn->lgr->smcd)) {
+		    smc_ism_support_dmb_nocopy(conn->lgr->smcd) &&
+		    sndbuf_desc) {
 			/* Calculate consumed data and
 			 * increment free send buffer space.
 			 */
-			diff_tx = smc_curs_diff(conn->sndbuf_desc->len,
+			diff_tx = smc_curs_diff(sndbuf_desc->len,
 						&conn->tx_curs_fin,
 						&conn->local_rx_ctrl.cons);
 			/* increase local sndbuf space and fin_curs */
@@ -391,10 +406,15 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
 			conn->urg_state = SMC_URG_NOTYET;
 	}
 
-	/* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */
-	if ((diff_cons && smc_tx_prepared_sends(conn)) ||
-	    conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
-	    conn->local_rx_ctrl.prod_flags.urg_data_pending) {
+	/* trigger sndbuf consumer: RDMA write into peer RMBE and CDC.
+	 * smc_tx_prepared_sends() and smc_tx_pending() dereference sndbuf_desc,
+	 * so skip the whole trigger while it is unset (the SMC-D DMB-nocopy
+	 * setup window): there is nothing to send without a send buffer.
+	 */
+	if (sndbuf_desc &&
+	    ((diff_cons && smc_tx_prepared_sends(conn)) ||
+	     conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
+	     conn->local_rx_ctrl.prod_flags.urg_data_pending)) {
 		if (!sock_owned_by_user(&smc->sk))
 			smc_tx_pending(conn);
 		else
@@ -443,13 +463,21 @@ static void smcd_cdc_rx_tsklet(struct tasklet_struct *t)
 {
 	struct smc_connection *conn = from_tasklet(conn, t, rx_tsklet);
 	struct smcd_cdc_msg *data_cdc;
+	struct smc_buf_desc *rmb_desc;
 	struct smcd_cdc_msg cdc;
 	struct smc_sock *smc;
 
 	if (!conn || conn->killed)
 		return;
+	/* Pair with smp_store_release() in __smc_buf_create(): the connection
+	 * is published before its RMB is allocated, so bail while rmb_desc is
+	 * unset to avoid a NULL deref and a load of an uninitialised buffer.
+	 */
+	rmb_desc = smp_load_acquire(&conn->rmb_desc);
+	if (!rmb_desc)
+		return;
 
-	data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr;
+	data_cdc = (struct smcd_cdc_msg *)rmb_desc->cpu_addr;
 	smcd_curs_copy(&cdc.prod, &data_cdc->prod, conn);
 	smcd_curs_copy(&cdc.cons, &data_cdc->cons, conn);
 	smc = container_of(conn, struct smc_sock, conn);
@@ -483,7 +511,11 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
 	lgr = smc_get_lgr(link);
 	read_lock_bh(&lgr->conns_lock);
 	conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
-	if (!conn || conn->out_of_sync) {
+	/* Pair with smp_store_release() in __smc_buf_create(): bail while the
+	 * RMB is unset (smc_cdc_msg_recv_action() dereferences it) to avoid a
+	 * NULL deref and a stale-buffer read in the connection setup window.
+	 */
+	if (!conn || conn->out_of_sync || !smp_load_acquire(&conn->rmb_desc)) {
 		read_unlock_bh(&lgr->conns_lock);
 		return;
 	}
diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c
index cf6b620fef05..0561f83be327 100644
--- a/net/smc/smc_core.c
+++ b/net/smc/smc_core.c
@@ -2499,15 +2499,26 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
 	}
 
 	if (is_rmb) {
-		conn->rmb_desc = buf_desc;
 		conn->rmbe_size_comp = bufsize_comp;
 		smc->sk.sk_rcvbuf = bufsize * 2;
 		atomic_set(&conn->bytes_to_rcv, 0);
 		conn->rmbe_update_limit =
 			smc_rmb_wnd_update_limit(buf_desc->len);
+		/* Publish the receive buffer last, with release semantics: the
+		 * connection is already in the link group's token tree, so a
+		 * concurrent CDC receive handler must observe the fully
+		 * initialised receive state above (and the buffer) once it sees
+		 * a non-NULL rmb_desc.  Pairs with the smp_load_acquire() in the
+		 * CDC receive path.
+		 */
+		smp_store_release(&conn->rmb_desc, buf_desc);
 		if (is_smcd)
 			smc_ism_set_conn(conn); /* map RMB/smcd_dev to conn */
 	} else {
+		/* Plain store: this send-buffer pass runs before the RMB pass,
+		 * whose smp_store_release(&conn->rmb_desc) then publishes this
+		 * store too, and the CDC receive path is gated on rmb_desc.
+		 */
 		conn->sndbuf_desc = buf_desc;
 		smc->sk.sk_sndbuf = bufsize * 2;
 		atomic_set(&conn->sndbuf_space, bufsize);
@@ -2599,9 +2610,16 @@ int smcd_buf_attach(struct smc_sock *smc)
 	buf_desc->cpu_addr =
 		(u8 *)buf_desc->cpu_addr + sizeof(struct smcd_cdc_msg);
 	buf_desc->len -= sizeof(struct smcd_cdc_msg);
-	conn->sndbuf_desc = buf_desc;
-	conn->sndbuf_desc->used = 1;
-	atomic_set(&conn->sndbuf_space, conn->sndbuf_desc->len);
+	buf_desc->used = 1;
+	atomic_set(&conn->sndbuf_space, buf_desc->len);
+	/* Publish the ghost send buffer last, with release semantics: the
+	 * connection is already reachable to the ISM device (smc_ism_set_conn()
+	 * ran in __smc_buf_create()), so the CDC receive tasklet must observe
+	 * the fully initialised ghost buffer once it sees a non-NULL
+	 * sndbuf_desc.  Pairs with smp_load_acquire() in
+	 * smc_cdc_msg_recv_action().
+	 */
+	smp_store_release(&conn->sndbuf_desc, buf_desc);
 	return 0;
 
 free:

---
base-commit: 3f1f755366687d051174739fb99f7d560202f60b
change-id: 20260716-b4-disp-aa52955a-560f19c43a2a

Best regards,
-- 
Bryam Vargas <hexlabsecurity@proton.me>



^ permalink raw reply related

* Re: net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Andrew Lunn @ 2026-07-16 13:43 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Markus Elfring, netdev, kernel-janitors, Uwe Kleine-König,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	MD Danish Anwar, Michael Grzeschik, Paolo Abeni, Vadim Fedorenko,
	Yibo Dong, LKML, Jonathan Corbet
In-Reply-To: <aljdzTZ_f5v6ckVg@stanley.mountain>

On Thu, Jul 16, 2026 at 04:34:05PM +0300, Dan Carpenter wrote:
> On Thu, Jul 16, 2026 at 03:24:22PM +0200, Andrew Lunn wrote:
> > On Thu, Jul 16, 2026 at 11:48:43AM +0200, Markus Elfring wrote:
> > > > In C writing if (!p) and if (p == NULL) are always equivalent but weirdly
> > > > the NULL doesn't have to zero.  It's part of the C FAQ.
> > > > https://c-faq.com/null/machexamp.html It's just a bit of fun trivia that
> > > > doesn't really matter unless you have a time machine.  Even if you invented
> > > > a time machine, the code here would still be fine because we have a NULL
> > > > test before dereferencing the results of our pointer math.
> > > Which expression evaluations would you interpret as pointer dereferences finally?
> > > https://c-faq.com/sx1/
> > 
> > This is all interesting, but nobody has yet explain how this function
> > can be called such that we have a NULL pointer.
> 
> mucse cannot be NULL.
> 
> We call pci_set_drvdata() during probe() and it is still valid when
> we call the remove() function.

Same as what i thought. So while this is an interesting discussion,
please could someone post the real fix, remove the NULL pointer check.

       Andrew

^ permalink raw reply

* Re: [PATCH net v2] phonet: check register_netdevice_notifier() error in phonet_device_init()
From: Andrew Lunn @ 2026-07-16 13:40 UTC (permalink / raw)
  To: Minhong He
  Cc: courmisch, davem, edumazet, kuba, pabeni, horms,
	remi.denis-courmont, netdev, linux-kernel
In-Reply-To: <20260716101504.158387-1-heminhong@kylinos.cn>

On Thu, Jul 16, 2026 at 06:15:04PM +0800, Minhong He wrote:
> phonet_device_init() registers a netdevice notifier before calling
> phonet_netlink_register(), but does not check whether notifier
> registration succeeded. On failure, netlink setup still proceeds and
> init may return success without the notifier in place.
> 
> Check the notifier registration error and unwind only the resources
> that were already registered (proc entry and pernet), without calling
> unregister_netdevice_notifier() for a notifier that was never
> registered.

It is a good idea to look around, and see if the same sort of bug
appears again. Think about what happens when phonet_netlink_register()
fails.

> Fixes: f8ff60283de2 ("Phonet: network device and address handling")

Does this bother people

https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html

    Andrew

---
pw-bot: cr

^ permalink raw reply

* Re: [PATCH net v5 0/3] octeon_ep, octeon_ep_vf: fix RX skb frags overflow and page leak
From: Maciej Fijalkowski @ 2026-07-16 13:33 UTC (permalink / raw)
  To: Maoyi Xie
  Cc: Veerasenareddy Burru, Sathesh Edara, Satananda Burla,
	Shinas Rasheed, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Guangshuo Li, David Carlier,
	Simon Horman, netdev, linux-kernel
In-Reply-To: <20260716063432.2908100-1-maoyixie.tju@gmail.com>

On Thu, Jul 16, 2026 at 02:34:29PM +0800, Maoyi Xie wrote:
> The octeon_ep and octeon_ep_vf RX paths add one skb fragment per buffer
> with no bound against MAX_SKB_FRAGS. buff_info->len comes from the device
> response header. A long packet needs about 18 fragments. That is one past
> the default MAX_SKB_FRAGS of 17, so skb_add_rx_frag() writes past
> shinfo->frags[]. Patch 1 bounds octeon_ep. Patch 3 bounds octeon_ep_vf.
> 
> Patch 2 is Guangshuo Li's fix for an octeon_ep_vf RX page leak on the
> napi_build_skb() failure path. It touches the same drop code as patch 3.
> I carry it here so the series applies without conflict, per Maciej. Patch 3
> moves that drain loop into a helper. The helper carries the page frees from
> patch 2, so the overflow drop path frees its pages too.
> 
> octeon_ep has the same leak on its drop path. A separate patch will fix it
> once this series lands.
> 
> v5:
>  - octeon_ep, octeon_ep_vf: widen data_len to u32, per Simon Horman.
>    buff_info->len is a u64, a u16 could truncate a long length.
>  - octeon_ep: dropped Maciej Fijalkowski's Reviewed-by, the check changed.
>  - octeon_ep_vf: the drop helper drains the length in a u32 too.

The check changed however I provided certain suggestions that are included
in this set (checking frags before build_skb, wrapping common code to
helper), so I feel I still can have my review here.

For the series:
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>

> 
> v1: https://lore.kernel.org/r/20260701112825.1653044-1-maoyixie.tju@gmail.com
> v2: https://lore.kernel.org/r/20260702180518.2013324-1-maoyixie.tju@gmail.com
> v3: https://lore.kernel.org/r/20260704061511.2350737-1-maoyixie.tju@gmail.com
> v4: https://lore.kernel.org/r/20260706150208.2944898-1-maoyixie.tju@gmail.com
> 
> Guangshuo Li (1):
>   octeon_ep_vf: Fix RX page leak on napi_build_skb() failure
> 
> Maoyi Xie (2):
>   octeon_ep: fix skb frags overflow in the RX path
>   octeon_ep_vf: fix skb frags overflow in the RX path
> 
>  .../net/ethernet/marvell/octeon_ep/octep_rx.c |  9 ++++
>  .../marvell/octeon_ep_vf/octep_vf_rx.c        | 51 ++++++++++++-------
>  2 files changed, 43 insertions(+), 17 deletions(-)
> 
> --
> 2.34.1
> 

^ permalink raw reply

* Re: net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Dan Carpenter @ 2026-07-16 13:34 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Markus Elfring, netdev, kernel-janitors, Uwe Kleine-König,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	MD Danish Anwar, Michael Grzeschik, Paolo Abeni, Vadim Fedorenko,
	Yibo Dong, LKML, Jonathan Corbet
In-Reply-To: <062f9181-e494-4576-bedb-36de488c11ca@lunn.ch>

On Thu, Jul 16, 2026 at 03:24:22PM +0200, Andrew Lunn wrote:
> On Thu, Jul 16, 2026 at 11:48:43AM +0200, Markus Elfring wrote:
> > > In C writing if (!p) and if (p == NULL) are always equivalent but weirdly
> > > the NULL doesn't have to zero.  It's part of the C FAQ.
> > > https://c-faq.com/null/machexamp.html It's just a bit of fun trivia that
> > > doesn't really matter unless you have a time machine.  Even if you invented
> > > a time machine, the code here would still be fine because we have a NULL
> > > test before dereferencing the results of our pointer math.
> > Which expression evaluations would you interpret as pointer dereferences finally?
> > https://c-faq.com/sx1/
> 
> This is all interesting, but nobody has yet explain how this function
> can be called such that we have a NULL pointer.

mucse cannot be NULL.

We call pci_set_drvdata() during probe() and it is still valid when
we call the remove() function.

regards,
dan carpenter

^ permalink raw reply

* Re: [PATCH net-next v2 12/14] gpio: tc956x: add TC956x/QPS615 support
From: Andrew Lunn @ 2026-07-16 13:32 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Alex Elder, andrew+netdev, davem, edumazet, kuba, pabeni,
	maxime.chevallier, rmk+kernel, andersson, konradybcio, robh,
	krzk+dt, conor+dt, linusw, brgl, arnd, gregkh, daniel, mohd.anwar,
	a0987203069, alexandre.torgue, ast, boon.khai.ng, chenchuangyu,
	chenhuacai, daniel, hawk, hkallweit1, inochiama, john.fastabend,
	julianbraha, livelycarpet87, mcoquelin.stm32, me,
	prabhakar.mahadev-lad.rj, richardcochran, rohan.g.thomas, sdf,
	siyanteng, weishangjuan, wens, netdev, bpf, linux-arm-msm,
	devicetree, linux-gpio, linux-stm32, linux-arm-kernel,
	linux-kernel
In-Reply-To: <dhnylud7o5dhaaq6kvbes4vkml3ppa5ev542j2lyxdijibknzg@pfcy22wz6jav>

> So we need to make sure that the GPIO controller driver is available before
> enumerating the switch device. One way to achieve is by creating the GPIO Aux
> device in the pci/pwrctrl/pci-pwrctrl-tc9563.c driver and let the GPIO
> controller driver use I2C communication for setting up the GPIOs. Thankfully,
> the switch allows both I2C and BAR MMIO configurations for internal GPIOs.

There was a comment from Rob that the GPIO controller should be
associated with the upstream port of the switch. And there was a way
to do that.

What we need to ensure is that the GPIO controller has an independent
lifetime to the downstream ports. That would allow the downstream
ports to return EPROBE_AGAIN, if the GPIO controller associated to the
upstream port is still initialising.

	 Andrew

^ permalink raw reply

* Re: [PATCH net v3 2/2] sctp: close UDP tunnel sockets during netns teardown
From: Xin Long @ 2026-07-16 13:31 UTC (permalink / raw)
  To: Ren Wei
  Cc: linux-sctp, netdev, marcelo.leitner, davem, edumazet, pabeni,
	horms, matttbe, yuantan098, yifanwucs, tomapufckgml, bird,
	tpluszz77, roxy520tt, sashiko-bot
In-Reply-To: <6dab75f22855cb219e2e30a5497cab03b970ab91.1784033357.git.roxy520tt@gmail.com>

On Tue, Jul 14, 2026 at 9:51 PM Ren Wei <n05ec@lzu.edu.cn> wrote:
>
> From: Zhiling Zou <roxy520tt@gmail.com>
>
> proc_sctp_do_udp_port() starts per-net SCTP UDP tunneling sockets when
> net.sctp.udp_port is set, and stops/restarts them when the sysctl value
> changes. The netns exit path does not stop these sockets, so a namespace
> can be torn down while its SCTP UDP tunnel sockets are still installed.
>
> Close the UDP tunnel sockets from sctp_ctrlsock_exit() after unregistering
> the per-net sysctl table. This prevents new sysctl writes from racing in
> while the sockets are being released, and closes the sockets before the
> control socket is destroyed.
>
> Fixes: 046c052b475e ("sctp: enable udp tunneling socks")
> Cc: stable@vger.kernel.org
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://sashiko.dev/#/patchset/b9f1f02b0780ad6a719e2413f5f0bb8eb7702d94.1782585631.git.roxy520tt%40gmail.com
> Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> ---
> Changes in v3:
> - No code changes.
>
>  net/sctp/protocol.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
> index 49d9740b1e0f..27c26e12f95d 100644
> --- a/net/sctp/protocol.c
> +++ b/net/sctp/protocol.c
> @@ -1460,6 +1460,7 @@ static int __net_init sctp_ctrlsock_init(struct net *net)
>  static void __net_exit sctp_ctrlsock_exit(struct net *net)
>  {
>         sctp_sysctl_net_unregister(net);
> +       sctp_udp_sock_stop(net);
>
>         /* Free the control endpoint.  */
>         inet_ctl_sock_destroy(net->sctp.ctl_sock);
> --
> 2.43.0
>
Acked-by: Xin Long <lucien.xin@gmail.com>

^ permalink raw reply

* Re: [PATCH net v3 1/2] sctp: avoid auth_enable sysctl UAF during netns teardown
From: Xin Long @ 2026-07-16 13:31 UTC (permalink / raw)
  To: Ren Wei
  Cc: linux-sctp, netdev, marcelo.leitner, davem, edumazet, pabeni,
	horms, matttbe, yuantan098, yifanwucs, tomapufckgml, bird,
	tpluszz77, roxy520tt, sashiko-bot
In-Reply-To: <390cd5e91ed60eea27b0b64d0468301a9e73b808.1784033357.git.roxy520tt@gmail.com>

On Tue, Jul 14, 2026 at 9:50 PM Ren Wei <n05ec@lzu.edu.cn> wrote:
>
> From: Zhiling Zou <roxy520tt@gmail.com>
>
> proc_sctp_do_auth() updates the SCTP control socket after changing
> net.sctp.auth_enable. The handler gets the per-net SCTP state from
> ctl->data, so an already opened sysctl file can still target a network
> namespace while that namespace is being torn down.
>
> SCTP previously registered its per-net sysctls from sctp_defaults_init(),
> while the control socket is created later from sctp_ctrlsock_init(). This
> exposed a window during initialization where auth_enable was writable
> before net->sctp.ctl_sock existed, and a teardown window where auth_enable
> stayed writable after inet_ctl_sock_destroy() had released the control
> socket.
>
> Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after
> sctp_ctl_sock_init() succeeds, and unregister the sysctl table before
> destroying the control socket in sctp_ctrlsock_exit(). If sysctl
> registration fails after the control socket was created, destroy the
> control socket in the same init path.
>
> Make sctp_sysctl_net_unregister() tolerate a missing header and clear the
> saved pointer so init-error and exit paths can safely share the unregister
> helper.
>
> Fixes: 15649fd5415e ("sctp: sysctl: auth_enable: avoid using current->nsproxy")
> Cc: stable@vger.kernel.org
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Xin Liu <bird@lzu.edu.cn>
> Co-developed-by: Qi Tang <tpluszz77@gmail.com>
> Signed-off-by: Qi Tang <tpluszz77@gmail.com>
> Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>

Acked-by: Xin Long <lucien.xin@gmail.com>

^ permalink raw reply

* Re: net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Dan Carpenter @ 2026-07-16 13:30 UTC (permalink / raw)
  To: Markus Elfring
  Cc: netdev, kernel-janitors, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, MD Danish Anwar, Michael Grzeschik,
	Paolo Abeni, Uwe Kleine-König, Vadim Fedorenko, Yibo Dong,
	LKML, Jonathan Corbet
In-Reply-To: <320cb00c-28f8-4871-9ec5-d9b86cca8bc9@web.de>

On Thu, Jul 16, 2026 at 10:20:42AM +0200, Markus Elfring wrote:
> >>>> The address of a data structure member was determined before
> >>>> a corresponding null pointer check in the implementation of
> >>>> the function “rnpgbe_rm_adapter”.
> >>>>
> >>>> Thus avoid the risk for undefined behaviour by omitting the variable “hw”.
> >>>> Pass the required address directly to a function call.
> >>>>
> >>>> This issue was detected by using the Coccinelle software.
> >>>>
> >>>> Fixes: 2ee95ec17e97c58b65e978a08b75fa8cb6424e4e ("net: rnpgbe: Add register_netdev")
> >>>
> >>> There is no NULL dereference here.  It's just pointer math.
> >>> No need for a Fixes tag.
> >>
> >> How does your view fit to information in an article like “Fun with NULL pointers, part 1”
> >> (by Jonathan Corbet from 2009-07-20)?>> https://lwn.net/Articles/342330/
> >>
> > 
> > Of course you can't have NULL pointer dereferences but this is not a
> > dereference, it's just pointer math.
> Does your understanding of programming language details differ from the view
> by Jonathan Corbet?

Jonathan Corbet's article talks about dereferences.

	p = tun->sk;
            ^^^^^^^
This is a dereference.

	p = &tun->sk;
            ^
This is pointer math.  It's not a dereference.

regards,
dan carpenter



^ permalink raw reply

* [PATCH v5 1/1] ptp: ocp: add CPLD ISP support for ADVA TimeCard X1
From: Sagi Maimon @ 2026-07-16 13:29 UTC (permalink / raw)
  To: jonathan.lemon, vadim.fedorenko, richardcochran, andrew+netdev,
	davem, edumazet, kuba, pabeni
  Cc: linux-kernel, netdev, Sagi Maimon

The ADVA TimeCard X1 (PCI device 0x0410) uses a Lattice MachXO3 CPLD
that is programmed over I2C using in-system programming (ISP).

The CPLD is connected to a secondary I2C bus shared with the onboard
MicroBlaze soft CPU.  Add support for taking ownership of this bus and
exposing the required interfaces through sysfs, allowing userspace tools
to perform CPLD programming.

To limit the scope of this functionality, sysfs-based I2C access is
restricted to the ADVA TimeCard X1 variant and only for the two I2C
slave addresses used during ISP (0x40 CPLD, 0x74 mux).

Add two sysfs attributes under /sys/class/timecard/ocpN/ (x1 only):

i2c_bus_ctrl  - arbitrate the shared I2C bus from the MicroBlaze via
                a three-step read/write/poll handshake

cpld_i2c_xfer - binary passthrough for I2C transactions to the CPLD
                and its PCA9548 mux; one atomic request per write()

Signed-off-by: Sagi Maimon <maimon.sagi@gmail.com>
---

 Addressed comments from:
  - Simon Horman : https://lore.kernel.org/all/20260715111559.1920391-1-horms@kernel.org/
 
 Changes since v4:
  - Move mutex_init(&bp->tap_i2c_lock) and bp->tap_i2c_adap_nr = -1 to
  ptp_ocp_probe() before ptp_ocp_register_resources(). On adva_x1, the
  I2C resource is registered before board_init runs; if xiic-i2c probes
  synchronously the notifier writes tap_i2c_adap_nr, which board_init
  then clobbers with -1, breaking cpld_i2c_xfer permanently.

  - Drop the if (tap_i2c_adap_nr != 0) guard in ptp_ocp_detach(). The
  notifier has no variant filter so non-x1 boards can leave adap_nr != 0,
  triggering mutex_destroy on an uninitialised mutex. Also, nr=0 is a
  valid adapter number. Since mutex_init is now unconditional, so is
  mutex_destroy.

  - Fix bounce-buffer leak in ptp_ocp_cpld_i2c_write(). Assigning
  msg->buf = i2c_get_dma_safe_msg_buf() makes buf == msg->buf, so
  i2c_put_dma_safe_msg_buf() returns without kfree() on every call.
  Replace with kzalloc()/kfree() and set I2C_M_DMA_SAFE. 
 
 drivers/ptp/ptp_ocp.c | 236 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 231 insertions(+), 5 deletions(-)

diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 35e911f1ad78..91fa06f0e44c 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -163,7 +163,8 @@ struct gpio_reg {
 	u32	gpio1;
 	u32	__pad0;
 	u32	gpio2;
-	u32	__pad1;
+	/* adva_x1: I2C bus ownership register; reserved on other variants */
+	u32	i2c_bus_ctrl;
 };
 
 struct irig_master_reg {
@@ -416,6 +417,11 @@ struct ptp_ocp {
 	dpll_tracker tracker;
 	int signals_nr;
 	int freq_in_nr;
+	/* cpld_i2c_xfer sysfs (adva_x1) */
+	struct mutex		tap_i2c_lock;
+	int			tap_i2c_adap_nr; /* adapter nr; -1 if absent */
+	u8			tap_i2c_rsp[21]; /* [status, read_data...] */
+	size_t			tap_i2c_rsp_len;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -4224,6 +4230,205 @@ static const struct ocp_attr_group art_timecard_groups[] = {
 	{ },
 };
 
+/*
+ * i2c_bus_ctrl exposes the MicroBlaze I2C bus arbitration register.
+ *
+ * The shared bus requires a three-step handshake before use:
+ *   read 0x00000000 (free) -> write 0x0000ffff (request) ->
+ *   poll until 0xffffffff (MicroBlaze confirms release).
+ *
+ * The poll is a PCIe non-posted read, which also flushes the preceding
+ * posted write to the FPGA, so no separate kernel read-back is needed.
+ */
+static ssize_t
+i2c_bus_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct ptp_ocp *bp = dev_get_drvdata(dev);
+
+	if (!bp->pps_select)
+		return -ENODEV;
+	return sysfs_emit(buf, "0x%08x\n",
+			  ioread32(&bp->pps_select->i2c_bus_ctrl));
+}
+
+static ssize_t
+i2c_bus_ctrl_store(struct device *dev, struct device_attribute *attr,
+		   const char *buf, size_t count)
+{
+	struct ptp_ocp *bp = dev_get_drvdata(dev);
+	u32 val;
+
+	if (!bp->pps_select)
+		return -ENODEV;
+	if (kstrtou32(buf, 0, &val))
+		return -EINVAL;
+	iowrite32(val, &bp->pps_select->i2c_bus_ctrl);
+	return count;
+}
+
+static DEVICE_ATTR_RW(i2c_bus_ctrl);
+
+/*
+ * cpld_i2c_xfer - sysfs binary I2C passthrough for adva_x1 TAP CPLD.
+ *
+ * write: [addr][write_len][read_len][flags][write_data...]
+ *   flags bit 0: I2C_M_NOSTART on the read segment
+ * read:  [status][read_data...]
+ *   status 0 = success, else positive errno
+ *
+ * Only addresses 0x40 (CPLD) and 0x74 (mux) are permitted.
+ */
+#define TAP_I2C_ALLOWED_ADDRS_NUM  2
+static const u8 tap_i2c_allowed_addrs[TAP_I2C_ALLOWED_ADDRS_NUM] = {
+	0x40, /* CPLD */
+	0x74, /* mux  */
+};
+
+/*
+ * tap_i2c_errno_to_byte - encode a kernel errno as a one-byte status.
+ *
+ * Errnos > 255 (e.g. ENOTSUPP=524) or multiples of 256 would truncate
+ * to a wrong or zero value with a plain (u8) cast.  Map those to EIO.
+ */
+static u8 tap_i2c_errno_to_byte(int err)
+{
+	int val = (err < 0) ? -err : EIO;
+
+	return (val > 0 && val <= 0xFF) ? (u8)val : EIO;
+}
+
+#define TAP_I2C_REQ_HDR_LEN   4
+#define TAP_I2C_MAX_WRITE_LEN 67
+#define TAP_I2C_MAX_READ_LEN  20
+#define TAP_I2C_FLAG_NOSTART  BIT(0)
+
+static ssize_t
+ptp_ocp_cpld_i2c_write(struct file *file, struct kobject *kobj,
+		       const struct bin_attribute *attr,
+		       char *buf, loff_t off, size_t count)
+{
+	struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+	const u8 *req = (const u8 *)buf;
+	u8 addr, write_len, read_len, flags;
+	struct i2c_adapter *adap;
+	struct i2c_msg msgs[2];
+	u8 *rdbuf = NULL;
+	int nmsgs, ret, i;
+
+	/* Each write is one atomic request; non-zero offset means a
+	 * mid-buffer pwrite() which would misparse the header.
+	 */
+	if (off != 0)
+		return -EINVAL;
+	if (count < TAP_I2C_REQ_HDR_LEN || count > TAP_I2C_REQ_HDR_LEN + TAP_I2C_MAX_WRITE_LEN)
+		return -EINVAL;
+
+	addr      = req[0];
+	write_len = req[1];
+	read_len  = req[2];
+	flags     = req[3];
+
+	/* Validate */
+	for (i = 0; i < TAP_I2C_ALLOWED_ADDRS_NUM; i++)
+		if (addr == tap_i2c_allowed_addrs[i])
+			break;
+	if (i == TAP_I2C_ALLOWED_ADDRS_NUM)
+		return -EPERM;
+
+	if (write_len > TAP_I2C_MAX_WRITE_LEN)
+		return -EINVAL;
+	if (read_len > TAP_I2C_MAX_READ_LEN)
+		return -EINVAL;
+	if (write_len + TAP_I2C_REQ_HDR_LEN > count)
+		return -EINVAL;
+	if (write_len == 0 && read_len == 0)
+		return -EINVAL;
+	/* I2C_M_NOSTART suppresses the repeated START between write and read
+	 * segments; it has no meaning on a first-and-only message.
+	 */
+	if ((flags & TAP_I2C_FLAG_NOSTART) && write_len == 0)
+		return -EINVAL;
+
+	/* i2c_get_adapter() takes a reference under core_lock; safe against
+	 * concurrent adapter unbind.
+	 */
+	adap = i2c_get_adapter(READ_ONCE(bp->tap_i2c_adap_nr));
+	if (!adap)
+		return -ENODEV;
+
+	nmsgs = 0;
+	if (write_len > 0) {
+		msgs[nmsgs].addr  = addr;
+		msgs[nmsgs].flags = 0;
+		msgs[nmsgs].len   = write_len;
+		msgs[nmsgs].buf   = (u8 *)req + TAP_I2C_REQ_HDR_LEN;
+		nmsgs++;
+	}
+	if (read_len > 0) {
+		u16 rd_flags = I2C_M_RD;
+
+		if (flags & TAP_I2C_FLAG_NOSTART)
+			rd_flags |= I2C_M_NOSTART;
+		msgs[nmsgs].addr  = addr;
+		msgs[nmsgs].flags = rd_flags | I2C_M_DMA_SAFE;
+		msgs[nmsgs].len   = read_len;
+		rdbuf = kzalloc(read_len, GFP_KERNEL);
+		if (!rdbuf) {
+			i2c_put_adapter(adap);
+			return -ENOMEM;
+		}
+		msgs[nmsgs].buf = rdbuf;
+		nmsgs++;
+	}
+
+	/* Serialise transfer+publish so concurrent writers cannot overwrite
+	 * each other's response in tap_i2c_rsp.
+	 */
+	mutex_lock(&bp->tap_i2c_lock);
+	ret = i2c_transfer(adap, msgs, nmsgs);
+	if (ret == nmsgs) {
+		bp->tap_i2c_rsp[0] = 0;
+		if (read_len > 0)
+			memcpy(&bp->tap_i2c_rsp[1], rdbuf, read_len);
+		bp->tap_i2c_rsp_len = 1 + read_len;
+		ret = count;
+	} else {
+		bp->tap_i2c_rsp[0]  = tap_i2c_errno_to_byte(ret);
+		bp->tap_i2c_rsp_len = 1;
+		ret = (ret < 0) ? ret : -EIO;
+	}
+	mutex_unlock(&bp->tap_i2c_lock);
+	kfree(rdbuf);
+	i2c_put_adapter(adap);
+
+	return ret;
+}
+
+static ssize_t
+ptp_ocp_cpld_i2c_read(struct file *file, struct kobject *kobj,
+		      const struct bin_attribute *attr,
+		      char *buf, loff_t off, size_t count)
+{
+	struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+	ssize_t ret;
+
+	mutex_lock(&bp->tap_i2c_lock);
+	if (off >= bp->tap_i2c_rsp_len) {
+		ret = 0;
+	} else {
+		ret = min(count, bp->tap_i2c_rsp_len - (size_t)off);
+		memcpy(buf, bp->tap_i2c_rsp + off, ret);
+	}
+	mutex_unlock(&bp->tap_i2c_lock);
+	return ret;
+}
+
+static const struct bin_attribute tap_i2c_bin_attr = {
+	.attr  = { .name = "cpld_i2c_xfer", .mode = 0600 },
+	.write = ptp_ocp_cpld_i2c_write,
+	.read  = ptp_ocp_cpld_i2c_read,
+};
+
 static struct attribute *adva_timecard_attrs[] = {
 	&dev_attr_serialnum.attr,
 	&dev_attr_gnss_sync.attr,
@@ -4272,11 +4477,18 @@ static struct attribute *adva_timecard_x1_attrs[] = {
 	&dev_attr_ts_window_adjust.attr,
 	&dev_attr_utc_tai_offset.attr,
 	&dev_attr_tod_correction.attr,
+	&dev_attr_i2c_bus_ctrl.attr,
+	NULL,
+};
+
+static const struct bin_attribute *const bin_adva_x1_timecard_attrs[] = {
+	&tap_i2c_bin_attr,
 	NULL,
 };
 
 static const struct attribute_group adva_timecard_x1_group = {
-	.attrs = adva_timecard_x1_attrs,
+	.attrs     = adva_timecard_x1_attrs,
+	.bin_attrs = bin_adva_x1_timecard_attrs,
 };
 
 static const struct ocp_attr_group adva_timecard_x1_groups[] = {
@@ -4902,6 +5114,7 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 		clk_hw_unregister_fixed_rate(bp->i2c_clk);
 	if (bp->n_irqs)
 		pci_free_irq_vectors(bp->pdev);
+	mutex_destroy(&bp->tap_i2c_lock);
 	device_unregister(&bp->dev);
 }
 
@@ -5093,6 +5306,14 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	bp->n_irqs = err;
 	pci_set_master(pdev);
 
+	/* Initialise before ptp_ocp_register_resources() so that the I2C bus
+	 * notifier (ptp_ocp_i2c_notifier_call) cannot fire and write
+	 * tap_i2c_adap_nr before ptp_ocp_adva_board_init() overwrites it
+	 * with -1, leaving the adapter number lost for the device lifetime.
+	 */
+	mutex_init(&bp->tap_i2c_lock);
+	bp->tap_i2c_adap_nr = -1;
+
 	err = ptp_ocp_register_resources(bp, id->driver_data);
 	if (err)
 		goto out;
@@ -5217,11 +5438,16 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,
 
 found:
 	bp = dev_get_drvdata(dev);
-	if (add)
+	if (add) {
 		ptp_ocp_symlink(bp, child, "i2c");
-	else
+		/* Cache adapter number; cpld_i2c_xfer uses i2c_get_adapter()
+		 * for a reference-counted, unbind-safe lookup.
+		 */
+		WRITE_ONCE(bp->tap_i2c_adap_nr, i2c_verify_adapter(child)->nr);
+	} else {
+		WRITE_ONCE(bp->tap_i2c_adap_nr, -1);	/* invalidate before free */
 		sysfs_remove_link(&bp->dev.kobj, "i2c");
-
+	}
 	return 0;
 }
 
-- 
2.47.0


^ permalink raw reply related

* Re: net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Andrew Lunn @ 2026-07-16 13:24 UTC (permalink / raw)
  To: Markus Elfring
  Cc: Dan Carpenter, netdev, kernel-janitors, Uwe Kleine-König,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	MD Danish Anwar, Michael Grzeschik, Paolo Abeni, Vadim Fedorenko,
	Yibo Dong, LKML, Jonathan Corbet
In-Reply-To: <95abcefe-2211-4abb-9af2-7e8d7f4a9b76@web.de>

On Thu, Jul 16, 2026 at 11:48:43AM +0200, Markus Elfring wrote:
> > In C writing if (!p) and if (p == NULL) are always equivalent but weirdly
> > the NULL doesn't have to zero.  It's part of the C FAQ.
> > https://c-faq.com/null/machexamp.html It's just a bit of fun trivia that
> > doesn't really matter unless you have a time machine.  Even if you invented
> > a time machine, the code here would still be fine because we have a NULL
> > test before dereferencing the results of our pointer math.
> Which expression evaluations would you interpret as pointer dereferences finally?
> https://c-faq.com/sx1/

This is all interesting, but nobody has yet explain how this function
can be called such that we have a NULL pointer.

    Andrew

^ permalink raw reply

* [PATCH rdma-next v3 14/14] RDMA/selftests: Add rxe_netns_names test
From: Jiri Pirko @ 2026-07-16 13:23 UTC (permalink / raw)
  To: linux-rdma
  Cc: cgroups, netdev, linux-s390, linux-kselftest, jgg, leon, parav,
	mbloch, cmeiohas, roman.gushchin, bvanassche, zyjzyj2000, shuah,
	tj, mkoutny, hannes, alibuda, dust.li, sidraya, wenjia,
	yanjun.zhu, cui.tao
In-Reply-To: <20260716132316.1495242-1-jiri@resnulli.us>

From: Jiri Pirko <jiri@nvidia.com>

Add a kselftest script that exercises per-netns RDMA device naming
with RXE. Cover duplicate names across namespaces, move conflict
handling, move-with-rename, and same-namespace rename requests.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
v2->v3:
- added wait for rdma devices are removed
- added retry for old rdma netns mode restore
v1->v2:
- fixed ktap_set_plan
- s/RXE_A/RXE_SAME/ in dup rename
---
 tools/testing/selftests/rdma/Makefile         |   3 +-
 tools/testing/selftests/rdma/config           |   2 +
 .../testing/selftests/rdma/rxe_netns_names.sh | 334 ++++++++++++++++++
 3 files changed, 338 insertions(+), 1 deletion(-)
 create mode 100755 tools/testing/selftests/rdma/rxe_netns_names.sh

diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
index 07af7f15c1bf..a91c14c45006 100644
--- a/tools/testing/selftests/rdma/Makefile
+++ b/tools/testing/selftests/rdma/Makefile
@@ -3,6 +3,7 @@ TEST_PROGS := rxe_rping_between_netns.sh \
 		rxe_ipv6.sh \
 		rxe_socket_with_netns.sh \
 		rxe_test_NETDEV_UNREGISTER.sh \
-		rxe_sent_rcvd_bytes.sh
+		rxe_sent_rcvd_bytes.sh \
+		rxe_netns_names.sh
 
 include ../lib.mk
diff --git a/tools/testing/selftests/rdma/config b/tools/testing/selftests/rdma/config
index 4ffb814e253b..e1ff54ec0f57 100644
--- a/tools/testing/selftests/rdma/config
+++ b/tools/testing/selftests/rdma/config
@@ -1,3 +1,5 @@
 CONFIG_TUN
 CONFIG_VETH
+CONFIG_DUMMY
+CONFIG_NET_NS
 CONFIG_RDMA_RXE
diff --git a/tools/testing/selftests/rdma/rxe_netns_names.sh b/tools/testing/selftests/rdma/rxe_netns_names.sh
new file mode 100755
index 000000000000..f40118407f4c
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_netns_names.sh
@@ -0,0 +1,334 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Exercise RDMA device name handling across network namespaces.
+
+source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
+
+NAME_PREFIX="rxe_netns_names_$$"
+NETDEV_PREFIX="rxn$$"
+NS1="${NAME_PREFIX}ns1"
+NS2="${NAME_PREFIX}ns2"
+RXE_A="${NAME_PREFIX}rxe_a"
+RXE_B="${NAME_PREFIX}rxe_b"
+RXE_SAME="${NAME_PREFIX}rxe_same"
+RXE_NEW="${NAME_PREFIX}rxe_new"
+DUMMY_A="${NETDEV_PREFIX}a"
+DUMMY_B="${NETDEV_PREFIX}b"
+OLD_MODE=""
+MODE_CHANGED=0
+MODS=("dummy" "rdma_rxe")
+TEST_SAME_NAMES="same RDMA device name can exist in two net namespaces"
+TEST_MOVE_CONFLICT="move without rename fails on destination name conflict"
+TEST_MOVE_RENAME="move then rename succeeds"
+TEST_COMBINED_MOVE_RENAME="move with requested destination name succeeds"
+TEST_SAME_NETNS_DUP_RENAME="same-netns rename rejects duplicate name"
+TEST_TEARDOWN_RETURN="netns delete returns device to init_net and renames on conflict"
+
+ksft_skip()
+{
+	ktap_skip_all "$*"
+	exit "$KSFT_SKIP"
+}
+
+fail()
+{
+	ktap_exit_fail_msg "$*"
+}
+
+need_cmd()
+{
+	command -v "$1" >/dev/null 2>&1 || ksft_skip "missing command: $1"
+}
+
+rdma_ns()
+{
+	local ns=$1
+
+	shift
+	ip netns exec "$ns" rdma "$@"
+}
+
+rdma_dev_exists()
+{
+	local ns=$1
+	local dev=$2
+
+	if [ -n "$ns" ]; then
+		rdma_ns "$ns" dev show "$dev" >/dev/null 2>&1
+	else
+		rdma dev show "$dev" >/dev/null 2>&1
+	fi
+}
+
+add_dummy()
+{
+	local netdev=$1
+
+	ip link add "$netdev" type dummy || return 1
+	ip link set "$netdev" up || return 1
+}
+
+add_rxe()
+{
+	local dev=$1
+	local netdev=$2
+
+	rdma link add "$dev" type rxe netdev "$netdev"
+}
+
+rdma_dev_on_netdev()
+{
+	local netdev=$1
+
+	rdma link show 2>/dev/null | awk -v want="$netdev" '
+		{
+			for (i = 1; i < NF; i++)
+				if ($i == "netdev" && $(i + 1) == want) {
+					dev = $2
+					sub(/\/.*/, "", dev)
+					print dev
+					exit
+				}
+		}'
+}
+
+wait_rdma_dev_on_netdev()
+{
+	local netdev=$1
+	local dev
+	local i
+
+	for i in $(seq 1 50); do
+		dev=$(rdma_dev_on_netdev "$netdev")
+		if [ -n "$dev" ]; then
+			echo "$dev"
+			return 0
+		fi
+		sleep 0.1
+	done
+
+	return 1
+}
+
+# ip link del returns after NETDEV_UNREGISTER, but rxe tears the RDMA device
+# down asynchronously via ib_unregister_device_queued(). Wait until our names
+# are gone.
+wait_rdma_devs_gone()
+{
+	local i name ns
+	local names=("$RXE_A" "$RXE_B" "$RXE_SAME" "$RXE_NEW")
+
+	for i in $(seq 1 50); do
+		local found=0
+
+		for name in "${names[@]}"; do
+			if rdma_dev_exists "" "$name"; then
+				found=1
+				break
+			fi
+			for ns in "$NS1" "$NS2"; do
+				ip netns exec "$ns" true 2>/dev/null || continue
+				if rdma_dev_exists "$ns" "$name"; then
+					found=1
+					break 2
+				fi
+			done
+		done
+
+		[ "$found" -eq 0 ] && return 0
+		sleep 0.1
+	done
+
+	return 1
+}
+
+setup_devs()
+{
+	cleanup_devs || return 1
+
+	add_dummy "$DUMMY_A" || return 1
+	add_dummy "$DUMMY_B" || return 1
+
+	add_rxe "$RXE_A" "$DUMMY_A" || return 1
+	add_rxe "$RXE_B" "$DUMMY_B" || return 1
+}
+
+cleanup_devs()
+{
+	ip link del "$DUMMY_A" 2>/dev/null
+	ip link del "$DUMMY_B" 2>/dev/null
+	wait_rdma_devs_gone
+}
+
+setup()
+{
+	OLD_MODE=$(rdma system show 2>/dev/null |
+		   sed -n 's/.*netns \([^ ]*\).*/\1/p')
+	[ -n "$OLD_MODE" ] || ksft_skip "failed to read RDMA netns mode"
+
+	rdma system set netns exclusive >/dev/null 2>&1 ||
+		ksft_skip "rdma netns exclusive mode is not supported"
+	MODE_CHANGED=1
+
+	ip netns add "$NS1" || return 1
+	ip netns add "$NS2" || return 1
+}
+
+# ip netns del returns before rdma_dev_exit_net() removes the net from
+# rdma_nets. rdma_compatdev_set() returns -EBUSY until that completes, so
+# retry the mode restore instead of leaving the system in exclusive mode.
+restore_netns_mode()
+{
+	local i
+
+	[ "$MODE_CHANGED" -eq 1 ] || return 0
+
+	for i in $(seq 1 50); do
+		if rdma system set netns "$OLD_MODE" >/dev/null 2>&1; then
+			MODE_CHANGED=0
+			return 0
+		fi
+		sleep 0.1
+	done
+
+	echo "warning: failed to restore RDMA netns mode to $OLD_MODE" >&2
+	return 1
+}
+
+cleanup()
+{
+	cleanup_devs
+
+	ip netns del "$NS1" 2>/dev/null
+	ip netns del "$NS2" 2>/dev/null
+
+	restore_netns_mode
+
+	for m in "${MODS[@]}"; do
+		modprobe -r "$m" 2>/dev/null
+	done
+}
+
+rdma_supports_combined_move_rename()
+{
+	rdma dev help 2>&1 | grep -Eq 'netns .*name|name .*netns'
+}
+
+[ "$(id -u)" -eq 0 ] || ksft_skip "must be run as root"
+need_cmd ip
+need_cmd rdma
+need_cmd modprobe
+
+trap cleanup EXIT
+
+for m in "${MODS[@]}"; do
+	modinfo "$m" >/dev/null 2>&1 || ksft_skip "module $m not found"
+	modprobe "$m" || fail "failed to load $m"
+done
+
+setup || fail "failed to create net namespaces"
+
+ktap_print_header
+ktap_set_plan 6
+
+if setup_devs &&
+   rdma dev set "$RXE_A" netns "$NS1" &&
+   rdma_ns "$NS1" dev set "$RXE_A" name "$RXE_SAME" &&
+   rdma dev set "$RXE_B" netns "$NS2" &&
+   rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME" &&
+   rdma_dev_exists "$NS1" "$RXE_SAME" &&
+   rdma_dev_exists "$NS2" "$RXE_SAME"; then
+	ktap_test_pass "$TEST_SAME_NAMES"
+else
+	ktap_test_fail "$TEST_SAME_NAMES"
+fi
+cleanup_devs
+
+if ! setup_devs ||
+   ! rdma dev set "$RXE_A" netns "$NS1" ||
+   ! rdma_ns "$NS1" dev set "$RXE_A" name "$RXE_SAME" ||
+   ! rdma dev set "$RXE_B" netns "$NS2" ||
+   ! rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME"; then
+	ktap_test_fail "$TEST_MOVE_CONFLICT"
+elif rdma_ns "$NS1" dev set "$RXE_SAME" netns "$NS2" >/dev/null 2>&1; then
+	ktap_test_fail "$TEST_MOVE_CONFLICT"
+elif rdma_dev_exists "$NS1" "$RXE_SAME" &&
+     rdma_dev_exists "$NS2" "$RXE_SAME"; then
+	ktap_test_pass "$TEST_MOVE_CONFLICT"
+else
+	ktap_test_fail "$TEST_MOVE_CONFLICT"
+fi
+cleanup_devs
+
+if ! setup_devs; then
+	ktap_test_fail "$TEST_MOVE_RENAME"
+elif rdma dev set "$RXE_A" netns "$NS2" &&
+     rdma_ns "$NS2" dev set "$RXE_A" name "$RXE_NEW"; then
+	if rdma_dev_exists "$NS2" "$RXE_NEW" &&
+	   ! rdma_dev_exists "" "$RXE_A"; then
+		ktap_test_pass "$TEST_MOVE_RENAME"
+	else
+		ktap_test_fail "$TEST_MOVE_RENAME"
+	fi
+else
+	ktap_test_fail "$TEST_MOVE_RENAME"
+fi
+cleanup_devs
+
+if ! rdma_supports_combined_move_rename; then
+	ktap_test_skip "$TEST_COMBINED_MOVE_RENAME"
+elif ! setup_devs; then
+	ktap_test_fail "$TEST_COMBINED_MOVE_RENAME"
+elif rdma dev set "$RXE_A" netns "$NS2" name "$RXE_NEW"; then
+	if rdma_dev_exists "$NS2" "$RXE_NEW" &&
+	   ! rdma_dev_exists "" "$RXE_A"; then
+		ktap_test_pass "$TEST_COMBINED_MOVE_RENAME"
+	else
+		ktap_test_fail "$TEST_COMBINED_MOVE_RENAME"
+	fi
+else
+	ktap_test_fail "$TEST_COMBINED_MOVE_RENAME"
+fi
+cleanup_devs
+
+if ! setup_devs; then
+	ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+elif rdma dev set "$RXE_A" name "$RXE_SAME" &&
+     rdma dev set "$RXE_B" name "$RXE_NEW"; then
+	if rdma dev set "$RXE_SAME" name "$RXE_NEW" >/dev/null 2>&1; then
+		ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+	elif rdma_dev_exists "" "$RXE_SAME" &&
+	     rdma_dev_exists "" "$RXE_NEW"; then
+		ktap_test_pass "$TEST_SAME_NETNS_DUP_RENAME"
+	else
+		ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+	fi
+else
+	ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+fi
+cleanup_devs
+
+if ! setup_devs; then
+	ktap_test_fail "$TEST_TEARDOWN_RETURN"
+elif ! rdma dev set "$RXE_A" name "$RXE_SAME" ||
+     ! rdma dev set "$RXE_B" netns "$NS2" ||
+     ! rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME" ||
+     ! rdma_dev_exists "$NS2" "$RXE_SAME"; then
+	ktap_test_fail "$TEST_TEARDOWN_RETURN"
+else
+	ip netns del "$NS2"
+	returned=$(wait_rdma_dev_on_netdev "$DUMMY_B")
+	ktap_print_msg "device returned to init_net as '${returned:-<missing>}'"
+	if rdma_dev_exists "" "$RXE_SAME" &&
+	   [ -n "$returned" ] &&
+	   [ "$returned" != "$RXE_SAME" ] &&
+	   [ "${returned#ibdev}" != "$returned" ]; then
+		ktap_test_pass "$TEST_TEARDOWN_RETURN"
+	else
+		ktap_test_fail "$TEST_TEARDOWN_RETURN"
+	fi
+fi
+cleanup_devs
+
+ktap_finished
-- 
2.54.0


^ permalink raw reply related

* [PATCH rdma-next v3 13/14] RDMA/rxe: Implement disassociate_ucontext callback
From: Jiri Pirko @ 2026-07-16 13:23 UTC (permalink / raw)
  To: linux-rdma
  Cc: cgroups, netdev, linux-s390, linux-kselftest, jgg, leon, parav,
	mbloch, cmeiohas, roman.gushchin, bvanassche, zyjzyj2000, shuah,
	tj, mkoutny, hannes, alibuda, dust.li, sidraya, wenjia,
	yanjun.zhu, cui.tao
In-Reply-To: <20260716132316.1495242-1-jiri@resnulli.us>

From: Jiri Pirko <jiri@nvidia.com>

Implement an empty disassociate_ucontext() callback so the RDMA core
can move rxe devices between net namespaces. The core requires this
callback to reset user contexts without waiting for userspace.

rxe needs no teardown here: its user-mapped queues live in
reference-counted vmalloc memory (see rxe_mmap.c) that stays valid
while userspace holds the mappings.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/infiniband/sw/rxe/rxe_verbs.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c
index 1ec130fee8ea..6eb10d2f0653 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.c
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.c
@@ -240,6 +240,10 @@ static void rxe_dealloc_ucontext(struct ib_ucontext *ibuc)
 		rxe_err_uc(uc, "cleanup failed, err = %d\n", err);
 }
 
+static void rxe_disassociate_ucontext(struct ib_ucontext *ibuc)
+{
+}
+
 /* pd */
 static int rxe_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
 {
@@ -1478,6 +1482,7 @@ static const struct ib_device_ops rxe_dev_ops = {
 	.destroy_srq = rxe_destroy_srq,
 	.detach_mcast = rxe_detach_mcast,
 	.device_group = &rxe_attr_group,
+	.disassociate_ucontext = rxe_disassociate_ucontext,
 	.enable_driver = rxe_enable_driver,
 	.get_dma_mr = rxe_get_dma_mr,
 	.get_hw_stats = rxe_ib_get_hw_stats,
-- 
2.54.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox