162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/module.h> 562306a36Sopenharmony_ci#include <linux/sched.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/file.h> 862306a36Sopenharmony_ci#include <linux/namei.h> 962306a36Sopenharmony_ci#include <linux/writeback.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/ceph/libceph.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_civoid ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci int i; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci for (i = 0; i < num_pages; i++) { 1862306a36Sopenharmony_ci if (dirty) 1962306a36Sopenharmony_ci set_page_dirty_lock(pages[i]); 2062306a36Sopenharmony_ci put_page(pages[i]); 2162306a36Sopenharmony_ci } 2262306a36Sopenharmony_ci kvfree(pages); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_put_page_vector); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_civoid ceph_release_page_vector(struct page **pages, int num_pages) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci int i; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci for (i = 0; i < num_pages; i++) 3162306a36Sopenharmony_ci __free_pages(pages[i], 0); 3262306a36Sopenharmony_ci kfree(pages); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_release_page_vector); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * allocate a vector new pages 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistruct page **ceph_alloc_page_vector(int num_pages, gfp_t flags) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct page **pages; 4262306a36Sopenharmony_ci int i; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci pages = kmalloc_array(num_pages, sizeof(*pages), flags); 4562306a36Sopenharmony_ci if (!pages) 4662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 4762306a36Sopenharmony_ci for (i = 0; i < num_pages; i++) { 4862306a36Sopenharmony_ci pages[i] = __page_cache_alloc(flags); 4962306a36Sopenharmony_ci if (pages[i] == NULL) { 5062306a36Sopenharmony_ci ceph_release_page_vector(pages, i); 5162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci return pages; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_alloc_page_vector); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * copy user data into a page vector 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ciint ceph_copy_user_to_page_vector(struct page **pages, 6262306a36Sopenharmony_ci const void __user *data, 6362306a36Sopenharmony_ci loff_t off, size_t len) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int i = 0; 6662306a36Sopenharmony_ci int po = off & ~PAGE_MASK; 6762306a36Sopenharmony_ci int left = len; 6862306a36Sopenharmony_ci int l, bad; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci while (left > 0) { 7162306a36Sopenharmony_ci l = min_t(int, PAGE_SIZE-po, left); 7262306a36Sopenharmony_ci bad = copy_from_user(page_address(pages[i]) + po, data, l); 7362306a36Sopenharmony_ci if (bad == l) 7462306a36Sopenharmony_ci return -EFAULT; 7562306a36Sopenharmony_ci data += l - bad; 7662306a36Sopenharmony_ci left -= l - bad; 7762306a36Sopenharmony_ci po += l - bad; 7862306a36Sopenharmony_ci if (po == PAGE_SIZE) { 7962306a36Sopenharmony_ci po = 0; 8062306a36Sopenharmony_ci i++; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci return len; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_copy_user_to_page_vector); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_civoid ceph_copy_to_page_vector(struct page **pages, 8862306a36Sopenharmony_ci const void *data, 8962306a36Sopenharmony_ci loff_t off, size_t len) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int i = 0; 9262306a36Sopenharmony_ci size_t po = off & ~PAGE_MASK; 9362306a36Sopenharmony_ci size_t left = len; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci while (left > 0) { 9662306a36Sopenharmony_ci size_t l = min_t(size_t, PAGE_SIZE-po, left); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci memcpy(page_address(pages[i]) + po, data, l); 9962306a36Sopenharmony_ci data += l; 10062306a36Sopenharmony_ci left -= l; 10162306a36Sopenharmony_ci po += l; 10262306a36Sopenharmony_ci if (po == PAGE_SIZE) { 10362306a36Sopenharmony_ci po = 0; 10462306a36Sopenharmony_ci i++; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_copy_to_page_vector); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_civoid ceph_copy_from_page_vector(struct page **pages, 11162306a36Sopenharmony_ci void *data, 11262306a36Sopenharmony_ci loff_t off, size_t len) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int i = 0; 11562306a36Sopenharmony_ci size_t po = off & ~PAGE_MASK; 11662306a36Sopenharmony_ci size_t left = len; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci while (left > 0) { 11962306a36Sopenharmony_ci size_t l = min_t(size_t, PAGE_SIZE-po, left); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci memcpy(data, page_address(pages[i]) + po, l); 12262306a36Sopenharmony_ci data += l; 12362306a36Sopenharmony_ci left -= l; 12462306a36Sopenharmony_ci po += l; 12562306a36Sopenharmony_ci if (po == PAGE_SIZE) { 12662306a36Sopenharmony_ci po = 0; 12762306a36Sopenharmony_ci i++; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_copy_from_page_vector); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* 13462306a36Sopenharmony_ci * Zero an extent within a page vector. Offset is relative to the 13562306a36Sopenharmony_ci * start of the first page. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_civoid ceph_zero_page_vector_range(int off, int len, struct page **pages) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int i = off >> PAGE_SHIFT; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci off &= ~PAGE_MASK; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci dout("zero_page_vector_page %u~%u\n", off, len); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* leading partial page? */ 14662306a36Sopenharmony_ci if (off) { 14762306a36Sopenharmony_ci int end = min((int)PAGE_SIZE, off + len); 14862306a36Sopenharmony_ci dout("zeroing %d %p head from %d\n", i, pages[i], 14962306a36Sopenharmony_ci (int)off); 15062306a36Sopenharmony_ci zero_user_segment(pages[i], off, end); 15162306a36Sopenharmony_ci len -= (end - off); 15262306a36Sopenharmony_ci i++; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci while (len >= PAGE_SIZE) { 15562306a36Sopenharmony_ci dout("zeroing %d %p len=%d\n", i, pages[i], len); 15662306a36Sopenharmony_ci zero_user_segment(pages[i], 0, PAGE_SIZE); 15762306a36Sopenharmony_ci len -= PAGE_SIZE; 15862306a36Sopenharmony_ci i++; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci /* trailing partial page? */ 16162306a36Sopenharmony_ci if (len) { 16262306a36Sopenharmony_ci dout("zeroing %d %p tail to %d\n", i, pages[i], (int)len); 16362306a36Sopenharmony_ci zero_user_segment(pages[i], 0, len); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ciEXPORT_SYMBOL(ceph_zero_page_vector_range); 167