|
@@ -2272,9 +2272,17 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|
|
struct vm_area_struct *vma,
|
|
|
int acctflag)
|
|
|
{
|
|
|
- long ret = 0, chg;
|
|
|
+ long ret, chg;
|
|
|
struct hstate *h = hstate_inode(inode);
|
|
|
|
|
|
+ /*
|
|
|
+ * Only apply hugepage reservation if asked. At fault time, an
|
|
|
+ * attempt will be made for VM_NORESERVE to allocate a page
|
|
|
+ * and filesystem quota without using reserves
|
|
|
+ */
|
|
|
+ if (acctflag & VM_NORESERVE)
|
|
|
+ return 0;
|
|
|
+
|
|
|
/*
|
|
|
* Shared mappings base their reservation on the number of pages that
|
|
|
* are already allocated on behalf of the file. Private mappings need
|
|
@@ -2283,42 +2291,47 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|
|
*/
|
|
|
if (!vma || vma->vm_flags & VM_SHARED)
|
|
|
chg = region_chg(&inode->i_mapping->private_list, from, to);
|
|
|
- else
|
|
|
+ else {
|
|
|
+ struct resv_map *resv_map = resv_map_alloc();
|
|
|
+ if (!resv_map)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
chg = to - from;
|
|
|
|
|
|
+ set_vma_resv_map(vma, resv_map);
|
|
|
+ set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
|
|
|
+ }
|
|
|
+
|
|
|
if (chg < 0)
|
|
|
return chg;
|
|
|
|
|
|
+ /* There must be enough filesystem quota for the mapping */
|
|
|
if (hugetlb_get_quota(inode->i_mapping, chg))
|
|
|
return -ENOSPC;
|
|
|
|
|
|
/*
|
|
|
- * Only apply hugepage reservation if asked. We still have to
|
|
|
- * take the filesystem quota because it is an upper limit
|
|
|
- * defined for the mount and not necessarily memory as a whole
|
|
|
+ * Check enough hugepages are available for the reservation.
|
|
|
+ * Hand back the quota if there are not
|
|
|
*/
|
|
|
- if (acctflag & VM_NORESERVE) {
|
|
|
- reset_vma_resv_huge_pages(vma);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
ret = hugetlb_acct_memory(h, chg);
|
|
|
if (ret < 0) {
|
|
|
hugetlb_put_quota(inode->i_mapping, chg);
|
|
|
return ret;
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Account for the reservations made. Shared mappings record regions
|
|
|
+ * that have reservations as they are shared by multiple VMAs.
|
|
|
+ * When the last VMA disappears, the region map says how much
|
|
|
+ * the reservation was and the page cache tells how much of
|
|
|
+ * the reservation was consumed. Private mappings are per-VMA and
|
|
|
+ * only the consumed reservations are tracked. When the VMA
|
|
|
+ * disappears, the original reservation is the VMA size and the
|
|
|
+ * consumed reservations are stored in the map. Hence, nothing
|
|
|
+ * else has to be done for private mappings here
|
|
|
+ */
|
|
|
if (!vma || vma->vm_flags & VM_SHARED)
|
|
|
region_add(&inode->i_mapping->private_list, from, to);
|
|
|
- else {
|
|
|
- struct resv_map *resv_map = resv_map_alloc();
|
|
|
-
|
|
|
- if (!resv_map)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- set_vma_resv_map(vma, resv_map);
|
|
|
- set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
|
|
|
- }
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|