dm: hugetlb: add file lock to make sure huge page reserve atomic

Currently, DM only access /sys/kernel/mm/hugepages/hugepages-2048kB/
entries according to its own huge page requirement. So it could have
following race issue:

         DM1                                   DM2
      read nr pages
                                            read nr pages
                                            write DM2 nr pages
      write DM1 nr pages

Suppose we should write DM1 + DM2 nr page to kernel sysfs interface
to reserve enough huge page (DM1 + DM2). But actually only reserve
huge page requested by DM1.  Which could trigger one VM can't boot.
We can easily hit this issue if we enable multiple UOS auto boot
because more than one VM are started at almost same time.

We add file lock to make sure huge page reserving in DM atomic.

Tracked-On: #3729
Signed-off-by: Yin Fengwei <fengwei.yin@intel.com>
Acked-by: Wang Yu <yu1.wang@intel.com>
This commit is contained in:
Yin Fengwei 2019-09-24 13:29:52 +08:00 committed by wenlingz
parent 9ddcb3dde8
commit 9456d91b76
1 changed files with 55 additions and 2 deletions

View File

@ -36,6 +36,7 @@
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <log.h>
#include "vmmapi.h"
@ -60,6 +61,17 @@ extern char *vmname;
#define SYS_NR_HUGEPAGES "nr_hugepages"
#define SYS_FREE_HUGEPAGES "free_hugepages"
/* File used for lock between different processes access to hugetlbfs.
* We observed when access hugetlbfs from different process to allocate
* huge page at the same time could fail. So use file lock here to make
* sure hugetlbfs is accessed sequentially.
*
* We use file range (0..9) for hugetlbfs access lock.
*/
#define ACRN_HUGETLB_LOCK_FILE "/run/hugepage/acrn/lock"
#define LOCK_OFFSET_START 0
#define LOCK_OFFSET_END 10
/* hugetlb_info record private information for one specific hugetlbfs:
* - mounted: is hugetlbfs mounted for below mount_path
* - mount_path: hugetlbfs mount path
@ -124,6 +136,35 @@ static struct hugetlb_info hugetlb_priv[HUGETLB_LV_MAX] = {
static void *ptr;
static size_t total_size;
static int hugetlb_lv_max;
static int lock_fd;
static int lock_acrn_hugetlb(void)
{
int ret;
ret = lockf(lock_fd, F_LOCK, LOCK_OFFSET_END);
if (ret < 0) {
pr_err("lock acrn hugetlb failed with errno: %d\n", errno);
return ret;
}
return 0;
}
static int unlock_acrn_hugetlb(void)
{
int ret;
ret = lockf(lock_fd, F_ULOCK, LOCK_OFFSET_END);
if (ret < 0) {
pr_err("lock acrn hugetlb failed with errno: %d\n", errno);
return ret;
}
return 0;
}
static int open_hugetlbfs(struct vmctx *ctx, int level)
{
@ -603,7 +644,6 @@ static bool hugetlb_reserve_pages(void)
return true;
}
bool init_hugetlb(void)
{
int level;
@ -625,6 +665,12 @@ bool init_hugetlb(void)
else if (level == HUGETLB_LV1) /* mount fail for level 2 */
printf("WARNING: only level 1 hugetlb supported");
lock_fd = open(ACRN_HUGETLB_LOCK_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (lock_fd < 0) {
return false;
}
lseek(lock_fd, SEEK_SET, LOCK_OFFSET_START);
hugetlb_lv_max = level;
return true;
@ -637,6 +683,8 @@ void uninit_hugetlb(void)
umount_hugetlbfs(level);
rm_hugetlb_dirs(level);
}
close(lock_fd);
}
int hugetlb_setup_memory(struct vmctx *ctx)
@ -695,8 +743,12 @@ int hugetlb_setup_memory(struct vmctx *ctx)
/* it will check each level memory need */
has_gap = hugetlb_check_memgap();
if (has_gap) {
if (!hugetlb_reserve_pages())
lock_acrn_hugetlb();
if (!hugetlb_reserve_pages()) {
unlock_acrn_hugetlb();
goto err;
}
unlock_acrn_hugetlb();
}
/* align up total size with huge page size for vma alignment */
@ -802,6 +854,7 @@ err:
for (level = HUGETLB_LV1; level < hugetlb_lv_max; level++) {
close_hugetlbfs(level);
}
return -ENOMEM;
}