static void vmtruncate_list(struct vm_area_struct *mpnt, unsigned long pgoff, unsigned long partial) { do { struct mm_struct *mm = mpnt->vm_mm; unsigned long start = mpnt->vm_start; unsigned long end = mpnt->vm_end; unsigned long len = end - start; unsigned long diff; /* mapping wholly truncated? */ if (mpnt->vm_pgoff >= pgoff) { flush_cache_range(mm, start, end); zap_page_range(mm, start, len); flush_tlb_range(mm, start, end); continue; } /* mapping wholly unaffected? */ len = len >> PAGE_SHIFT; diff = pgoff - mpnt->vm_pgoff; if (diff >= len) continue; /* Ok, partially affected.. */ start += diff << PAGE_SHIFT; len = (len - diff) << PAGE_SHIFT; flush_cache_range(mm, start, end); zap_page_range(mm, start, len); flush_tlb_range(mm, start, end); } while ((mpnt = mpnt->vm_next_share) != NULL); }
/* * Handle all mappings that got truncated by a "truncate()" * system call. * * NOTE! We have to be ready to update the memory sharing * between the file and the memory map for a potential last * incomplete page. Ugly, but necessary. */ void vmtruncate(struct inode * inode, unsigned long offset) { struct vm_area_struct * mpnt; truncate_inode_pages(inode, offset); if (!inode->i_mmap) return; mpnt = inode->i_mmap; do { unsigned long start = mpnt->vm_start; unsigned long len = mpnt->vm_end - start; unsigned long diff; /* mapping wholly truncated? */ if (mpnt->vm_offset >= offset) { zap_page_range(mpnt->vm_mm, start, len); continue; } /* mapping wholly unaffected? */ diff = offset - mpnt->vm_offset; if (diff >= len) continue; /* Ok, partially affected.. */ start += diff; len = (len - diff) & PAGE_MASK; if (start & ~PAGE_MASK) { partial_clear(mpnt, start); start = (start + ~PAGE_MASK) & PAGE_MASK; } zap_page_range(mpnt->vm_mm, start, len); } while ((mpnt = mpnt->vm_next_share) != inode->i_mmap); }
static int move_page_tables(struct mm_struct * mm, unsigned long new_addr, unsigned long old_addr, unsigned long len) { unsigned long offset = len; flush_cache_range(mm, old_addr, old_addr + len); flush_tlb_range(mm, old_addr, old_addr + len); /* * This is not the clever way to do this, but we're taking the * easy way out on the assumption that most remappings will be * only a few pages.. This also makes error recovery easier. */ while (offset) { offset -= PAGE_SIZE; if (move_one_page(mm, old_addr + offset, new_addr + offset)) goto oops_we_failed; } return 0; /* * Ok, the move failed because we didn't have enough pages for * the new page table tree. This is unlikely, but we have to * take the possibility into account. In that case we just move * all the pages back (this will work, because we still have * the old page tables) */ oops_we_failed: flush_cache_range(mm, new_addr, new_addr + len); while ((offset += PAGE_SIZE) < len) move_one_page(mm, new_addr + offset, old_addr + offset); zap_page_range(mm, new_addr, new_addr + len); flush_tlb_range(mm, new_addr, new_addr + len); return -1; }
static void hw3d_vma_open(struct vm_area_struct *vma) { zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, NULL); }
/* * For fun, we are using the MMU for this. */ static inline size_t read_zero_pagealigned(char * buf, size_t size) { struct mm_struct *mm; struct vm_area_struct * vma; unsigned long addr=(unsigned long)buf; mm = current->mm; /* Oops, this was forgotten before. -ben */ down(&mm->mmap_sem); /* For private mappings, just map in zero pages. */ for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { unsigned long count; if (vma->vm_start > addr || (vma->vm_flags & VM_WRITE) == 0) goto out_up; if (vma->vm_flags & VM_SHARED) break; count = vma->vm_end - addr; if (count > size) count = size; flush_cache_range(mm, addr, addr + count); zap_page_range(mm, addr, count); zeromap_page_range(addr, count, PAGE_COPY); flush_tlb_range(mm, addr, addr + count); size -= count; buf += count; addr += count; if (size == 0) goto out_up; } up(&mm->mmap_sem); /* The shared case is hard. Let's do the conventional zeroing. */ do { unsigned long unwritten = clear_user(buf, PAGE_SIZE); if (unwritten) return size + unwritten - PAGE_SIZE; if (current->need_resched) schedule(); buf += PAGE_SIZE; size -= PAGE_SIZE; } while (size); return size; out_up: up(&mm->mmap_sem); return size; }
/* * Free the backing physical pages of bounds table 'bt_addr'. * Assume start...end is within that bounds table. */ static int zap_bt_entries(struct mm_struct *mm, unsigned long bt_addr, unsigned long start, unsigned long end) { struct vm_area_struct *vma; unsigned long addr, len; /* * Find the first overlapping vma. If vma->vm_start > start, there * will be a hole in the bounds table. This -EINVAL return will * cause a SIGSEGV. */ vma = find_vma(mm, start); if (!vma || vma->vm_start > start) return -EINVAL; /* * A NUMA policy on a VM_MPX VMA could cause this bouds table to * be split. So we need to look across the entire 'start -> end' * range of this bounds table, find all of the VM_MPX VMAs, and * zap only those. */ addr = start; while (vma && vma->vm_start < end) { /* * We followed a bounds directory entry down * here. If we find a non-MPX VMA, that's bad, * so stop immediately and return an error. This * probably results in a SIGSEGV. */ if (!is_mpx_vma(vma)) return -EINVAL; len = min(vma->vm_end, end) - addr; zap_page_range(vma, addr, len, NULL); vma = vma->vm_next; addr = vma->vm_start; } return 0; }