| |
@@ -45,6 +45,7 @@
|
| |
#include "map.h"
|
| |
#include "portmap.h"
|
| |
#include "wrap.h"
|
| |
+ #include "back-shr.h"
|
| |
|
| |
/* The singleton for the cache. */
|
| |
static struct {
|
| |
@@ -91,6 +92,7 @@
|
| |
} *domains;
|
| |
int n_domains;
|
| |
struct wrapped_rwlock *lock;
|
| |
+ struct wrapped_rwlock *plugin_lock;
|
| |
} map_data;
|
| |
|
| |
static void *
|
| |
@@ -1155,6 +1157,10 @@
|
| |
if (map_data.lock == NULL) {
|
| |
return -1;
|
| |
}
|
| |
+ map_data.plugin_lock = wrap_new_rwlock();
|
| |
+ if (map_data.plugin_lock == NULL) {
|
| |
+ return -1;
|
| |
+ }
|
| |
return 0;
|
| |
}
|
| |
|
| |
@@ -1193,6 +1199,8 @@
|
| |
}
|
| |
wrap_free_rwlock(map_data.lock);
|
| |
map_data.lock = NULL;
|
| |
+ wrap_free_rwlock(map_data.plugin_lock);
|
| |
+ map_data.plugin_lock = NULL;
|
| |
}
|
| |
|
| |
int
|
| |
@@ -1219,19 +1227,270 @@
|
| |
}
|
| |
|
| |
int
|
| |
- map_rdlock(void)
|
| |
+ plugin_rdlock(void)
|
| |
{
|
| |
- return wrap_rwlock_rdlock(map_data.lock);
|
| |
+ return wrap_rwlock_rdlock(map_data.plugin_lock);
|
| |
}
|
| |
|
| |
int
|
| |
- map_wrlock(void)
|
| |
+ plugin_wrlock(void)
|
| |
{
|
| |
- return wrap_rwlock_wrlock(map_data.lock);
|
| |
+ return wrap_rwlock_wrlock(map_data.plugin_lock);
|
| |
}
|
| |
|
| |
int
|
| |
- map_unlock(void)
|
| |
+ plugin_unlock(void)
|
| |
{
|
| |
- return wrap_rwlock_unlock(map_data.lock);
|
| |
+ return wrap_rwlock_unlock(map_data.plugin_lock);
|
| |
}
|
| |
+
|
| |
+ int
|
| |
+ map_rdlock(void)
|
| |
+ {
|
| |
+ int lock_status;
|
| |
+ int lock_count;
|
| |
+ int rc = 0;
|
| |
+
|
| |
+ if (rw_monitor_enabled() == MAP_MONITOR_DISABLED) {
|
| |
+ /* This is not initialized used the old way */
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: old way MAP_MONITOR_DISABLED\n");
|
| |
+ return wrap_rwlock_rdlock(map_data.lock);
|
| |
+ }
|
| |
+
|
| |
+
|
| |
+ lock_status = get_plugin_monitor_status();
|
| |
+ lock_count = get_plugin_monitor_count();
|
| |
+
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "map_rdlock",
|
| |
+ "thread_id = %p (call level = %d)\n", PR_GetCurrentThread(), wrap_get_call_level());
|
| |
+ #endif
|
| |
+ if (lock_status == MAP_RWLOCK_UNINIT) {
|
| |
+ /* This is not initialized used the old way */
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: old way lock_status == MAP_RWLOCK_UNINIT\n");
|
| |
+ return wrap_rwlock_rdlock(map_data.lock);
|
| |
+ }
|
| |
+
|
| |
+ if (lock_status == MAP_RWLOCK_FREE) {
|
| |
+ /* The plugin lock is free, acquire it */
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: current lock_status == MAP_RWLOCK_FREE\n");
|
| |
+ #endif
|
| |
+ set_plugin_monitor_status(MAP_RLOCK_HELD);
|
| |
+ set_plugin_monitor_count(1);
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ if (lock_count != 0) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: (%p) ALERT !!! count was %d -> 1\n", PR_GetCurrentThread(), lock_count);
|
| |
+ }
|
| |
+ #endif
|
| |
+
|
| |
+ /* Acquire the slapi plugin in read */
|
| |
+ rc = plugin_rdlock();
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: (%p) MAP_RWLOCK_FREE -> MAP_RLOCK_HELD: fail to read lock plugin lock (%d)\n", PR_GetCurrentThread(), rc);
|
| |
+ return rc;
|
| |
+ }
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: (%p) MAP_RWLOCK_FREE -> MAP_RLOCK_HELD : count=%d\n", PR_GetCurrentThread(), 1);
|
| |
+ #endif
|
| |
+ rc = wrap_rwlock_rdlock(map_data.lock);
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "Fail to acquire map lock in read (%d)\n", rc);
|
| |
+ plugin_unlock();
|
| |
+ return rc;
|
| |
+ }
|
| |
+ return 0;
|
| |
+ }
|
| |
+
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map rdlock: (%p) was already hold %s : count=%d > %d!!!\n",
|
| |
+ PR_GetCurrentThread(),
|
| |
+ (lock_status == MAP_WLOCK_HELD) ? "MAP_WLOCK_HELD": "MAP_RLOCK_HELD",
|
| |
+ lock_count, lock_count + 1);
|
| |
+ #endif
|
| |
+ set_plugin_monitor_count(lock_count + 1);
|
| |
+ return 0;
|
| |
+ }
|
| |
+
|
| |
+ int
|
| |
+ map_wrlock(void)
|
| |
+ {
|
| |
+ int lock_status;
|
| |
+ int lock_count;
|
| |
+ int rc = 0;
|
| |
+
|
| |
+ if (rw_monitor_enabled() == MAP_MONITOR_DISABLED) {
|
| |
+ /* This is not initialized used the old way */
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: old way MAP_MONITOR_DISABLED\n");
|
| |
+ return wrap_rwlock_wrlock(map_data.lock);
|
| |
+ }
|
| |
+
|
| |
+ lock_status = get_plugin_monitor_status();
|
| |
+ lock_count = get_plugin_monitor_count();
|
| |
+
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "map wrlock",
|
| |
+ "thread_id = %p (call level = %d)\n", PR_GetCurrentThread(), wrap_get_call_level());
|
| |
+ #endif
|
| |
+ if (lock_status == MAP_RWLOCK_UNINIT) {
|
| |
+ /* This is not initialized used the old way */
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: old way lock_status == MAP_LOCK_UNINIT\n");
|
| |
+
|
| |
+ return wrap_rwlock_wrlock(map_data.lock);
|
| |
+ }
|
| |
+
|
| |
+ if (lock_status == MAP_RWLOCK_FREE) {
|
| |
+ /* The lock is free, acquire it */
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: current lock_status == MAP_LOCK_FREE\n");
|
| |
+ #endif
|
| |
+
|
| |
+ set_plugin_monitor_count(1);
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ if (lock_count != 0) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: (%p) ALERT !!! count was %d --> 1\n", PR_GetCurrentThread(), lock_count);
|
| |
+ }
|
| |
+ #endif
|
| |
+ /* Acquire the slapi plugin in write */
|
| |
+ rc = plugin_wrlock();
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schemacompat",
|
| |
+ "map wrlock: (%p) MAP_RWLOCK_FREE -> MAP_RLOCK_HELD: fail to read lock plugin lock (%d)\n", PR_GetCurrentThread(), rc);
|
| |
+ return rc;
|
| |
+ }
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: (%p) MAP_RWLOCK_FREE --> MAP_WLOCK_HELD : count=%d\n", PR_GetCurrentThread(), 1);
|
| |
+ #endif
|
| |
+
|
| |
+ rc = wrap_rwlock_wrlock(map_data.lock);
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: (%p) MAP_RWLOCK_FREE --> MAP_WLOCK_HELD : fail to write lock map lock (%d)\n", PR_GetCurrentThread(), rc);
|
| |
+ plugin_unlock();
|
| |
+ goto common;
|
| |
+ }
|
| |
+ } else {
|
| |
+ set_plugin_monitor_count(lock_count + 1);
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: (%p) %s --> MAP_WLOCK_HELD : count=%d\n",
|
| |
+ PR_GetCurrentThread(),
|
| |
+ (lock_status == MAP_WLOCK_HELD) ? "MAP_WLOCK_HELD": "MAP_RLOCK_HELD",
|
| |
+ lock_count + 1);
|
| |
+ #endif
|
| |
+
|
| |
+ if (lock_status == MAP_RLOCK_HELD) {
|
| |
+ /* lock is already acquired in read */
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: weird situation map lock is held in read and now required in write mode\n");
|
| |
+ #endif
|
| |
+ /* First free the lock held in read */
|
| |
+ rc = plugin_unlock();
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: fail to unlock plugin lock (%d)\n", rc);
|
| |
+ goto common;
|
| |
+ }
|
| |
+
|
| |
+ /* Second acquire it in write */
|
| |
+ rc = plugin_wrlock();
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map wrlock: fail to write lock plugin lock (%d)\n", rc);
|
| |
+ goto common;
|
| |
+ }
|
| |
+ }
|
| |
+ }
|
| |
+
|
| |
+ common:
|
| |
+ set_plugin_monitor_status(MAP_WLOCK_HELD);
|
| |
+ return rc;
|
| |
+ }
|
| |
+
|
| |
+ int
|
| |
+ map_unlock(void)
|
| |
+ {
|
| |
+ int lock_status;
|
| |
+ int lock_count;
|
| |
+ int rc = 0;
|
| |
+
|
| |
+ if (rw_monitor_enabled() == MAP_MONITOR_DISABLED) {
|
| |
+ /* This is not initialized used the old way */
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map_unlock: old way MAP_MONITOR_DISABLED\n");
|
| |
+ return wrap_rwlock_unlock(map_data.lock);
|
| |
+ }
|
| |
+
|
| |
+ lock_status = get_plugin_monitor_status();
|
| |
+ lock_count = get_plugin_monitor_count();
|
| |
+
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "map_unlock",
|
| |
+ "thread_id = %p (call level = %d)\n", PR_GetCurrentThread(), wrap_get_call_level());
|
| |
+ #endif
|
| |
+ if (lock_status == MAP_RWLOCK_UNINIT) {
|
| |
+ /* This is not initialized used the old way */
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map_unlock: old way lock_status == MAP_RWLOCK_UNINIT\n");
|
| |
+
|
| |
+ return wrap_rwlock_unlock(map_data.lock);
|
| |
+ }
|
| |
+
|
| |
+ if (lock_count == 1) {
|
| |
+ set_plugin_monitor_status(MAP_RWLOCK_FREE);
|
| |
+ rc = plugin_unlock();
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map unlock: fail to unlock plugin lock (%d)\n", rc);
|
| |
+ goto common;
|
| |
+ }
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map_unlock: (%p) %s --> MAP_RWLOCK_FREE : count=%d\n",
|
| |
+ PR_GetCurrentThread(),
|
| |
+ (lock_status == MAP_WLOCK_HELD) ? "MAP_WLOCK_HELD" : (lock_status == MAP_RLOCK_HELD) ? "MAP_RLOCK_HELD" : "MAP_RWLOCK_FREE",
|
| |
+ 0);
|
| |
+ #endif
|
| |
+ rc = wrap_rwlock_unlock(map_data.lock);
|
| |
+ if (rc) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map_unlock: fail to unlock map lock (%d)\n", rc);
|
| |
+ goto common;
|
| |
+ }
|
| |
+ }
|
| |
+ if (lock_count >= 1) {
|
| |
+ set_plugin_monitor_count(lock_count - 1);
|
| |
+ #if DEBUG_MAP_LOCK
|
| |
+ if (lock_count > 1) {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map_unlock: (%p) keep %s : count=%d\n",
|
| |
+ PR_GetCurrentThread(),
|
| |
+ (lock_status == MAP_WLOCK_HELD) ? "MAP_WLOCK_HELD" : (lock_status == MAP_RLOCK_HELD) ? "MAP_RLOCK_HELD" : "MAP_RWLOCK_FREE",
|
| |
+ lock_count - 1);
|
| |
+ } else {
|
| |
+ slapi_log_error(SLAPI_LOG_FATAL, "schema-compat",
|
| |
+ "map_unlock: (%p) is now %s : count=%d\n",
|
| |
+ PR_GetCurrentThread(),
|
| |
+ "MAP_RWLOCK_FREE",
|
| |
+ lock_count - 1);
|
| |
+ }
|
| |
+ #endif
|
| |
+ }
|
| |
+
|
| |
+ common:
|
| |
+ return rc;
|
| |
+ }
|
| |
Problem description:
Schema compat is a betxn pre/post op plugin managing maps.
The maps are protected by a RW lock.
A typical deadlock scenario is when a read thread (SRCH) holding
the map lock needs a ressource (DB page) acquired by an
write thread (MOD/ADD/DEL..) and that write thread needs to update
the map and so acquire the map lock.
Fix description:
The fix implements a plugin RW reentrant lock 'plugin_lock'.
To do this it uses a thread private variables (thread_plugin_lock_status and
thread_plugin_lock_count to remember the current status of the lock
(free, read_acquired, write_acquired).
Signed-off-by: Thierry Bordaz tbordaz@redhat.com