Message ID | 20240825041511.324452-5-almasrymina@google.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Device Memory TCP | expand |
On Sun, 25 Aug 2024 04:15:02 +0000 Mina Almasry wrote: > +void net_devmem_free_dmabuf(struct net_iov *niov) > +{ > + struct net_devmem_dmabuf_binding *binding = net_iov_binding(niov); > + unsigned long dma_addr = net_devmem_get_dma_addr(niov); > + > + if (gen_pool_has_addr(binding->chunk_pool, dma_addr, PAGE_SIZE)) > + gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE); Is the check necessary for correctness? Should it perhaps be a WARN under DEBUG_NET instead? The rest LGTM: Reviewed-by: Jakub Kicinski <kuba@kernel.org>
On Tue, Aug 27, 2024 at 7:15 PM Jakub Kicinski <kuba@kernel.org> wrote: > > On Sun, 25 Aug 2024 04:15:02 +0000 Mina Almasry wrote: > > +void net_devmem_free_dmabuf(struct net_iov *niov) > > +{ > > + struct net_devmem_dmabuf_binding *binding = net_iov_binding(niov); > > + unsigned long dma_addr = net_devmem_get_dma_addr(niov); > > + > > + if (gen_pool_has_addr(binding->chunk_pool, dma_addr, PAGE_SIZE)) > > + gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE); > > Is the check necessary for correctness? Should it perhaps be a WARN > under DEBUG_NET instead? The rest LGTM: > Not really necessary for correctness per se, but if we try to free a dma_addr that is not in a gen_pool (due to some other bug in the code), then gen_pool_free ends up BUG_ON, crashing the kernel. Arguably gen_pool_free should not BUG_ON, but I think that's an old API, and existing call sites have worked around the BUG_ON by doing a gen_pool_has_addr check like I do here, for example kernel/dma/pool.c. So I did not seek to change this established behavior. I think WARN seems fine to me, but maybe not under DEBUG_NET. I don't want production code crashing due to this error, if it's OK with you. Unless I hear otherwise I'll add a WARN without debug here. > Reviewed-by: Jakub Kicinski <kuba@kernel.org> > Thanks!
On Wed, 28 Aug 2024 00:20:23 -0700 Mina Almasry wrote: > > On Sun, 25 Aug 2024 04:15:02 +0000 Mina Almasry wrote: > > > +void net_devmem_free_dmabuf(struct net_iov *niov) > > > +{ > > > + struct net_devmem_dmabuf_binding *binding = net_iov_binding(niov); > > > + unsigned long dma_addr = net_devmem_get_dma_addr(niov); > > > + > > > + if (gen_pool_has_addr(binding->chunk_pool, dma_addr, PAGE_SIZE)) > > > + gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE); > > > > Is the check necessary for correctness? Should it perhaps be a WARN > > under DEBUG_NET instead? The rest LGTM: > > > > Not really necessary for correctness per se, but if we try to free a > dma_addr that is not in a gen_pool (due to some other bug in the > code), then gen_pool_free ends up BUG_ON, crashing the kernel. > > Arguably gen_pool_free should not BUG_ON, but I think that's an old > API, and existing call sites have worked around the BUG_ON by doing a > gen_pool_has_addr check like I do here, for example kernel/dma/pool.c. > So I did not seek to change this established behavior. > > I think WARN seems fine to me, but maybe not under DEBUG_NET. I don't > want production code crashing due to this error, if it's OK with you. > > Unless I hear otherwise I'll add a WARN without debug here. WARN makes sense, I didn't know about the BUG_ON() hiding inside gen_pool :(
diff --git a/include/net/devmem.h b/include/net/devmem.h index 02925d897aa6..7b856a3540a9 100644 --- a/include/net/devmem.h +++ b/include/net/devmem.h @@ -72,7 +72,20 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding); int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, struct net_devmem_dmabuf_binding *binding); void dev_dmabuf_uninstall(struct net_device *dev); +struct net_iov * +net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding); +void net_devmem_free_dmabuf(struct net_iov *ppiov); #else +static inline struct net_iov * +net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding) +{ + return NULL; +} + +static inline void net_devmem_free_dmabuf(struct net_iov *ppiov) +{ +} + static inline void __net_devmem_dmabuf_binding_free(struct net_devmem_dmabuf_binding *binding) { diff --git a/include/net/netmem.h b/include/net/netmem.h index 41e96c2f94b5..0fbc0999091a 100644 --- a/include/net/netmem.h +++ b/include/net/netmem.h @@ -16,6 +16,23 @@ struct net_iov { struct dmabuf_genpool_chunk_owner *owner; }; +static inline struct dmabuf_genpool_chunk_owner * +net_iov_owner(const struct net_iov *niov) +{ + return niov->owner; +} + +static inline unsigned int net_iov_idx(const struct net_iov *niov) +{ + return niov - net_iov_owner(niov)->niovs; +} + +static inline struct net_devmem_dmabuf_binding * +net_iov_binding(const struct net_iov *niov) +{ + return net_iov_owner(niov)->binding; +} + /* netmem */ /** diff --git a/net/core/devmem.c b/net/core/devmem.c index b2b271a921c1..d636a8b34215 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -35,6 +35,14 @@ static void net_devmem_dmabuf_free_chunk_owner(struct gen_pool *genpool, kfree(owner); } +static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov) +{ + struct dmabuf_genpool_chunk_owner *owner = net_iov_owner(niov); + + return owner->base_dma_addr + + ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT); +} + void __net_devmem_dmabuf_binding_free(struct net_devmem_dmabuf_binding *binding) { size_t size, avail; @@ -57,6 +65,36 @@ void __net_devmem_dmabuf_binding_free(struct net_devmem_dmabuf_binding *binding) kfree(binding); } +struct net_iov * +net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding) +{ + struct dmabuf_genpool_chunk_owner *owner; + unsigned long dma_addr; + struct net_iov *niov; + ssize_t offset; + ssize_t index; + + dma_addr = gen_pool_alloc_owner(binding->chunk_pool, PAGE_SIZE, + (void **)&owner); + if (!dma_addr) + return NULL; + + offset = dma_addr - owner->base_dma_addr; + index = offset / PAGE_SIZE; + niov = &owner->niovs[index]; + + return niov; +} + +void net_devmem_free_dmabuf(struct net_iov *niov) +{ + struct net_devmem_dmabuf_binding *binding = net_iov_binding(niov); + unsigned long dma_addr = net_devmem_get_dma_addr(niov); + + if (gen_pool_has_addr(binding->chunk_pool, dma_addr, PAGE_SIZE)) + gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE); +} + void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding) { struct netdev_rx_queue *rxq;