On 2021/7/21 22:06, Alexander Duyck wrote: > On Wed, Jul 21, 2021 at 1:15 AM Yunsheng Lin <linyunsheng@xxxxxxxxxx> wrote: >> >> On 2021/7/20 23:43, Alexander Duyck wrote: >>> On Mon, Jul 19, 2021 at 8:36 PM Yunsheng Lin <linyunsheng@xxxxxxxxxx> wrote: >>>> >>>> For 32 bit systems with 64 bit dma, dma_addr[1] is used to >>>> store the upper 32 bit dma addr, those system should be rare >>>> those days. >>>> >>>> For normal system, the dma_addr[1] in 'struct page' is not >>>> used, so we can reuse dma_addr[1] for storing frag count, >>>> which means how many frags this page might be splited to. >>>> >>>> In order to simplify the page frag support in the page pool, >>>> the PAGE_POOL_DMA_USE_PP_FRAG_COUNT macro is added to indicate >>>> the 32 bit systems with 64 bit dma, and the page frag support >>>> in page pool is disabled for such system. >>>> >>>> The newly added page_pool_set_frag_count() is called to reserve >>>> the maximum frag count before any page frag is passed to the >>>> user. The page_pool_atomic_sub_frag_count_return() is called >>>> when user is done with the page frag. >>>> >>>> Signed-off-by: Yunsheng Lin <linyunsheng@xxxxxxxxxx> >>>> --- >>>> include/linux/mm_types.h | 18 +++++++++++++----- >>>> include/net/page_pool.h | 41 ++++++++++++++++++++++++++++++++++------- >>>> net/core/page_pool.c | 4 ++++ >>>> 3 files changed, 51 insertions(+), 12 deletions(-) >>>> >>> >>> <snip> >>> >>>> +static inline long page_pool_atomic_sub_frag_count_return(struct page *page, >>>> + long nr) >>>> +{ >>>> + long frag_count = atomic_long_read(&page->pp_frag_count); >>>> + long ret; >>>> + >>>> + if (frag_count == nr) >>>> + return 0; >>>> + >>>> + ret = atomic_long_sub_return(nr, &page->pp_frag_count); >>>> + WARN_ON(ret < 0); >>>> + return ret; >>>> } >>>> >>> >>> So this should just be an atomic_long_sub_return call. You should get >>> rid of the atomic_long_read portion of this as it can cover up >>> reference count errors. >> >> atomic_long_sub_return() is used to avoid one possible cache bouncing and >> barrrier caused by the last user. > > I assume you mean "atomic_long_read()" here. Yes, sorry for the confusion. > >> You are right that that may cover up the reference count errors. How about >> something like below: >> >> static inline long page_pool_atomic_sub_frag_count_return(struct page *page, >> long nr) >> { >> #ifdef CONFIG_DEBUG_PAGE_REF >> long ret = atomic_long_sub_return(nr, &page->pp_frag_count); >> >> WARN_ON(ret < 0); >> >> return ret; >> #else >> if (atomic_long_read(&page->pp_frag_count) == nr) >> return 0; >> >> return atomic_long_sub_return(nr, &page->pp_frag_count); >> #end >> } >> >> Or any better suggestion? > > So the one thing I might change would be to make it so that you only > do the atomic_long_read if nr is a constant via __builtin_constant_p. > That way you would be performing the comparison in > __page_pool_put_page and in the cases of freeing or draining the > page_frags you would be using the atomic_long_sub_return which should > be paths where you would not expect it to match or that are slowpath > anyway. > > Also I would keep the WARN_ON in both paths just to be on the safe side. If I understand it correctly, we should change it as below, right? static inline long page_pool_atomic_sub_frag_count_return(struct page *page, long nr) { long ret; /* As suggested by Alexander, atomic_long_read() may cover up the * reference count errors, so avoid calling atomic_long_read() in * the cases of freeing or draining the page_frags, where we would * not expect it to match or that are slowpath anyway. */ if (__builtin_constant_p(nr) && atomic_long_read(&page->pp_frag_count) == nr) return 0; ret = atomic_long_sub_return(nr, &page->pp_frag_count); WARN_ON(ret < 0); return ret; } > . >