1 Star 0 Fork 17

openeuler-embedded/libdb

forked from src-openEuler/libdb 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
bugfix-fix-deadlock-on-mempool-file-locks.patch 4.36 KB
一键复制 编辑 原始数据 按行查看 历史
dogsheng 提交于 2019-11-19 14:17 . Package init
From 4d6bce62d61739271300a3ee32b1a44cb0dce10e Mon Sep 17 00:00:00 2001
From: huangkaibin <huangkaibin@huawei.com>
Date: Fri, 30 Jun 2017 16:24:08 +0800
Subject: [PATCH] libdb: Fix dead-lock on mempool file locks.
1. When operate on mempool files, the following locks must be got in order.
a) hash lock for mempool files(ref as hash lock)
b) file lock for mempool file(ref as mfp lock)
Note: these locks are shared between processes through mmap.
When there are at least 4 processes(A, B, C, D) running at the same time, there is a rare case
that will cause deadlock between processes.
The following steps describe how deadlock occurres.
1. process A gets mfp lock(ID of X)
(__memp_fclose->MUTEX_LOCK(env, mfp->mutex))
2. process B gets hash lock
(__memp_fopen->MUTEX_LOCK(env, hp->mtx_hash))
3. process B then wait for mfp lock(because it is locked by A)
(__memp_mpf_find->MUTEX_LOCK(env, mfp->mutex))
4. process C also wait for the same mfp lock.
(__memp_fclose->MUTEX_LOCK(env, mfp->mutex))
5. process A release mfp lock(ID of x)
(__memp_fclose->__memp_mf_discard->MUTEX_UNLOCK(env, mfp->mutex))
For the mempool file, there is a counter (mfp->mpf_cnt) to track
how many processes open this mempool file. And when no any process
open this mempool file, the file will be closed and the mfp lock will be freed.
Here, mfp->mpf_cnt is 1, and this mfp lock is freed, and will be reused by other locks.
6. process D create a new mfp lock using the freed ID X in step 5,
(__fop_file_setup->__lock_id->__lock_getlocker_int->__mutex_alloc)
and gets this lock immediately
(__fop_file_setup->__lock_id->__lock_getlocker_int->MUTEX_LOCK(env, mutex))
7. process D then gets and waits for hash lock(because it is locked by B)
(__memp_fopen->MUTEX_LOCK(env, hp->mtx_hash))
Thus, there is a deadlock between B and D, for,
B gets hash lock but wait for mfp lock, and
D gets mfp lock but wait for hash lock.
2. According to steps 1-7, process D waits for mfp lock, but is is freed by A in step 5,
and waits for a different lock with same ID created by process D. This is the root
reason for the deadlock.
In this patch, we will also lock the hash lock before locking mfp lock in step 1, so that
no other processes can obtain mfp lock while it is in freeing.
3. This deadlock can be produced easily by running many rpm commands at the same time.
rpm --checksig --nosignature xxxx.rpm
---
src/mp/mp_fopen.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/src/mp/mp_fopen.c b/src/mp/mp_fopen.c
index d825909..9123617 100644
--- a/src/mp/mp_fopen.c
+++ b/src/mp/mp_fopen.c
@@ -912,6 +912,9 @@ __memp_fclose(dbmfp, flags)
DB_MPOOL *dbmp;
ENV *env;
MPOOLFILE *mfp;
+ MPOOL *mp;
+ DB_MPOOL_HASH *hp;
+ int hp_locked;
char *rpath;
u_int32_t ref;
int deleted, purge_dead, ret, t_ret;
@@ -991,6 +994,9 @@ __memp_fclose(dbmfp, flags)
if (!F_ISSET(dbmfp, MP_OPEN_CALLED))
goto done;
+ mp = dbmp->reginfo[0].primary;
+ hp = R_ADDR(dbmp->reginfo, mp->ftab);
+ hp += mfp->bucket;
/*
* If it's a temp file, all outstanding references belong to unflushed
* buffers. (A temp file can only be referenced by one DB_MPOOLFILE).
@@ -999,8 +1005,11 @@ __memp_fclose(dbmfp, flags)
* when we try to flush them.
*/
deleted = 0;
- if (!LF_ISSET(DB_MPOOL_NOLOCK))
+ if (!LF_ISSET(DB_MPOOL_NOLOCK)) {
+ MUTEX_LOCK(env, hp->mtx_hash);
MUTEX_LOCK(env, mfp->mutex);
+ hp_locked = 1;
+ }
if (F_ISSET(dbmfp, MP_MULTIVERSION))
atomic_dec(env, &mfp->multiversion);
if (F_ISSET(dbmfp, MP_READONLY) ||
@@ -1038,7 +1047,7 @@ __memp_fclose(dbmfp, flags)
*/
DB_ASSERT(env, !LF_ISSET(DB_MPOOL_NOLOCK));
if ((t_ret =
- __memp_mf_discard(dbmp, mfp, 0)) != 0 && ret == 0)
+ __memp_mf_discard(dbmp, mfp, hp_locked)) != 0 && ret == 0)
ret = t_ret;
deleted = 1;
}
@@ -1046,5 +1055,10 @@ __memp_fclose(dbmfp, flags)
if (!deleted && !LF_ISSET(DB_MPOOL_NOLOCK))
MUTEX_UNLOCK(env, mfp->mutex);
+ if(hp_locked) {
+ MUTEX_UNLOCK(env, hp->mtx_hash);
+ hp_locked = 0;
+ }
+
if (purge_dead)
(void)__memp_purge_dead_files(env);
@@ -1126,7 +1140,7 @@ __memp_mf_discard(dbmp, mfp, hp_locked)
/* Lock the region and collect stats and free the space. */
MPOOL_SYSTEM_LOCK(env);
if (need_sync &&
- (t_ret = __memp_mf_sync(dbmp, mfp, 0)) != 0 && ret == 0)
+ (t_ret = __memp_mf_sync(dbmp, mfp, hp_locked)) != 0 && ret == 0)
ret = t_ret;
#ifdef HAVE_STATISTICS
--
1.8.3.1
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/openeuler-embedded/libdb.git
git@gitee.com:openeuler-embedded/libdb.git
openeuler-embedded
libdb
libdb
openEuler-embedded

搜索帮助