| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | #include <linux/ceph/ceph_debug.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/file.h>
 | 
					
						
							|  |  |  | #include <linux/namei.h>
 | 
					
						
							|  |  |  | #include <linux/writeback.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/ceph/libceph.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * build a vector of user pages | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | struct page **ceph_get_direct_page_vector(const void __user *data, | 
					
						
							| 
									
										
										
										
											2010-12-15 20:45:41 -08:00
										 |  |  | 					  int num_pages, bool write_page) | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct page **pages; | 
					
						
							| 
									
										
										
										
											2011-03-02 16:55:21 -08:00
										 |  |  | 	int got = 0; | 
					
						
							|  |  |  | 	int rc = 0; | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pages = kmalloc(sizeof(*pages) * num_pages, GFP_NOFS); | 
					
						
							|  |  |  | 	if (!pages) | 
					
						
							|  |  |  | 		return ERR_PTR(-ENOMEM); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-02 16:55:21 -08:00
										 |  |  | 	while (got < num_pages) { | 
					
						
							| 
									
										
										
										
											2015-02-11 15:27:26 -08:00
										 |  |  | 		rc = get_user_pages_unlocked(current, current->mm, | 
					
						
							| 
									
										
										
										
											2011-03-02 16:55:21 -08:00
										 |  |  | 		    (unsigned long)data + ((unsigned long)got * PAGE_SIZE), | 
					
						
							| 
									
										
										
										
											2015-02-11 15:27:26 -08:00
										 |  |  | 		    num_pages - got, write_page, 0, pages + got); | 
					
						
							| 
									
										
										
										
											2011-03-02 16:55:21 -08:00
										 |  |  | 		if (rc < 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		BUG_ON(rc == 0); | 
					
						
							|  |  |  | 		got += rc; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (rc < 0) | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	return pages; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | fail: | 
					
						
							| 
									
										
										
										
											2011-03-02 16:55:21 -08:00
										 |  |  | 	ceph_put_page_vector(pages, got, false); | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 	return ERR_PTR(rc); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_get_direct_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-15 20:45:41 -08:00
										 |  |  | void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-15 20:45:41 -08:00
										 |  |  | 	for (i = 0; i < num_pages; i++) { | 
					
						
							|  |  |  | 		if (dirty) | 
					
						
							|  |  |  | 			set_page_dirty_lock(pages[i]); | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 		put_page(pages[i]); | 
					
						
							| 
									
										
										
										
											2010-12-15 20:45:41 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-04-03 22:31:22 -04:00
										 |  |  | 	if (is_vmalloc_addr(pages)) | 
					
						
							|  |  |  | 		vfree(pages); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		kfree(pages); | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_put_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ceph_release_page_vector(struct page **pages, int num_pages) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < num_pages; i++) | 
					
						
							|  |  |  | 		__free_pages(pages[i], 0); | 
					
						
							|  |  |  | 	kfree(pages); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_release_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * allocate a vector new pages | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct page **pages; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pages = kmalloc(sizeof(*pages) * num_pages, flags); | 
					
						
							|  |  |  | 	if (!pages) | 
					
						
							|  |  |  | 		return ERR_PTR(-ENOMEM); | 
					
						
							|  |  |  | 	for (i = 0; i < num_pages; i++) { | 
					
						
							|  |  |  | 		pages[i] = __page_cache_alloc(flags); | 
					
						
							|  |  |  | 		if (pages[i] == NULL) { | 
					
						
							|  |  |  | 			ceph_release_page_vector(pages, i); | 
					
						
							|  |  |  | 			return ERR_PTR(-ENOMEM); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return pages; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_alloc_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * copy user data into a page vector | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int ceph_copy_user_to_page_vector(struct page **pages, | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | 					 const void __user *data, | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 					 loff_t off, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	int po = off & ~PAGE_CACHE_MASK; | 
					
						
							|  |  |  | 	int left = len; | 
					
						
							|  |  |  | 	int l, bad; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (left > 0) { | 
					
						
							|  |  |  | 		l = min_t(int, PAGE_CACHE_SIZE-po, left); | 
					
						
							|  |  |  | 		bad = copy_from_user(page_address(pages[i]) + po, data, l); | 
					
						
							|  |  |  | 		if (bad == l) | 
					
						
							|  |  |  | 			return -EFAULT; | 
					
						
							|  |  |  | 		data += l - bad; | 
					
						
							|  |  |  | 		left -= l - bad; | 
					
						
							|  |  |  | 		po += l - bad; | 
					
						
							|  |  |  | 		if (po == PAGE_CACHE_SIZE) { | 
					
						
							|  |  |  | 			po = 0; | 
					
						
							|  |  |  | 			i++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_copy_user_to_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | void ceph_copy_to_page_vector(struct page **pages, | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | 				    const void *data, | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 				    loff_t off, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	size_t po = off & ~PAGE_CACHE_MASK; | 
					
						
							|  |  |  | 	size_t left = len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (left > 0) { | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | 		size_t l = min_t(size_t, PAGE_CACHE_SIZE-po, left); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 		memcpy(page_address(pages[i]) + po, data, l); | 
					
						
							|  |  |  | 		data += l; | 
					
						
							|  |  |  | 		left -= l; | 
					
						
							|  |  |  | 		po += l; | 
					
						
							|  |  |  | 		if (po == PAGE_CACHE_SIZE) { | 
					
						
							|  |  |  | 			po = 0; | 
					
						
							|  |  |  | 			i++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_copy_to_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | void ceph_copy_from_page_vector(struct page **pages, | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | 				    void *data, | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 				    loff_t off, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	size_t po = off & ~PAGE_CACHE_MASK; | 
					
						
							|  |  |  | 	size_t left = len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (left > 0) { | 
					
						
							| 
									
										
										
										
											2013-02-06 13:11:38 -06:00
										 |  |  | 		size_t l = min_t(size_t, PAGE_CACHE_SIZE-po, left); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-06 15:14:15 -07:00
										 |  |  | 		memcpy(data, page_address(pages[i]) + po, l); | 
					
						
							|  |  |  | 		data += l; | 
					
						
							|  |  |  | 		left -= l; | 
					
						
							|  |  |  | 		po += l; | 
					
						
							|  |  |  | 		if (po == PAGE_CACHE_SIZE) { | 
					
						
							|  |  |  | 			po = 0; | 
					
						
							|  |  |  | 			i++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_copy_from_page_vector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Zero an extent within a page vector.  Offset is relative to the | 
					
						
							|  |  |  |  * start of the first page. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void ceph_zero_page_vector_range(int off, int len, struct page **pages) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = off >> PAGE_CACHE_SHIFT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	off &= ~PAGE_CACHE_MASK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dout("zero_page_vector_page %u~%u\n", off, len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* leading partial page? */ | 
					
						
							|  |  |  | 	if (off) { | 
					
						
							|  |  |  | 		int end = min((int)PAGE_CACHE_SIZE, off + len); | 
					
						
							|  |  |  | 		dout("zeroing %d %p head from %d\n", i, pages[i], | 
					
						
							|  |  |  | 		     (int)off); | 
					
						
							|  |  |  | 		zero_user_segment(pages[i], off, end); | 
					
						
							|  |  |  | 		len -= (end - off); | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	while (len >= PAGE_CACHE_SIZE) { | 
					
						
							|  |  |  | 		dout("zeroing %d %p len=%d\n", i, pages[i], len); | 
					
						
							|  |  |  | 		zero_user_segment(pages[i], 0, PAGE_CACHE_SIZE); | 
					
						
							|  |  |  | 		len -= PAGE_CACHE_SIZE; | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* trailing partial page? */ | 
					
						
							|  |  |  | 	if (len) { | 
					
						
							|  |  |  | 		dout("zeroing %d %p tail to %d\n", i, pages[i], (int)len); | 
					
						
							|  |  |  | 		zero_user_segment(pages[i], 0, len); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(ceph_zero_page_vector_range); | 
					
						
							|  |  |  | 
 |