return ret;
  }
 +EXPORT_SYMBOL(kmem_cache_alloc_lru);
  
 -void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
 +/**
 + * kmem_cache_alloc_node - Allocate an object on the specified node
 + * @s: The cache to allocate from.
 + * @gfpflags: See kmalloc().
 + * @node: node number of the target node.
 + *
 + * Identical to kmem_cache_alloc but it will allocate memory on the given
 + * node, which can improve the performance for cpu bound structures.
 + *
 + * Fallback to other node is possible if __GFP_THISNODE is not set.
 + *
 + * Return: pointer to the new object or %NULL in case of error
 + */
 +void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
  {
 -      return __kmem_cache_alloc_lru(s, NULL, gfpflags);
 +      void *ret = slab_alloc_node(s, NULL, gfpflags, node, _RET_IP_, s->object_size);
 +
 +      trace_kmem_cache_alloc(_RET_IP_, ret, s, gfpflags, node);
 +
 +      return ret;
  }
 -EXPORT_SYMBOL(kmem_cache_alloc);
 +EXPORT_SYMBOL(kmem_cache_alloc_node);
  
 -void *kmem_cache_alloc_lru(struct kmem_cache *s, struct list_lru *lru,
 -                         gfp_t gfpflags)
 +/*
 + * To avoid unnecessary overhead, we pass through large allocation requests
 + * directly to the page allocator. We use __GFP_COMP, because we will need to
 + * know the allocation order to free the pages properly in kfree.
 + */
 +static void *__kmalloc_large_node(size_t size, gfp_t flags, int node)
  {
-       struct page *page;
 -      return __kmem_cache_alloc_lru(s, lru, gfpflags);
++      struct folio *folio;
 +      void *ptr = NULL;
 +      unsigned int order = get_order(size);
 +
 +      if (unlikely(flags & GFP_SLAB_BUG_MASK))
 +              flags = kmalloc_fix_flags(flags);
 +
 +      flags |= __GFP_COMP;
-       page = alloc_pages_node(node, flags, order);
-       if (page) {
-               ptr = page_address(page);
-               mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
++      folio = (struct folio *)alloc_pages_node(node, flags, order);
++      if (folio) {
++              ptr = folio_address(folio);
++              lruvec_stat_mod_folio(folio, NR_SLAB_UNRECLAIMABLE_B,
 +                                    PAGE_SIZE << order);
 +      }
 +
 +      ptr = kasan_kmalloc_large(ptr, size, flags);
 +      /* As ptr might get tagged, call kmemleak hook after KASAN. */
 +      kmemleak_alloc(ptr, size, 1, flags);
 +      kmsan_kmalloc_large(ptr, size, flags);
 +
 +      return ptr;
  }
 -EXPORT_SYMBOL(kmem_cache_alloc_lru);
  
 -void *__kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags,
 -                            int node, size_t orig_size,
 -                            unsigned long caller)
 +void *kmalloc_large(size_t size, gfp_t flags)
  {
 -      return slab_alloc_node(s, NULL, gfpflags, node,
 -                             caller, orig_size);
 +      void *ret = __kmalloc_large_node(size, flags, NUMA_NO_NODE);
 +
 +      trace_kmalloc(_RET_IP_, ret, size, PAGE_SIZE << get_order(size),
 +                    flags, NUMA_NO_NODE);
 +      return ret;
  }
 +EXPORT_SYMBOL(kmalloc_large);
  
 -void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
 +void *kmalloc_large_node(size_t size, gfp_t flags, int node)
  {
 -      void *ret = slab_alloc_node(s, NULL, gfpflags, node, _RET_IP_, s->object_size);
 +      void *ret = __kmalloc_large_node(size, flags, node);
  
 -      trace_kmem_cache_alloc(_RET_IP_, ret, s, gfpflags, node);
 +      trace_kmalloc(_RET_IP_, ret, size, PAGE_SIZE << get_order(size),
 +                    flags, node);
 +      return ret;
 +}
 +EXPORT_SYMBOL(kmalloc_large_node);
  
 +static __always_inline
 +void *__do_kmalloc_node(size_t size, gfp_t flags, int node,
 +                      unsigned long caller)
 +{
 +      struct kmem_cache *s;
 +      void *ret;
 +
 +      if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {
 +              ret = __kmalloc_large_node(size, flags, node);
 +              trace_kmalloc(caller, ret, size,
 +                            PAGE_SIZE << get_order(size), flags, node);
 +              return ret;
 +      }
 +
 +      if (unlikely(!size))
 +              return ZERO_SIZE_PTR;
 +
 +      s = kmalloc_slab(size, flags, caller);
 +
 +      ret = slab_alloc_node(s, NULL, flags, node, caller, size);
 +      ret = kasan_kmalloc(s, ret, size, flags);
 +      trace_kmalloc(caller, ret, size, s->size, flags, node);
        return ret;
  }
 -EXPORT_SYMBOL(kmem_cache_alloc_node);
 +
 +void *__kmalloc_node(size_t size, gfp_t flags, int node)
 +{
 +      return __do_kmalloc_node(size, flags, node, _RET_IP_);
 +}
 +EXPORT_SYMBOL(__kmalloc_node);
 +
 +void *__kmalloc(size_t size, gfp_t flags)
 +{
 +      return __do_kmalloc_node(size, flags, NUMA_NO_NODE, _RET_IP_);
 +}
 +EXPORT_SYMBOL(__kmalloc);
 +
 +void *__kmalloc_node_track_caller(size_t size, gfp_t flags,
 +                                int node, unsigned long caller)
 +{
 +      return __do_kmalloc_node(size, flags, node, caller);
 +}
 +EXPORT_SYMBOL(__kmalloc_node_track_caller);
 +
 +void *kmalloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
 +{
 +      void *ret = slab_alloc_node(s, NULL, gfpflags, NUMA_NO_NODE,
 +                                          _RET_IP_, size);
 +
 +      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags, NUMA_NO_NODE);
 +
 +      ret = kasan_kmalloc(s, ret, size, gfpflags);
 +      return ret;
 +}
 +EXPORT_SYMBOL(kmalloc_trace);
 +
 +void *kmalloc_node_trace(struct kmem_cache *s, gfp_t gfpflags,
 +                       int node, size_t size)
 +{
 +      void *ret = slab_alloc_node(s, NULL, gfpflags, node, _RET_IP_, size);
 +
 +      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags, node);
 +
 +      ret = kasan_kmalloc(s, ret, size, gfpflags);
 +      return ret;
 +}
 +EXPORT_SYMBOL(kmalloc_node_trace);
  
  static noinline void free_to_partial_list(
        struct kmem_cache *s, struct slab *slab,
  }
  EXPORT_SYMBOL(kmem_cache_free);
  
-       mod_lruvec_page_state(folio_page(folio, 0), NR_SLAB_UNRECLAIMABLE_B,
 +static void free_large_kmalloc(struct folio *folio, void *object)
 +{
 +      unsigned int order = folio_order(folio);
 +
 +      if (WARN_ON_ONCE(order == 0))
 +              pr_warn_once("object pointer: 0x%p\n", object);
 +
 +      kmemleak_free(object);
 +      kasan_kfree_large(object);
 +      kmsan_kfree_large(object);
 +
-       __free_pages(folio_page(folio, 0), order);
++      lruvec_stat_mod_folio(folio, NR_SLAB_UNRECLAIMABLE_B,
 +                            -(PAGE_SIZE << order));
++      folio_put(folio);
 +}
 +
 +/**
 + * kfree - free previously allocated memory
 + * @object: pointer returned by kmalloc() or kmem_cache_alloc()
 + *
 + * If @object is NULL, no operation is performed.
 + */
 +void kfree(const void *object)
 +{
 +      struct folio *folio;
 +      struct slab *slab;
 +      struct kmem_cache *s;
 +      void *x = (void *)object;
 +
 +      trace_kfree(_RET_IP_, object);
 +
 +      if (unlikely(ZERO_OR_NULL_PTR(object)))
 +              return;
 +
 +      folio = virt_to_folio(object);
 +      if (unlikely(!folio_test_slab(folio))) {
 +              free_large_kmalloc(folio, (void *)object);
 +              return;
 +      }
 +
 +      slab = folio_slab(folio);
 +      s = slab->slab_cache;
 +      slab_free(s, slab, x, _RET_IP_);
 +}
 +EXPORT_SYMBOL(kfree);
 +
  struct detached_freelist {
        struct slab *slab;
        void *tail;