mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	mm: add get_kernel_page[s] for pinning of kernel addresses for I/O
This patch adds two new APIs get_kernel_pages() and get_kernel_page() that may be used to pin a vector of kernel addresses for IO. The initial user is expected to be NFS for allowing pages to be written to swap using aops->direct_IO(). Strictly speaking, swap-over-NFS only needs to pin one page for IO but it makes sense to express the API in terms of a vector and add a helper for pinning single pages. Signed-off-by: Mel Gorman <mgorman@suse.de> Reviewed-by: Rik van Riel <riel@redhat.com> Cc: Christoph Hellwig <hch@infradead.org> Cc: David S. Miller <davem@davemloft.net> Cc: Eric B Munson <emunson@mgebm.net> Cc: Eric Paris <eparis@redhat.com> Cc: James Morris <jmorris@namei.org> Cc: Mel Gorman <mgorman@suse.de> Cc: Mike Christie <michaelc@cs.wisc.edu> Cc: Neil Brown <neilb@suse.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> Cc: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: Xiaotian Feng <dfeng@redhat.com> Cc: Mark Salter <msalter@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									f981c5950f
								
							
						
					
					
						commit
						18022c5d86
					
				
					 4 changed files with 61 additions and 0 deletions
				
			
		| 
						 | 
					@ -160,6 +160,7 @@ enum rq_flag_bits {
 | 
				
			||||||
	__REQ_FLUSH_SEQ,	/* request for flush sequence */
 | 
						__REQ_FLUSH_SEQ,	/* request for flush sequence */
 | 
				
			||||||
	__REQ_IO_STAT,		/* account I/O stat */
 | 
						__REQ_IO_STAT,		/* account I/O stat */
 | 
				
			||||||
	__REQ_MIXED_MERGE,	/* merge of different types, fail separately */
 | 
						__REQ_MIXED_MERGE,	/* merge of different types, fail separately */
 | 
				
			||||||
 | 
						__REQ_KERNEL, 		/* direct IO to kernel pages */
 | 
				
			||||||
	__REQ_NR_BITS,		/* stops here */
 | 
						__REQ_NR_BITS,		/* stops here */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,5 +202,6 @@ enum rq_flag_bits {
 | 
				
			||||||
#define REQ_IO_STAT		(1 << __REQ_IO_STAT)
 | 
					#define REQ_IO_STAT		(1 << __REQ_IO_STAT)
 | 
				
			||||||
#define REQ_MIXED_MERGE		(1 << __REQ_MIXED_MERGE)
 | 
					#define REQ_MIXED_MERGE		(1 << __REQ_MIXED_MERGE)
 | 
				
			||||||
#define REQ_SECURE		(1 << __REQ_SECURE)
 | 
					#define REQ_SECURE		(1 << __REQ_SECURE)
 | 
				
			||||||
 | 
					#define REQ_KERNEL		(1 << __REQ_KERNEL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* __LINUX_BLK_TYPES_H */
 | 
					#endif /* __LINUX_BLK_TYPES_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,6 +165,8 @@ struct inodes_stat_t {
 | 
				
			||||||
#define READ			0
 | 
					#define READ			0
 | 
				
			||||||
#define WRITE			RW_MASK
 | 
					#define WRITE			RW_MASK
 | 
				
			||||||
#define READA			RWA_MASK
 | 
					#define READA			RWA_MASK
 | 
				
			||||||
 | 
					#define KERNEL_READ		(READ|REQ_KERNEL)
 | 
				
			||||||
 | 
					#define KERNEL_WRITE		(WRITE|REQ_KERNEL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define READ_SYNC		(READ | REQ_SYNC)
 | 
					#define READ_SYNC		(READ | REQ_SYNC)
 | 
				
			||||||
#define WRITE_SYNC		(WRITE | REQ_SYNC | REQ_NOIDLE)
 | 
					#define WRITE_SYNC		(WRITE | REQ_SYNC | REQ_NOIDLE)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1019,6 +1019,10 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
 | 
				
			||||||
			struct page **pages, struct vm_area_struct **vmas);
 | 
								struct page **pages, struct vm_area_struct **vmas);
 | 
				
			||||||
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
 | 
					int get_user_pages_fast(unsigned long start, int nr_pages, int write,
 | 
				
			||||||
			struct page **pages);
 | 
								struct page **pages);
 | 
				
			||||||
 | 
					struct kvec;
 | 
				
			||||||
 | 
					int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
 | 
				
			||||||
 | 
								struct page **pages);
 | 
				
			||||||
 | 
					int get_kernel_page(unsigned long start, int write, struct page **pages);
 | 
				
			||||||
struct page *get_dump_page(unsigned long addr);
 | 
					struct page *get_dump_page(unsigned long addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int try_to_release_page(struct page * page, gfp_t gfp_mask);
 | 
					extern int try_to_release_page(struct page * page, gfp_t gfp_mask);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										53
									
								
								mm/swap.c
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								mm/swap.c
									
									
									
									
									
								
							| 
						 | 
					@ -236,6 +236,59 @@ void put_pages_list(struct list_head *pages)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(put_pages_list);
 | 
					EXPORT_SYMBOL(put_pages_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * get_kernel_pages() - pin kernel pages in memory
 | 
				
			||||||
 | 
					 * @kiov:	An array of struct kvec structures
 | 
				
			||||||
 | 
					 * @nr_segs:	number of segments to pin
 | 
				
			||||||
 | 
					 * @write:	pinning for read/write, currently ignored
 | 
				
			||||||
 | 
					 * @pages:	array that receives pointers to the pages pinned.
 | 
				
			||||||
 | 
					 *		Should be at least nr_segs long.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns number of pages pinned. This may be fewer than the number
 | 
				
			||||||
 | 
					 * requested. If nr_pages is 0 or negative, returns 0. If no pages
 | 
				
			||||||
 | 
					 * were pinned, returns -errno. Each page returned must be released
 | 
				
			||||||
 | 
					 * with a put_page() call when it is finished with.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write,
 | 
				
			||||||
 | 
							struct page **pages)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int seg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (seg = 0; seg < nr_segs; seg++) {
 | 
				
			||||||
 | 
							if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE))
 | 
				
			||||||
 | 
								return seg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* virt_to_page sanity checks the PFN */
 | 
				
			||||||
 | 
							pages[seg] = virt_to_page(kiov[seg].iov_base);
 | 
				
			||||||
 | 
							page_cache_get(pages[seg]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return seg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(get_kernel_pages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * get_kernel_page() - pin a kernel page in memory
 | 
				
			||||||
 | 
					 * @start:	starting kernel address
 | 
				
			||||||
 | 
					 * @write:	pinning for read/write, currently ignored
 | 
				
			||||||
 | 
					 * @pages:	array that receives pointer to the page pinned.
 | 
				
			||||||
 | 
					 *		Must be at least nr_segs long.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns 1 if page is pinned. If the page was not pinned, returns
 | 
				
			||||||
 | 
					 * -errno. The page returned must be released with a put_page() call
 | 
				
			||||||
 | 
					 * when it is finished with.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int get_kernel_page(unsigned long start, int write, struct page **pages)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct kvec kiov = {
 | 
				
			||||||
 | 
							.iov_base = (void *)start,
 | 
				
			||||||
 | 
							.iov_len = PAGE_SIZE
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return get_kernel_pages(&kiov, 1, write, pages);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(get_kernel_page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void pagevec_lru_move_fn(struct pagevec *pvec,
 | 
					static void pagevec_lru_move_fn(struct pagevec *pvec,
 | 
				
			||||||
	void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),
 | 
						void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),
 | 
				
			||||||
	void *arg)
 | 
						void *arg)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue