Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/module.h>
0003 #include <linux/gfp.h>
0004 #include <linux/slab.h>
0005 #include <linux/pagemap.h>
0006 #include <linux/highmem.h>
0007 #include <linux/ceph/pagelist.h>
0008 
0009 struct ceph_pagelist *ceph_pagelist_alloc(gfp_t gfp_flags)
0010 {
0011     struct ceph_pagelist *pl;
0012 
0013     pl = kmalloc(sizeof(*pl), gfp_flags);
0014     if (!pl)
0015         return NULL;
0016 
0017     INIT_LIST_HEAD(&pl->head);
0018     pl->mapped_tail = NULL;
0019     pl->length = 0;
0020     pl->room = 0;
0021     INIT_LIST_HEAD(&pl->free_list);
0022     pl->num_pages_free = 0;
0023     refcount_set(&pl->refcnt, 1);
0024 
0025     return pl;
0026 }
0027 EXPORT_SYMBOL(ceph_pagelist_alloc);
0028 
0029 static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
0030 {
0031     if (pl->mapped_tail) {
0032         struct page *page = list_entry(pl->head.prev, struct page, lru);
0033         kunmap(page);
0034         pl->mapped_tail = NULL;
0035     }
0036 }
0037 
0038 void ceph_pagelist_release(struct ceph_pagelist *pl)
0039 {
0040     if (!refcount_dec_and_test(&pl->refcnt))
0041         return;
0042     ceph_pagelist_unmap_tail(pl);
0043     while (!list_empty(&pl->head)) {
0044         struct page *page = list_first_entry(&pl->head, struct page,
0045                              lru);
0046         list_del(&page->lru);
0047         __free_page(page);
0048     }
0049     ceph_pagelist_free_reserve(pl);
0050     kfree(pl);
0051 }
0052 EXPORT_SYMBOL(ceph_pagelist_release);
0053 
0054 static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
0055 {
0056     struct page *page;
0057 
0058     if (!pl->num_pages_free) {
0059         page = __page_cache_alloc(GFP_NOFS);
0060     } else {
0061         page = list_first_entry(&pl->free_list, struct page, lru);
0062         list_del(&page->lru);
0063         --pl->num_pages_free;
0064     }
0065     if (!page)
0066         return -ENOMEM;
0067     pl->room += PAGE_SIZE;
0068     ceph_pagelist_unmap_tail(pl);
0069     list_add_tail(&page->lru, &pl->head);
0070     pl->mapped_tail = kmap(page);
0071     return 0;
0072 }
0073 
0074 int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
0075 {
0076     while (pl->room < len) {
0077         size_t bit = pl->room;
0078         int ret;
0079 
0080         memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK),
0081                buf, bit);
0082         pl->length += bit;
0083         pl->room -= bit;
0084         buf += bit;
0085         len -= bit;
0086         ret = ceph_pagelist_addpage(pl);
0087         if (ret)
0088             return ret;
0089     }
0090 
0091     memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK), buf, len);
0092     pl->length += len;
0093     pl->room -= len;
0094     return 0;
0095 }
0096 EXPORT_SYMBOL(ceph_pagelist_append);
0097 
0098 /* Allocate enough pages for a pagelist to append the given amount
0099  * of data without allocating.
0100  * Returns: 0 on success, -ENOMEM on error.
0101  */
0102 int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
0103 {
0104     if (space <= pl->room)
0105         return 0;
0106     space -= pl->room;
0107     space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT;   /* conv to num pages */
0108 
0109     while (space > pl->num_pages_free) {
0110         struct page *page = __page_cache_alloc(GFP_NOFS);
0111         if (!page)
0112             return -ENOMEM;
0113         list_add_tail(&page->lru, &pl->free_list);
0114         ++pl->num_pages_free;
0115     }
0116     return 0;
0117 }
0118 EXPORT_SYMBOL(ceph_pagelist_reserve);
0119 
0120 /* Free any pages that have been preallocated. */
0121 int ceph_pagelist_free_reserve(struct ceph_pagelist *pl)
0122 {
0123     while (!list_empty(&pl->free_list)) {
0124         struct page *page = list_first_entry(&pl->free_list,
0125                              struct page, lru);
0126         list_del(&page->lru);
0127         __free_page(page);
0128         --pl->num_pages_free;
0129     }
0130     BUG_ON(pl->num_pages_free);
0131     return 0;
0132 }
0133 EXPORT_SYMBOL(ceph_pagelist_free_reserve);
0134 
0135 /* Create a truncation point. */
0136 void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
0137                   struct ceph_pagelist_cursor *c)
0138 {
0139     c->pl = pl;
0140     c->page_lru = pl->head.prev;
0141     c->room = pl->room;
0142 }
0143 EXPORT_SYMBOL(ceph_pagelist_set_cursor);
0144 
0145 /* Truncate a pagelist to the given point. Move extra pages to reserve.
0146  * This won't sleep.
0147  * Returns: 0 on success,
0148  *          -EINVAL if the pagelist doesn't match the trunc point pagelist
0149  */
0150 int ceph_pagelist_truncate(struct ceph_pagelist *pl,
0151                struct ceph_pagelist_cursor *c)
0152 {
0153     struct page *page;
0154 
0155     if (pl != c->pl)
0156         return -EINVAL;
0157     ceph_pagelist_unmap_tail(pl);
0158     while (pl->head.prev != c->page_lru) {
0159         page = list_entry(pl->head.prev, struct page, lru);
0160         /* move from pagelist to reserve */
0161         list_move_tail(&page->lru, &pl->free_list);
0162         ++pl->num_pages_free;
0163     }
0164     pl->room = c->room;
0165     if (!list_empty(&pl->head)) {
0166         page = list_entry(pl->head.prev, struct page, lru);
0167         pl->mapped_tail = kmap(page);
0168     }
0169     return 0;
0170 }
0171 EXPORT_SYMBOL(ceph_pagelist_truncate);