Commit a145dd411eb28c83ee4bb68b66f62c326c0f764e
1 parent
f8e988436b
Exists in
master
and in
4 other branches
VM: add "vm_insert_page()" function
This is what a lot of drivers will actually want to use to insert individual pages into a user VMA. It doesn't have the old PageReserved restrictions of remap_pfn_range(), and it doesn't complain about partial remappings. The page you insert needs to be a nice clean kernel allocation, so you can't insert arbitrary page mappings with this, but that's not what people want. Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 2 changed files with 35 additions and 2 deletions Side-by-side Diff
include/linux/mm.h
... | ... | @@ -956,6 +956,7 @@ |
956 | 956 | unsigned long vmalloc_to_pfn(void *addr); |
957 | 957 | int remap_pfn_range(struct vm_area_struct *, unsigned long addr, |
958 | 958 | unsigned long pfn, unsigned long size, pgprot_t); |
959 | +int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *); | |
959 | 960 | |
960 | 961 | struct page *follow_page(struct vm_area_struct *, unsigned long address, |
961 | 962 | unsigned int foll_flags); |
mm/memory.c
... | ... | @@ -1172,7 +1172,7 @@ |
1172 | 1172 | spinlock_t *ptl; |
1173 | 1173 | |
1174 | 1174 | retval = -EINVAL; |
1175 | - if (PageAnon(page) || !PageReserved(page)) | |
1175 | + if (PageAnon(page)) | |
1176 | 1176 | goto out; |
1177 | 1177 | retval = -ENOMEM; |
1178 | 1178 | flush_dcache_page(page); |
... | ... | @@ -1197,6 +1197,35 @@ |
1197 | 1197 | } |
1198 | 1198 | |
1199 | 1199 | /* |
1200 | + * This allows drivers to insert individual pages they've allocated | |
1201 | + * into a user vma. | |
1202 | + * | |
1203 | + * The page has to be a nice clean _individual_ kernel allocation. | |
1204 | + * If you allocate a compound page, you need to have marked it as | |
1205 | + * such (__GFP_COMP), or manually just split the page up yourself | |
1206 | + * (which is mainly an issue of doing "set_page_count(page, 1)" for | |
1207 | + * each sub-page, and then freeing them one by one when you free | |
1208 | + * them rather than freeing it as a compound page). | |
1209 | + * | |
1210 | + * NOTE! Traditionally this was done with "remap_pfn_range()" which | |
1211 | + * took an arbitrary page protection parameter. This doesn't allow | |
1212 | + * that. Your vma protection will have to be set up correctly, which | |
1213 | + * means that if you want a shared writable mapping, you'd better | |
1214 | + * ask for a shared writable mapping! | |
1215 | + * | |
1216 | + * The page does not need to be reserved. | |
1217 | + */ | |
1218 | +int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *page) | |
1219 | +{ | |
1220 | + if (addr < vma->vm_start || addr >= vma->vm_end) | |
1221 | + return -EFAULT; | |
1222 | + if (!page_count(page)) | |
1223 | + return -EINVAL; | |
1224 | + return insert_page(vma->vm_mm, addr, page, vma->vm_page_prot); | |
1225 | +} | |
1226 | +EXPORT_SYMBOL_GPL(vm_insert_page); | |
1227 | + | |
1228 | +/* | |
1200 | 1229 | * Somebody does a pfn remapping that doesn't actually work as a vma. |
1201 | 1230 | * |
1202 | 1231 | * Do it as individual pages instead, and warn about it. It's bad form, |
1203 | 1232 | |
... | ... | @@ -1225,8 +1254,11 @@ |
1225 | 1254 | if (!pfn_valid(pfn)) |
1226 | 1255 | return -EINVAL; |
1227 | 1256 | |
1228 | - retval = 0; | |
1229 | 1257 | page = pfn_to_page(pfn); |
1258 | + if (!PageReserved(page)) | |
1259 | + return -EINVAL; | |
1260 | + | |
1261 | + retval = 0; | |
1230 | 1262 | while (start < end) { |
1231 | 1263 | retval = insert_page(vma->vm_mm, start, page, prot); |
1232 | 1264 | if (retval < 0) |