net: page_pool: expose page pool stats via netlink
[linux-2.6-microblaze.git] / net / core / page_pool.c
index 5e409b9..3d0938a 100644 (file)
@@ -23,6 +23,8 @@
 
 #include <trace/events/page_pool.h>
 
+#include "page_pool_priv.h"
+
 #define DEFER_TIME (msecs_to_jiffies(1000))
 #define DEFER_WARN_INTERVAL (60 * HZ)
 
@@ -69,7 +71,7 @@ static const char pp_stats[][ETH_GSTRING_LEN] = {
  * is passed to this API which is filled in. The caller can then report
  * those stats to the user (perhaps via ethtool, debugfs, etc.).
  */
-bool page_pool_get_stats(struct page_pool *pool,
+bool page_pool_get_stats(const struct page_pool *pool,
                         struct page_pool_stats *stats)
 {
        int cpu = 0;
@@ -173,7 +175,8 @@ static int page_pool_init(struct page_pool *pool,
 {
        unsigned int ring_qsize = 1024; /* Default */
 
-       memcpy(&pool->p, params, sizeof(pool->p));
+       memcpy(&pool->p, &params->fast, sizeof(pool->p));
+       memcpy(&pool->slow, &params->slow, sizeof(pool->slow));
 
        /* Validate only known flags were used */
        if (pool->p.flags & ~(PP_FLAG_ALL))
@@ -211,14 +214,20 @@ static int page_pool_init(struct page_pool *pool,
                 */
        }
 
+       pool->has_init_callback = !!pool->slow.init_callback;
+
 #ifdef CONFIG_PAGE_POOL_STATS
        pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats);
        if (!pool->recycle_stats)
                return -ENOMEM;
 #endif
 
-       if (ptr_ring_init(&pool->ring, ring_qsize, GFP_KERNEL) < 0)
+       if (ptr_ring_init(&pool->ring, ring_qsize, GFP_KERNEL) < 0) {
+#ifdef CONFIG_PAGE_POOL_STATS
+               free_percpu(pool->recycle_stats);
+#endif
                return -ENOMEM;
+       }
 
        atomic_set(&pool->pages_state_release_cnt, 0);
 
@@ -231,6 +240,18 @@ static int page_pool_init(struct page_pool *pool,
        return 0;
 }
 
+static void page_pool_uninit(struct page_pool *pool)
+{
+       ptr_ring_cleanup(&pool->ring, NULL);
+
+       if (pool->p.flags & PP_FLAG_DMA_MAP)
+               put_device(pool->p.dev);
+
+#ifdef CONFIG_PAGE_POOL_STATS
+       free_percpu(pool->recycle_stats);
+#endif
+}
+
 /**
  * page_pool_create() - create a page pool.
  * @params: parameters, see struct page_pool_params
@@ -245,13 +266,21 @@ struct page_pool *page_pool_create(const struct page_pool_params *params)
                return ERR_PTR(-ENOMEM);
 
        err = page_pool_init(pool, params);
-       if (err < 0) {
-               pr_warn("%s() gave up with errno %d\n", __func__, err);
-               kfree(pool);
-               return ERR_PTR(err);
-       }
+       if (err < 0)
+               goto err_free;
+
+       err = page_pool_list(pool);
+       if (err)
+               goto err_uninit;
 
        return pool;
+
+err_uninit:
+       page_pool_uninit(pool);
+err_free:
+       pr_warn("%s() gave up with errno %d\n", __func__, err);
+       kfree(pool);
+       return ERR_PTR(err);
 }
 EXPORT_SYMBOL(page_pool_create);
 
@@ -384,8 +413,8 @@ static void page_pool_set_pp_info(struct page_pool *pool,
         * the overhead is negligible.
         */
        page_pool_fragment_page(page, 1);
-       if (pool->p.init_callback)
-               pool->p.init_callback(page, pool->p.init_arg);
+       if (pool->has_init_callback)
+               pool->slow.init_callback(page, pool->slow.init_arg);
 }
 
 static void page_pool_clear_pp_info(struct page *page)
@@ -500,7 +529,7 @@ EXPORT_SYMBOL(page_pool_alloc_pages);
  */
 #define _distance(a, b)        (s32)((a) - (b))
 
-static s32 page_pool_inflight(struct page_pool *pool)
+s32 page_pool_inflight(const struct page_pool *pool, bool strict)
 {
        u32 release_cnt = atomic_read(&pool->pages_state_release_cnt);
        u32 hold_cnt = READ_ONCE(pool->pages_state_hold_cnt);
@@ -508,8 +537,13 @@ static s32 page_pool_inflight(struct page_pool *pool)
 
        inflight = _distance(hold_cnt, release_cnt);
 
-       trace_page_pool_release(pool, inflight, hold_cnt, release_cnt);
-       WARN(inflight < 0, "Negative(%d) inflight packet-pages", inflight);
+       if (strict) {
+               trace_page_pool_release(pool, inflight, hold_cnt, release_cnt);
+               WARN(inflight < 0, "Negative(%d) inflight packet-pages",
+                    inflight);
+       } else {
+               inflight = max(0, inflight);
+       }
 
        return inflight;
 }
@@ -814,14 +848,8 @@ static void __page_pool_destroy(struct page_pool *pool)
        if (pool->disconnect)
                pool->disconnect(pool);
 
-       ptr_ring_cleanup(&pool->ring, NULL);
-
-       if (pool->p.flags & PP_FLAG_DMA_MAP)
-               put_device(pool->p.dev);
-
-#ifdef CONFIG_PAGE_POOL_STATS
-       free_percpu(pool->recycle_stats);
-#endif
+       page_pool_unlist(pool);
+       page_pool_uninit(pool);
        kfree(pool);
 }
 
@@ -858,7 +886,7 @@ static int page_pool_release(struct page_pool *pool)
        int inflight;
 
        page_pool_scrub(pool);
-       inflight = page_pool_inflight(pool);
+       inflight = page_pool_inflight(pool, true);
        if (!inflight)
                __page_pool_destroy(pool);
 
@@ -925,6 +953,7 @@ void page_pool_destroy(struct page_pool *pool)
        if (!page_pool_release(pool))
                return;
 
+       page_pool_detached(pool);
        pool->defer_start = jiffies;
        pool->defer_warn  = jiffies + DEFER_WARN_INTERVAL;