Fix file locking in EAL memory
cover Fix file locking in EAL memory
This patchset replaces the fcntl()-based locking used in the original DPDK memory hotplug patchset, to an flock()- and lockfile-based implementation, due to numerous (well, one, really) problems with how fcntl() locks work.
Long story short, fcntl() locks will be dropped if any fd referring to locked file, is closed - even if it's not the last fd, even if it wasn't even the fd that was used to lock the file in the first place, even if it wasn't you who closed that fd, but some other library.
This patchset corrects this saddening design defect in the original implementation.
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+, so we cannot rely on them if we want to support old kernels. Hence, we use per-segment lockfiles. The number of file descriptors we open does not end up more than in non-single file segments case - we still open the same amount of files (a file per page), plus a file per memseg list.
Additionally, since flock() is not atomic, we also lock the hugepage dir to prevent multiple processes from concurrently performing operations on hugetlbfs mounts.
If you know of a more enlightened way of fixing this limitation, you are certainly welcome to comment :)
Anatoly Burakov (2):
mem: add memalloc init stage
mem: revert to using flock() and add per-segment lockfiles
lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 + lib/librte_eal/common/eal_common_memory.c | 3 + lib/librte_eal/common/eal_filesystem.h | 18 + lib/librte_eal/common/eal_memalloc.h | 3 + lib/librte_eal/linuxapp/eal/eal_hugepage_info.c | 28 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 604 +++++++++++++++--------- lib/librte_eal/linuxapp/eal/eal_memory.c | 22 +- 7 files changed, 420 insertions(+), 264 deletions(-)
Signature · 1 line
2.7.4
PATCH 1/2 mem: add memalloc init stage
Currently, memseg lists for secondary process are allocated on sync (triggered by init), when they are accessed for the first time. Move this initialization to a separate init stage for memalloc.
--- lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 +++ lib/librte_eal/common/eal_common_memory.c | 3 ++ lib/librte_eal/common/eal_memalloc.h | 3 ++ lib/librte_eal/linuxapp/eal/eal_memalloc.c | 66 ++++++++++++++++++------------ 4 files changed, 52 insertions(+), 26 deletions(-)
lib/librte_eal/bsdapp/eal/eal_memalloc.c +6 -0
@@ -46,3 +46,9 @@ eal_memalloc_sync_with_primary(void)RTE_LOG (ERR ,EAL ,"Memory hotplug not supported on FreeBSD\n" );return -1 ; } + +int +eal_memalloc_init (void ) +{ +return 0 ; +}
lib/librte_eal/common/eal_common_memory.c +3 -0
@@ -864,6 +864,9 @@ rte_eal_memory_init(void)if (retval <0 ) goto fail; +if (eal_memalloc_init () <0 ) + goto fail; + retval =rte_eal_process_type () ==RTE_PROC_PRIMARY ?rte_eal_hugepage_init () :rte_eal_hugepage_attach ();
lib/librte_eal/common/eal_memalloc.h +3 -0
@@ -76,4 +76,7 @@ eal_memalloc_mem_alloc_validator_unregister(const char *name, int socket_id);int eal_memalloc_mem_alloc_validate (int socket_id,size_t new_len); +int +eal_memalloc_init (void ); +#endif /* EAL_MEMALLOC_H */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +40 -26
86 lines not on this page · raw message
Signature · 1 line
2.7.4
Re: [PATCH 1/2] mem: add memalloc init stage
Quoted 5 lines · Anatoly Burakov
Currently, memseg lists for secondary process are allocated on sync (triggered by init), when they are accessed for the first time. Move this initialization to a separate init stage for memalloc.
Quoted 8 lines
Signed-off-by Anatoly Burakov <[email protected]>--- lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 +++ lib/librte_eal/common/eal_common_memory.c | 3 ++ lib/librte_eal/common/eal_memalloc.h | 3 ++ lib/librte_eal/linuxapp/eal/eal_memalloc.c | 66 ++++++++++++++++++------------ 4 files changed, 52 insertions(+), 26 deletions(-)
Looks ok to me.
PATCH 2/2 mem: revert to using flock() and add per-segment lockfiles
In reply to Anatoly BurakovThe original implementation used flock() locks, but was later switched to using fcntl() locks for page locking, because fcntl() locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.
However, according to fcntl()'s Ubuntu manpage [1], semantics of fcntl() locks have a giant oversight:
This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an fcntl() lock (which we do because we don't want to leak fd's) will drop the lock completely.
So, in this commit, we will be reverting back to using flock() locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those with flock().
We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to flock() Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.
[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.html
---
Notes:
We could've used OFD locks [1] instead of flock(), but they are only supported on kernel 3.15+ [2], so we're forced to use this flock()-based contraption to support older kernels.
[1] https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
[2] http://man7.org/linux/man-pages/man2/fcntl.2.html
lib/librte_eal/common/eal_filesystem.h | 18 + lib/librte_eal/linuxapp/eal/eal_hugepage_info.c | 28 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 538 +++++++++++++++--------- lib/librte_eal/linuxapp/eal/eal_memory.c | 22 +- 4 files changed, 368 insertions(+), 238 deletions(-)
lib/librte_eal/common/eal_filesystem.h +18 -0
@@ -115,6 +115,24 @@ eal_get_hugefile_path(char *buffer, size_t buflen, const char *hugedir, int f_idreturn buffer; } +/** String format for hugepage map lock files. */ +#define HUGEFILE_LOCK_FMT "%s/%smap_%d_%d.lock" + +static inline const char * +eal_get_hugefile_lock_path (char *buffer,size_t buflen,int list_idx, +int seg_idx) +{ +const char *directory = default_config_dir; +const char *home_dir =getenv ("HOME" ); + +if (getuid () !=0 && home_dir !=NULL ) + directory = home_dir; +snprintf (buffer, buflen -1 ,HUGEFILE_LOCK_FMT , directory, + internal_config.hugefile_prefix, list_idx, seg_idx); + buffer[buflen -1 ] ='\0' ; +return buffer; +} +/** define the default filename prefix for the %s values above */ #define HUGEFILE_PREFIX_DEFAULT "rte"
lib/librte_eal/linuxapp/eal/eal_hugepage_info.c +3 -25
@@ -237,18 +237,6 @@ get_hugepage_dir(uint64_t hugepage_sz, char *hugedir, int len) }/* -* uses fstat to report the size of a file on disk -*/ -static off_t -get_file_size (int fd) -{ -struct stat st; -if (fstat (fd, &st) <0 ) -return 0 ; -return st.st_size; -} - -/* * Clear the hugepage directory of whatever hugepage files * there are. Checks if the file is locked (i.e. * if it's in use by another DPDK process).
@@ -278,8 +266,6 @@ clear_hugedir(const char * hugedir) }while (dirent !=NULL ){ -struct flock lck = {0 }; -/* skip files that don't match the hugepage pattern */ if (fnmatch (filter, dirent->d_name,0 ) >0 ) { dirent =readdir (dir);
@@ -296,19 +282,11 @@ clear_hugedir(const char * hugedir) }/* non-blocking lock */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len =get_file_size (fd); + lck_result =flock (fd,LOCK_EX |LOCK_NB ); - lck_result =fcntl (fd,F_SETLK , &lck); - -/* if lock succeeds, unlock and remove the file */ -if (lck_result !=-1 ) { - lck.l_type =F_UNLCK ; -fcntl (fd,F_SETLK , &lck); +/* if lock succeeds, remove the file */ +if (lck_result !=-1 )unlinkat (dir_fd, dirent->d_name,0 ); - }close (fd); dirent =readdir (dir); }
lib/librte_eal/linuxapp/eal/eal_memalloc.c +344 -194
741 lines not on this page · raw message
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -19
@@ -259,7 +259,6 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi,int fd;unsigned i;void *virtaddr; -struct flock lck = {0 };#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES int node_id =-1 ;int essential_prev =0 ;
@@ -378,13 +377,8 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi, } *(int *)virtaddr =0 ; -/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = hugepage_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"%s(): Locking file failed:%s \n" , __func__,strerror (errno));close (fd);
@@ -708,7 +702,6 @@ remap_segment(struct hugepage_file *hugepages, int seg_start, int seg_end)#endif struct hugepage_file *hfile = &hugepages[cur_page];struct rte_memseg *ms =rte_fbarray_get (arr, ms_idx); -struct flock lck;void *addr;int fd;
@@ -719,11 +712,7 @@ remap_segment(struct hugepage_file *hugepages, int seg_start, int seg_end)return -1 ; }/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = page_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"Could not lock '%s': %s\n" , hfile->filepath,strerror (errno));close (fd);
@@ -1732,7 +1721,6 @@ eal_legacy_hugepage_attach(void)struct hugepage_file *hf = &hp[i];size_t map_sz = hf->size;void *map_addr = hf->final_va; -struct flock lck;/* if size is zero, no more pages left */ if (map_sz ==0 )
@@ -1754,11 +1742,7 @@ eal_legacy_hugepage_attach(void) }/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = map_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"%s(): Locking file failed: %s\n" , __func__,strerror (errno));close (fd);
Signature · 1 line
2.7.4
Re: [PATCH 2/2] mem: revert to using flock() and add per-segment lockfiles
Quoted 58 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.htmlFixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>---
<snip>
Review
Quoted 2 lines
(quoted diff) +2
-0+ +static int get_lockfile(int list_idx, int seg_idx)
Minor nit:
Not sure about this name, since it returns an fd rather than a filename or filepath. How about get_lock_fd().
Quoted 26 lines
lib/librte_eal/linuxapp/eal/eal_memalloc.c +26
-0+{ +char path[PATH_MAX ] = {0 }; +int fd; + +if (list_idx <0 || list_idx >= (int )RTE_DIM (lock_fds)) +return -1 ; +if (seg_idx <0 || seg_idx >= lock_fds[list_idx].len) +return -1 ; + + fd = lock_fds[list_idx].fds[seg_idx]; +/* does this lock already exist? */ +if (fd >=0 ) +return fd; + +eal_get_hugefile_lock_path (path,sizeof (path), list_idx, seg_idx); + + fd =open (path,O_CREAT |O_RDWR ,0660 ); +if (fd <0 ) { +RTE_LOG (ERR ,EAL ,"%s(): error creating lockfile '%s': %s\n" , + __func__, path,strerror (errno)); +return -1 ; + } +/* take out a read lock */ +if (lock (fd,LOCK_SH ) !=1 ) { +RTE_LOG (ERR ,EAL ,"%s(): failed to take out a readlock on '%s': %s\n" , + __func__, path,strerror (errno));
lib/librte_eal/linuxapp/eal/eal_memalloc.c memseg_list_walk_thread_unsafe(rte_memseg_list_walk_t func, void *arg) · line 258
Quoted 10 lines
lib/librte_eal/linuxapp/eal/eal_memalloc.c +10
-0+close (fd); +return -1 ; + } +/* store it for future reference */ + lock_fds[list_idx].fds[seg_idx] = fd; + lock_fds[list_idx].count++; +return fd; +} + +static int put_lockfile (int list_idx,int seg_idx)
This name doesn't really tell me much. I realise that "put" is the opposite of "get", but if we get a file descriptor from the previous function, it's not exactly something we need to "put back". Can you come up with a more descriptive name here.
Quoted 36 lines
lib/librte_eal/linuxapp/eal/eal_memalloc.c +36
-0+{ +int fd, ret; + +if (list_idx <0 || list_idx >= (int )RTE_DIM (lock_fds)) +return -1 ; +if (seg_idx <0 || seg_idx >= lock_fds[list_idx].len) +return -1 ; + + fd = lock_fds[list_idx].fds[seg_idx]; + +/* upgrade lock to exclusive to see if we can remove the lockfile */ + ret =lock (fd,LOCK_EX ); +if (ret ==1 ) { +/* we've succeeded in taking exclusive lock, this lockfile may +* be removed. +*/ +char path[PATH_MAX ] = {0 }; +eal_get_hugefile_lock_path (path,sizeof (path), list_idx, + seg_idx); +if (unlink (path)) { +RTE_LOG (ERR ,EAL ,"%s(): error removing lockfile '%s': %s\n" , + __func__, path,strerror (errno)); + } + } +/* we don't want to leak the fd, so even if we fail to lock, close fd +* and remove it from list anyway. +*/ +close (fd); + lock_fds[list_idx].fds[seg_idx] =-1 ; + lock_fds[list_idx].count--; + +if (ret <0 ) +return -1 ; +return 0 ; +} +
<snip>
Re: [PATCH 2/2] mem: revert to using flock() and add per-segment lockfiles
In reply to Anatoly BurakovQuoted 50 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.html
Quoted 8 lines
Fixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>---
While the memory subsystem in DPDK has changed a lot since I last looked at it, thereby preventing me from doing an in-depth review, this change makes sense to me. So for this and any future versions:
v2 cover Fix file locking in EAL memory
In reply to Anatoly BurakovThis patchset replaces the fcntl()-based locking used in the original DPDK memory hotplug patchset, to an flock()- and lockfile-based implementation, due to numerous (well, one, really) problems with how fcntl() locks work.
Long story short, fcntl() locks will be dropped if any fd referring to locked file, is closed - even if it's not the last fd, even if it wasn't even the fd that was used to lock the file in the first place, even if it wasn't you who closed that fd, but some other library.
This patchset corrects this saddening design defect in the original implementation.
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+, so we cannot rely on them if we want to support old kernels. Hence, we use per-segment lockfiles. The number of file descriptors we open does not end up more than in non-single file segments case - we still open the same amount of files (a file per page), plus a file per memseg list.
Additionally, since flock() is not atomic, we also lock the hugepage dir to prevent multiple processes from concurrently performing operations on hugetlbfs mounts.
If you know of a more enlightened way of fixing this limitation, you are certainly welcome to comment :)
v2:
- Fixes as per review comments
- Make lockfiles hidden by default
Anatoly Burakov (2):
mem: add memalloc init stage
mem: revert to using flock() and add per-segment lockfiles
lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 + lib/librte_eal/common/eal_common_memory.c | 3 + lib/librte_eal/common/eal_filesystem.h | 18 + lib/librte_eal/common/eal_memalloc.h | 3 + lib/librte_eal/linuxapp/eal/eal_hugepage_info.c | 28 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 604 +++++++++++++++--------- lib/librte_eal/linuxapp/eal/eal_memory.c | 22 +- 7 files changed, 420 insertions(+), 264 deletions(-)
Signature · 1 line
2.7.4
PATCH v2 1/2 mem: add memalloc init stage
Currently, memseg lists for secondary process are allocated on sync (triggered by init), when they are accessed for the first time. Move this initialization to a separate init stage for memalloc.
--- lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 +++ lib/librte_eal/common/eal_common_memory.c | 3 ++ lib/librte_eal/common/eal_memalloc.h | 3 ++ lib/librte_eal/linuxapp/eal/eal_memalloc.c | 66 ++++++++++++++++++------------ 4 files changed, 52 insertions(+), 26 deletions(-)
lib/librte_eal/bsdapp/eal/eal_memalloc.c +6 -0
@@ -46,3 +46,9 @@ eal_memalloc_sync_with_primary(void)RTE_LOG (ERR ,EAL ,"Memory hotplug not supported on FreeBSD\n" );return -1 ; } + +int +eal_memalloc_init (void ) +{ +return 0 ; +}
lib/librte_eal/common/eal_common_memory.c +3 -0
@@ -864,6 +864,9 @@ rte_eal_memory_init(void)if (retval <0 ) goto fail; +if (eal_memalloc_init () <0 ) + goto fail; + retval =rte_eal_process_type () ==RTE_PROC_PRIMARY ?rte_eal_hugepage_init () :rte_eal_hugepage_attach ();
lib/librte_eal/common/eal_memalloc.h +3 -0
@@ -76,4 +76,7 @@ eal_memalloc_mem_alloc_validator_unregister(const char *name, int socket_id);int eal_memalloc_mem_alloc_validate (int socket_id,size_t new_len); +int +eal_memalloc_init (void ); +#endif /* EAL_MEMALLOC_H */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +40 -26
86 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v2 2/2 mem: revert to using flock() and add per-segment lockfiles
In reply to Anatoly BurakovThe original implementation used flock() locks, but was later switched to using fcntl() locks for page locking, because fcntl() locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.
However, according to fcntl()'s Ubuntu manpage [1], semantics of fcntl() locks have a giant oversight:
This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an fcntl() lock (which we do because we don't want to leak fd's) will drop the lock completely.
So, in this commit, we will be reverting back to using flock() locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those with flock().
We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to flock() Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.
[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.html
---
Notes:
v2:
- Make lockfiles hidden
- renamed functions as per Bruce's comments
We could've used OFD locks [1] instead of flock(), but they are only supported on kernel 3.15+ [2], so we're forced to use this flock()-based contraption to support older kernels.
[1] https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
[2] http://man7.org/linux/man-pages/man2/fcntl.2.html
lib/librte_eal/common/eal_filesystem.h | 18 + lib/librte_eal/linuxapp/eal/eal_hugepage_info.c | 28 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 538 +++++++++++++++--------- lib/librte_eal/linuxapp/eal/eal_memory.c | 22 +- 4 files changed, 368 insertions(+), 238 deletions(-)
lib/librte_eal/common/eal_filesystem.h +18 -0
@@ -115,6 +115,24 @@ eal_get_hugefile_path(char *buffer, size_t buflen, const char *hugedir, int f_idreturn buffer; } +/** String format for hugepage map lock files. */ +#define HUGEFILE_LOCK_FMT "%s/.%smap_%d_%d.lock" + +static inline const char * +eal_get_hugefile_lock_path (char *buffer,size_t buflen,int list_idx, +int seg_idx) +{ +const char *directory = default_config_dir; +const char *home_dir =getenv ("HOME" ); + +if (getuid () !=0 && home_dir !=NULL ) + directory = home_dir; +snprintf (buffer, buflen -1 ,HUGEFILE_LOCK_FMT , directory, + internal_config.hugefile_prefix, list_idx, seg_idx); + buffer[buflen -1 ] ='\0' ; +return buffer; +} +/** define the default filename prefix for the %s values above */ #define HUGEFILE_PREFIX_DEFAULT "rte"
lib/librte_eal/linuxapp/eal/eal_hugepage_info.c +3 -25
@@ -237,18 +237,6 @@ get_hugepage_dir(uint64_t hugepage_sz, char *hugedir, int len) }/* -* uses fstat to report the size of a file on disk -*/ -static off_t -get_file_size (int fd) -{ -struct stat st; -if (fstat (fd, &st) <0 ) -return 0 ; -return st.st_size; -} - -/* * Clear the hugepage directory of whatever hugepage files * there are. Checks if the file is locked (i.e. * if it's in use by another DPDK process).
@@ -278,8 +266,6 @@ clear_hugedir(const char * hugedir) }while (dirent !=NULL ){ -struct flock lck = {0 }; -/* skip files that don't match the hugepage pattern */ if (fnmatch (filter, dirent->d_name,0 ) >0 ) { dirent =readdir (dir);
@@ -296,19 +282,11 @@ clear_hugedir(const char * hugedir) }/* non-blocking lock */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len =get_file_size (fd); + lck_result =flock (fd,LOCK_EX |LOCK_NB ); - lck_result =fcntl (fd,F_SETLK , &lck); - -/* if lock succeeds, unlock and remove the file */ -if (lck_result !=-1 ) { - lck.l_type =F_UNLCK ; -fcntl (fd,F_SETLK , &lck); +/* if lock succeeds, remove the file */ +if (lck_result !=-1 )unlinkat (dir_fd, dirent->d_name,0 ); - }close (fd); dirent =readdir (dir); }
lib/librte_eal/linuxapp/eal/eal_memalloc.c +344 -194
741 lines not on this page · raw message
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -19
@@ -259,7 +259,6 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi,int fd;unsigned i;void *virtaddr; -struct flock lck = {0 };#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES int node_id =-1 ;int essential_prev =0 ;
@@ -378,13 +377,8 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi, } *(int *)virtaddr =0 ; -/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = hugepage_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"%s(): Locking file failed:%s \n" , __func__,strerror (errno));close (fd);
@@ -708,7 +702,6 @@ remap_segment(struct hugepage_file *hugepages, int seg_start, int seg_end)#endif struct hugepage_file *hfile = &hugepages[cur_page];struct rte_memseg *ms =rte_fbarray_get (arr, ms_idx); -struct flock lck;void *addr;int fd;
@@ -719,11 +712,7 @@ remap_segment(struct hugepage_file *hugepages, int seg_start, int seg_end)return -1 ; }/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = page_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"Could not lock '%s': %s\n" , hfile->filepath,strerror (errno));close (fd);
@@ -1732,7 +1721,6 @@ eal_legacy_hugepage_attach(void)struct hugepage_file *hf = &hp[i];size_t map_sz = hf->size;void *map_addr = hf->final_va; -struct flock lck;/* if size is zero, no more pages left */ if (map_sz ==0 )
@@ -1754,11 +1742,7 @@ eal_legacy_hugepage_attach(void) }/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = map_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"%s(): Locking file failed: %s\n" , __func__,strerror (errno));close (fd);
Signature · 1 line
2.7.4
Quoted 2 lines · On Tue, 24 Apr 2018 16:54:21 +0100 Anatoly Burakov <[email protected]>…
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+,
Maybe time to move to next LTS kernel (3.16)?
Quoted 8 lines · Stephen Hemminger
Quoted 2 lines · On Tue, 24 Apr 2018 16:54:21 +0100 Anatoly Burakov <[email protected]>…
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+,
Maybe time to move to next LTS kernel (3.16)?
I'm all for it, but it's not my call to make :(
Signature · 2 lines
Thanks, Anatoly
24/04/2018 19:25, Burakov, Anatoly:
Quoted 11 lines · Stephen Hemminger
Quoted 8 lines
Quoted 2 lines · On Tue, 24 Apr 2018 16:54:21 +0100 Anatoly Burakov <[email protected]>…
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+,
Maybe time to move to next LTS kernel (3.16)?
I'm all for it, but it's not my call to make :(
I am in favor of upgrading kernel requirements.
The discussion already started few months ago:
https://dpdk.org/patch/34778
We just need to consider how to support the latest versions of distributions Debian, Ubuntu, SLES and RHEL.
Quoted 11 lines · On Tue, 24 Apr 2018 22:05:10 +0200 Thomas Monjalon <[email protected]> wrote:
24/04/2018 19:25, Burakov, Anatoly:
Quoted 10 lines · Stephen Hemminger
Quoted 8 lines
Quoted 2 lines · On Tue, 24 Apr 2018 16:54:21 +0100 Anatoly Burakov <[email protected]>…
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+,
Maybe time to move to next LTS kernel (3.16)?
Quoted 8 lines
Quoted 1 line
I'm all for it, but it's not my call to make :(
I am in favor of upgrading kernel requirements.
The discussion already started few months ago:https://dpdk.org/patch/34778We just need to consider how to support the latest versions of distributions Debian, Ubuntu, SLES and RHEL.
Does anyone have a quick map for major distributions, current stable versions?
v3 cover Fix file locking in EAL memory
In reply to Anatoly BurakovThis patchset replaces the fcntl()-based locking used in the original DPDK memory hotplug patchset, to an flock()- and lockfile-based implementation, due to numerous (well, one, really) problems with how fcntl() locks work.
Long story short, fcntl() locks will be dropped if any fd referring to locked file, is closed - even if it's not the last fd, even if it wasn't even the fd that was used to lock the file in the first place, even if it wasn't you who closed that fd, but some other library.
This patchset corrects this saddening design defect in the original implementation.
One of the ways to work around this was using OFD locks, but they are only supported on kernels 3.15+, so we cannot rely on them if we want to support old kernels. Hence, we use per-segment lockfiles. The number of file descriptors we open does not end up more than in non-single file segments case - we still open the same amount of files (a file per page), plus a file per memseg list.
Additionally, since flock() is not atomic, we also lock the hugepage dir to prevent multiple processes from concurrently performing operations on hugetlbfs mounts.
If you know of a more enlightened way of fixing this limitation, you are certainly welcome to comment :)
v3:
- Made lockfile naming more consistent with hugetlbfs file naming
v2:
- Fixes as per review comments
- Make lockfiles hidden by default
Anatoly Burakov (2):
mem: add memalloc init stage
mem: revert to using flock() and add per-segment lockfiles
lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 + lib/librte_eal/common/eal_common_memory.c | 3 + lib/librte_eal/common/eal_filesystem.h | 17 + lib/librte_eal/common/eal_memalloc.h | 3 + lib/librte_eal/linuxapp/eal/eal_hugepage_info.c | 28 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 605 +++++++++++++++--------- lib/librte_eal/linuxapp/eal/eal_memory.c | 22 +- 7 files changed, 420 insertions(+), 264 deletions(-)
Signature · 1 line
2.7.4
PATCH v3 1/2 mem: add memalloc init stage
Currently, memseg lists for secondary process are allocated on sync (triggered by init), when they are accessed for the first time. Move this initialization to a separate init stage for memalloc.
--- lib/librte_eal/bsdapp/eal/eal_memalloc.c | 6 +++ lib/librte_eal/common/eal_common_memory.c | 3 ++ lib/librte_eal/common/eal_memalloc.h | 3 ++ lib/librte_eal/linuxapp/eal/eal_memalloc.c | 66 ++++++++++++++++++------------ 4 files changed, 52 insertions(+), 26 deletions(-)
lib/librte_eal/bsdapp/eal/eal_memalloc.c +6 -0
@@ -46,3 +46,9 @@ eal_memalloc_sync_with_primary(void)RTE_LOG (ERR ,EAL ,"Memory hotplug not supported on FreeBSD\n" );return -1 ; } + +int +eal_memalloc_init (void ) +{ +return 0 ; +}
lib/librte_eal/common/eal_common_memory.c +3 -0
@@ -864,6 +864,9 @@ rte_eal_memory_init(void)if (retval <0 ) goto fail; +if (eal_memalloc_init () <0 ) + goto fail; + retval =rte_eal_process_type () ==RTE_PROC_PRIMARY ?rte_eal_hugepage_init () :rte_eal_hugepage_attach ();
lib/librte_eal/common/eal_memalloc.h +3 -0
@@ -76,4 +76,7 @@ eal_memalloc_mem_alloc_validator_unregister(const char *name, int socket_id);int eal_memalloc_mem_alloc_validate (int socket_id,size_t new_len); +int +eal_memalloc_init (void ); +#endif /* EAL_MEMALLOC_H */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +40 -26
86 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v3 2/2 mem: revert to using flock() and add per-segment lockfiles
In reply to Anatoly BurakovThe original implementation used flock() locks, but was later switched to using fcntl() locks for page locking, because fcntl() locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.
However, according to fcntl()'s Ubuntu manpage [1], semantics of fcntl() locks have a giant oversight:
This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an fcntl() lock (which we do because we don't want to leak fd's) will drop the lock completely.
So, in this commit, we will be reverting back to using flock() locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those with flock().
We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to flock() Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.
[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.html
---
Notes:
v3:
- Make lock naming consistent with hugetlbfs file naming
v2:
- Make lockfiles hidden
- renamed functions as per Bruce's comments
We could've used OFD locks [1] instead of flock(), but they are only supported on kernel 3.15+ [2], so we're forced to use this flock()-based contraption to support older kernels.
[1] https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
[2] http://man7.org/linux/man-pages/man2/fcntl.2.html
lib/librte_eal/common/eal_filesystem.h | 17 + lib/librte_eal/linuxapp/eal/eal_hugepage_info.c | 28 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 539 +++++++++++++++--------- lib/librte_eal/linuxapp/eal/eal_memory.c | 22 +- 4 files changed, 368 insertions(+), 238 deletions(-)
lib/librte_eal/common/eal_filesystem.h +17 -0
@@ -115,6 +115,23 @@ eal_get_hugefile_path(char *buffer, size_t buflen, const char *hugedir, int f_idreturn buffer; } +/** String format for hugepage map lock files. */ +#define HUGEFILE_LOCK_FMT "%s/.%smap_%d.lock" + +static inline const char * +eal_get_hugefile_lock_path (char *buffer,size_t buflen,int f_id) +{ +const char *directory = default_config_dir; +const char *home_dir =getenv ("HOME" ); + +if (getuid () !=0 && home_dir !=NULL ) + directory = home_dir; +snprintf (buffer, buflen -1 ,HUGEFILE_LOCK_FMT , directory, + internal_config.hugefile_prefix, f_id); + buffer[buflen -1 ] ='\0' ; +return buffer; +} +/** define the default filename prefix for the %s values above */ #define HUGEFILE_PREFIX_DEFAULT "rte"
lib/librte_eal/linuxapp/eal/eal_hugepage_info.c +3 -25
@@ -237,18 +237,6 @@ get_hugepage_dir(uint64_t hugepage_sz, char *hugedir, int len) }/* -* uses fstat to report the size of a file on disk -*/ -static off_t -get_file_size (int fd) -{ -struct stat st; -if (fstat (fd, &st) <0 ) -return 0 ; -return st.st_size; -} - -/* * Clear the hugepage directory of whatever hugepage files * there are. Checks if the file is locked (i.e. * if it's in use by another DPDK process).
@@ -278,8 +266,6 @@ clear_hugedir(const char * hugedir) }while (dirent !=NULL ){ -struct flock lck = {0 }; -/* skip files that don't match the hugepage pattern */ if (fnmatch (filter, dirent->d_name,0 ) >0 ) { dirent =readdir (dir);
@@ -296,19 +282,11 @@ clear_hugedir(const char * hugedir) }/* non-blocking lock */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len =get_file_size (fd); + lck_result =flock (fd,LOCK_EX |LOCK_NB ); - lck_result =fcntl (fd,F_SETLK , &lck); - -/* if lock succeeds, unlock and remove the file */ -if (lck_result !=-1 ) { - lck.l_type =F_UNLCK ; -fcntl (fd,F_SETLK , &lck); +/* if lock succeeds, remove the file */ +if (lck_result !=-1 )unlinkat (dir_fd, dirent->d_name,0 ); - }close (fd); dirent =readdir (dir); }
lib/librte_eal/linuxapp/eal/eal_memalloc.c +345 -194
742 lines not on this page · raw message
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -19
@@ -259,7 +259,6 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi,int fd;unsigned i;void *virtaddr; -struct flock lck = {0 };#ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES int node_id =-1 ;int essential_prev =0 ;
@@ -378,13 +377,8 @@ map_all_hugepages(struct hugepage_file *hugepg_tbl, struct hugepage_info *hpi, } *(int *)virtaddr =0 ; -/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = hugepage_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"%s(): Locking file failed:%s \n" , __func__,strerror (errno));close (fd);
@@ -708,7 +702,6 @@ remap_segment(struct hugepage_file *hugepages, int seg_start, int seg_end)#endif struct hugepage_file *hfile = &hugepages[cur_page];struct rte_memseg *ms =rte_fbarray_get (arr, ms_idx); -struct flock lck;void *addr;int fd;
@@ -719,11 +712,7 @@ remap_segment(struct hugepage_file *hugepages, int seg_start, int seg_end)return -1 ; }/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = page_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"Could not lock '%s': %s\n" , hfile->filepath,strerror (errno));close (fd);
@@ -1732,7 +1721,6 @@ eal_legacy_hugepage_attach(void)struct hugepage_file *hf = &hp[i];size_t map_sz = hf->size;void *map_addr = hf->final_va; -struct flock lck;/* if size is zero, no more pages left */ if (map_sz ==0 )
@@ -1754,11 +1742,7 @@ eal_legacy_hugepage_attach(void) }/* set shared lock on the file. */ - lck.l_type =F_RDLCK ; - lck.l_whence =SEEK_SET ; - lck.l_start =0 ; - lck.l_len = map_sz; -if (fcntl (fd,F_SETLK , &lck) ==-1 ) { +if (flock (fd,LOCK_SH ) <0 ) {RTE_LOG (DEBUG ,EAL ,"%s(): Locking file failed: %s\n" , __func__,strerror (errno));close (fd);
Signature · 1 line
2.7.4
Re: [PATCH v3 2/2] mem: revert to using flock() and add per-segment lockfiles
Quoted 50 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.html
Quoted 8 lines
Fixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>Acked-by Bruce Richardson <[email protected]>
We have a problem with the changeset if EAL option -m or --socket-mem is used. EAL initialization hangs just after EAL: Probing VFIO support... strace points to flock(7, LOCK_EX
List of file descriptors:
# ls /proc/25452/fd -l total 0 lrwx------ 1 root root 64 Apr 28 10:34 0 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 1 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:32 2 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 3 -> /run/.rte_config lrwx------ 1 root root 64 Apr 28 10:34 4 -> socket:[154166] lrwx------ 1 root root 64 Apr 28 10:34 5 -> socket:[154158] lr-x------ 1 root root 64 Apr 28 10:34 6 -> /dev/hugepages lr-x------ 1 root root 64 Apr 28 10:34 7 -> /dev/hugepages
I guess the problem is that there are two /dev/hugepages and it hangs on the second.
Ideas how to solve it?
Andrew.
Re: [PATCH v3 2/2] mem: revert to using flock() and add per-segment lockfiles
Quoted 75 lines · Andrew Rybchenko
Quoted 50 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.htmlQuoted 8 lines
Fixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>Acked-by Bruce Richardson <[email protected]>We have a problem with the changeset if EAL option -m or --socket-mem is used. EAL initialization hangs just after EAL: Probing VFIO support... strace points to flock(7,
LOCK_EX
List of file descriptors:# ls /proc/25452/fd -l total 0 lrwx------ 1 root root 64 Apr 28 10:34 0 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 1 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:32 2 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 3 -> /run/.rte_config lrwx------ 1 root root 64 Apr 28 10:34 4 -> socket:[154166] lrwx------ 1 root root 64 Apr 28 10:34 5 -> socket:[154158] lr-x------ 1 root root 64 Apr 28 10:34 6 -> /dev/hugepages lr-x------ 1 root root 64 Apr 28 10:34 7 -> /dev/hugepages
Quoted 7 lines
I guess the problem is that there are two
/dev/hugepagesand it hangs on the second.Ideas how to solve it?
Andrew.
Seeing similar reports from validation. I'm looking into it.
Signature · 2 lines
Thanks, Anatoly
Re: [PATCH v3 2/2] mem: revert to using flock() and add per-segment lockfiles
In reply to Andrew RybchenkoQuoted 75 lines · Andrew Rybchenko
Quoted 50 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.htmlQuoted 8 lines
Fixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>Acked-by Bruce Richardson <[email protected]>We have a problem with the changeset if EAL option -m or --socket-mem is used. EAL initialization hangs just after EAL: Probing VFIO support... strace points to flock(7,
LOCK_EX
List of file descriptors:# ls /proc/25452/fd -l total 0 lrwx------ 1 root root 64 Apr 28 10:34 0 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 1 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:32 2 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 3 -> /run/.rte_config lrwx------ 1 root root 64 Apr 28 10:34 4 -> socket:[154166] lrwx------ 1 root root 64 Apr 28 10:34 5 -> socket:[154158] lr-x------ 1 root root 64 Apr 28 10:34 6 -> /dev/hugepages lr-x------ 1 root root 64 Apr 28 10:34 7 -> /dev/hugepages
Quoted 7 lines
I guess the problem is that there are two
/dev/hugepagesand it hangs on the second.Ideas how to solve it?
Andrew.
Hi Andrew,
Please try the following patch:
http://dpdk.org/dev/patchwork/patch/39166/
This should fix the issue.
Signature · 2 lines
Thanks, Anatoly
Maxime Coquelin
Tested-byRe: [PATCH v3 2/2] mem: revert to using flock() and add per-segment lockfiles
Quoted 84 lines · Anatoly
Quoted 75 lines · Andrew Rybchenko
Quoted 50 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.htmlQuoted 8 lines
Fixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>Acked-by Bruce Richardson <[email protected]>We have a problem with the changeset if EAL option -m or --socket-mem is used. EAL initialization hangs just after EAL: Probing VFIO support... strace points to flock(7,
LOCK_EX
List of file descriptors:# ls /proc/25452/fd -l total 0 lrwx------ 1 root root 64 Apr 28 10:34 0 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 1 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:32 2 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 3 -> /run/.rte_config lrwx------ 1 root root 64 Apr 28 10:34 4 -> socket:[154166] lrwx------ 1 root root 64 Apr 28 10:34 5 -> socket:[154158] lr-x------ 1 root root 64 Apr 28 10:34 6 -> /dev/hugepages lr-x------ 1 root root 64 Apr 28 10:34 7 -> /dev/hugepagesQuoted 7 lines
I guess the problem is that there are two
/dev/hugepagesand it hangs on the second.Ideas how to solve it?
Andrew.
Quoted 8 lines
Hi Andrew,
Please try the following patch:
http://dpdk.org/dev/patchwork/patch/39166/
This should fix the issue.
I faced the regression in my test bench, your patch fixes the issue in my case:
Thanks,
Maxime
Re: [PATCH v3 2/2] mem: revert to using flock() and add per-segment lockfiles
In reply to Burakov, AnatolyQuoted 84 lines · Anatoly
Quoted 75 lines · Andrew Rybchenko
Quoted 50 lines · Anatoly Burakov
The original implementation used
flock()locks, but was later switched to usingfcntl()locks for page locking, becausefcntl()locks allow locking parts of a file, which is useful for single-file segments mode, where locking the entire file isn't as useful because we still need to grow and shrink it.However, according to fcntl()'s Ubuntu manpage [1], semantics of
fcntl()locks have a giant oversight:This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access.
Basically, closing *any* fd with an
fcntl()lock (which we do because we don't want to leak fd's) will drop the lock completely.So, in this commit, we will be reverting back to using
flock()locks everywhere. However, that still leaves the problem of locking parts of a memseg list file in single file segments mode, and we will be solving it with creating separate lock files per each page, and tracking those withflock().We will also be removing all of this tailq business and replacing it with a simple array - saving a few bytes is not worth the extra hassle of dealing with pointers and potential memory allocation failures. Also, remove the tailq lock since it is not needed - these fd lists are per-process, and within a given process, it is always only one thread handling access to hugetlbfs.
So, first one to allocate a segment will create a lockfile, and put a shared lock on it. When we're shrinking the page file, we will be trying to take out a write lock on that lockfile, which would fail if any other process is holding onto the lockfile as well. This way, we can know if we can shrink the segment file. Also, if no other locks are found in the lock list for a given memseg list, the memseg list fd is automatically closed.
One other thing to note is, according to
flock()Ubuntu manpage [2], upgrading the lock from shared to exclusive is implemented by dropping and reacquiring the lock, which is not atomic and thus would have created race conditions. So, on attempting to perform operations in hugetlbfs, we will take out a writelock on hugetlbfs directory, so that only one process could perform hugetlbfs operations concurrently.[1] http://manpages.ubuntu.com/manpages/artful/en/man2/fcntl.2freebsd.html
[2] http://manpages.ubuntu.com/manpages/bionic/en/man2/flock.2.htmlQuoted 8 lines
Fixes 66cc45e293ed ("mem: replace memseg with memseg lists")Fixes 582bed1e1d1d ("mem: support mapping hugepages at runtime")Fixes a5ff05d60fc5 ("mem: support unmapping pages at runtime")Fixes 2a04139f66b4 ("eal: add single file segments option")Signed-off-by Anatoly Burakov <[email protected]>Acked-by Bruce Richardson <[email protected]>We have a problem with the changeset if EAL option -m or --socket-mem is used. EAL initialization hangs just after EAL: Probing VFIO support... strace points to flock(7,
LOCK_EX
List of file descriptors:# ls /proc/25452/fd -l total 0 lrwx------ 1 root root 64 Apr 28 10:34 0 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 1 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:32 2 -> /dev/pts/0 lrwx------ 1 root root 64 Apr 28 10:34 3 -> /run/.rte_config lrwx------ 1 root root 64 Apr 28 10:34 4 -> socket:[154166] lrwx------ 1 root root 64 Apr 28 10:34 5 -> socket:[154158] lr-x------ 1 root root 64 Apr 28 10:34 6 -> /dev/hugepages lr-x------ 1 root root 64 Apr 28 10:34 7 -> /dev/hugepagesQuoted 7 lines
I guess the problem is that there are two
/dev/hugepagesand it hangs on the second.Ideas how to solve it?
Andrew.
Quoted 7 lines
Hi Andrew,
Please try the following patch:
http://dpdk.org/dev/patchwork/patch/39166/
This should fix the issue.
Hi Anatoly,
yes, it fixes the issue.
Thanks,
Andrew.
Thomas Monjalon
AppliedQuoted 3 lines
Anatoly Burakov (2):
mem: add memalloc init stage
mem: revert to usingflock()and add per-segment lockfiles
Applied, thanks