Blame SOURCES/0031-Issue-4667-incorrect-accounting-of-readers-in-vattr-.patch

fcc543
From 6a1af7917a86c0b7218d876852cd9c0d5027edeb Mon Sep 17 00:00:00 2001
fcc543
From: tbordaz <tbordaz@redhat.com>
fcc543
Date: Thu, 29 Apr 2021 09:29:44 +0200
fcc543
Subject: [PATCH 2/2] Issue 4667 - incorrect accounting of readers in vattr
fcc543
 rwlock (#4732)
fcc543
fcc543
Bug description:
fcc543
	The fix #2932 (Contention on virtual attribute lookup) reduced
fcc543
	contention on vattr acquiring vattr lock at the operation
fcc543
	level rather than at the attribute level (filter and
fcc543
        returned attr).
fcc543
        The fix #2932 is invalid. it can lead to deadlock scenario
fcc543
	(3 threads). A vattr writer (new cos/schema) blocks
fcc543
        an update thread that hold DB pages and later needs vattr.
fcc543
	Then if a reader (holding vattr) blocks vattr writer and later
fcc543
        needs the same DB pages, there is a deadlock.
fcc543
	The decisions are:
fcc543
		- revert #2932 (this issue)
fcc543
		- Skip contention if deployement has no vattr #4678
fcc543
		- reduce contention with new approaches
fcc543
                  (COW and/or cache vattr struct in each thread)
fcc543
		  no issue opened
fcc543
fcc543
Fix description:
fcc543
	The fix reverts #2932
fcc543
fcc543
relates: https://github.com/389ds/389-ds-base/issues/4667
fcc543
fcc543
Reviewed by: William Brown, Simon Pichugin
fcc543
fcc543
Platforms tested:  F33
fcc543
---
fcc543
 ldap/servers/slapd/detach.c     |   8 --
fcc543
 ldap/servers/slapd/opshared.c   |   6 --
fcc543
 ldap/servers/slapd/proto-slap.h |   5 --
fcc543
 ldap/servers/slapd/vattr.c      | 147 ++------------------------------
fcc543
 4 files changed, 8 insertions(+), 158 deletions(-)
fcc543
fcc543
diff --git a/ldap/servers/slapd/detach.c b/ldap/servers/slapd/detach.c
fcc543
index d5c95a04f..8270b83c5 100644
fcc543
--- a/ldap/servers/slapd/detach.c
fcc543
+++ b/ldap/servers/slapd/detach.c
fcc543
@@ -144,10 +144,6 @@ detach(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *
fcc543
             }
fcc543
             break;
fcc543
         }
fcc543
-        /* The thread private counter needs to be allocated after the fork
fcc543
-         * it is not inherited from parent process
fcc543
-         */
fcc543
-        vattr_global_lock_create();
fcc543
 
fcc543
         /* call this right after the fork, but before closing stdin */
fcc543
         if (slapd_do_all_nss_ssl_init(slapd_exemode, importexport_encrypt, s_port, ports_info)) {
fcc543
@@ -178,10 +174,6 @@ detach(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *
fcc543
 
fcc543
         g_set_detached(1);
fcc543
     } else { /* not detaching - call nss/ssl init */
fcc543
-        /* The thread private counter needs to be allocated after the fork
fcc543
-         * it is not inherited from parent process
fcc543
-         */
fcc543
-        vattr_global_lock_create();
fcc543
 
fcc543
         if (slapd_do_all_nss_ssl_init(slapd_exemode, importexport_encrypt, s_port, ports_info)) {
fcc543
             return 1;
fcc543
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
fcc543
index 05b9a1553..a1630e134 100644
fcc543
--- a/ldap/servers/slapd/opshared.c
fcc543
+++ b/ldap/servers/slapd/opshared.c
fcc543
@@ -266,7 +266,6 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
fcc543
     int pr_idx = -1;
fcc543
     Slapi_DN *orig_sdn = NULL;
fcc543
     int free_sdn = 0;
fcc543
-    PRBool vattr_lock_acquired = PR_FALSE;
fcc543
 
fcc543
     be_list[0] = NULL;
fcc543
     referral_list[0] = NULL;
fcc543
@@ -538,8 +537,6 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
fcc543
     }
fcc543
 
fcc543
     slapi_pblock_set(pb, SLAPI_BACKEND_COUNT, &index);
fcc543
-    vattr_rdlock();
fcc543
-    vattr_lock_acquired = PR_TRUE;
fcc543
 
fcc543
     if (be) {
fcc543
         slapi_pblock_set(pb, SLAPI_BACKEND, be);
fcc543
@@ -1007,9 +1004,6 @@ free_and_return:
fcc543
     } else if (be_single) {
fcc543
         slapi_be_Unlock(be_single);
fcc543
     }
fcc543
-    if (vattr_lock_acquired) {
fcc543
-        vattr_rd_unlock();
fcc543
-    }
fcc543
 
fcc543
 free_and_return_nolock:
fcc543
     slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
fcc543
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
fcc543
index 13b41ffa4..50f1ff75a 100644
fcc543
--- a/ldap/servers/slapd/proto-slap.h
fcc543
+++ b/ldap/servers/slapd/proto-slap.h
fcc543
@@ -1415,11 +1415,6 @@ void subentry_create_filter(Slapi_Filter **filter);
fcc543
  * vattr.c
fcc543
  */
fcc543
 void vattr_init(void);
fcc543
-void vattr_global_lock_create(void);
fcc543
-void vattr_rdlock();
fcc543
-void vattr_rd_unlock();
fcc543
-void vattr_wrlock();
fcc543
-void vattr_wr_unlock();
fcc543
 void vattr_cleanup(void);
fcc543
 
fcc543
 /*
fcc543
diff --git a/ldap/servers/slapd/vattr.c b/ldap/servers/slapd/vattr.c
fcc543
index eef444270..b9a4ee965 100644
fcc543
--- a/ldap/servers/slapd/vattr.c
fcc543
+++ b/ldap/servers/slapd/vattr.c
fcc543
@@ -110,7 +110,6 @@ struct _vattr_map
fcc543
 typedef struct _vattr_map vattr_map;
fcc543
 
fcc543
 static vattr_map *the_map = NULL;
fcc543
-static PRUintn thread_private_global_vattr_lock;
fcc543
 
fcc543
 /* Housekeeping Functions, called by server startup/shutdown code */
fcc543
 
fcc543
@@ -125,136 +124,6 @@ vattr_init()
fcc543
     vattr_basic_sp_init();
fcc543
 #endif
fcc543
 }
fcc543
-/*
fcc543
- * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSPR/Reference/PR_NewThreadPrivateIndex
fcc543
- * It is called each time:
fcc543
- *  - PR_SetThreadPrivate is call with a not NULL private value
fcc543
- *  - on thread exit
fcc543
- */
fcc543
-static void
fcc543
-vattr_global_lock_free(void *ptr)
fcc543
-{
fcc543
-    int *nb_acquired = ptr;
fcc543
-    if (nb_acquired) {
fcc543
-        slapi_ch_free((void **)&nb_acquired);
fcc543
-    }
fcc543
-}
fcc543
-/* Create a private variable for each individual thread of the current process */
fcc543
-void
fcc543
-vattr_global_lock_create()
fcc543
-{
fcc543
-    if (PR_NewThreadPrivateIndex(&thread_private_global_vattr_lock, vattr_global_lock_free) != PR_SUCCESS) {
fcc543
-        slapi_log_err(SLAPI_LOG_ALERT,
fcc543
-              "vattr_global_lock_create", "Failure to create global lock for virtual attribute !\n");
fcc543
-        PR_ASSERT(0);
fcc543
-    }
fcc543
-}
fcc543
-static int
fcc543
-global_vattr_lock_get_acquired_count()
fcc543
-{
fcc543
-    int *nb_acquired;
fcc543
-    nb_acquired = (int *) PR_GetThreadPrivate(thread_private_global_vattr_lock);
fcc543
-    if (nb_acquired == NULL) {
fcc543
-        /* if it was not initialized set it to zero */
fcc543
-        nb_acquired = (int *) slapi_ch_calloc(1, sizeof(int));
fcc543
-        PR_SetThreadPrivate(thread_private_global_vattr_lock, (void *) nb_acquired);
fcc543
-    }
fcc543
-    return *nb_acquired;
fcc543
-}
fcc543
-static void
fcc543
-global_vattr_lock_set_acquired_count(int nb_acquired)
fcc543
-{
fcc543
-    int *val;
fcc543
-    val = (int *) PR_GetThreadPrivate(thread_private_global_vattr_lock);
fcc543
-    if (val == NULL) {
fcc543
-        /* if it was not initialized set it to zero */
fcc543
-        val = (int *) slapi_ch_calloc(1, sizeof(int));
fcc543
-        PR_SetThreadPrivate(thread_private_global_vattr_lock, (void *) val);
fcc543
-    }
fcc543
-    *val = nb_acquired;
fcc543
-}
fcc543
-/* The map lock can be acquired recursively. So only the first rdlock
fcc543
- * will acquire the lock.
fcc543
- * A optimization acquires it at high level (op_shared_search), so that
fcc543
- * later calls during the operation processing will just increase/decrease a counter.
fcc543
- */
fcc543
-void
fcc543
-vattr_rdlock()
fcc543
-{
fcc543
-    int nb_acquire = global_vattr_lock_get_acquired_count();
fcc543
-
fcc543
-    if (nb_acquire == 0) {
fcc543
-        /* The lock was not held just acquire it */
fcc543
-        slapi_rwlock_rdlock(the_map->lock);
fcc543
-    }
fcc543
-    nb_acquire++;
fcc543
-    global_vattr_lock_set_acquired_count(nb_acquire);
fcc543
-
fcc543
-}
fcc543
-/* The map lock can be acquired recursively. So only the last unlock
fcc543
- * will release the lock.
fcc543
- * A optimization acquires it at high level (op_shared_search), so that
fcc543
- * later calls during the operation processing will just increase/decrease a counter.
fcc543
- */
fcc543
-void
fcc543
-vattr_rd_unlock()
fcc543
-{
fcc543
-    int nb_acquire = global_vattr_lock_get_acquired_count();
fcc543
-
fcc543
-    if (nb_acquire >= 1) {
fcc543
-        nb_acquire--;
fcc543
-        if (nb_acquire == 0) {
fcc543
-            slapi_rwlock_unlock(the_map->lock);
fcc543
-        }
fcc543
-        global_vattr_lock_set_acquired_count(nb_acquire);
fcc543
-    } else {
fcc543
-        /* this is likely the consequence of lock acquire in read during an internal search
fcc543
-         * but the search callback updated the map and release the readlock and acquired
fcc543
-         * it in write.
fcc543
-         * So after the update the lock was no longer held but when completing the internal
fcc543
-         * search we release the global read lock, that now has nothing to do
fcc543
-         */
fcc543
-        slapi_log_err(SLAPI_LOG_DEBUG,
fcc543
-          "vattr_rd_unlock", "vattr lock no longer acquired in read.\n");
fcc543
-    }
fcc543
-}
fcc543
-
fcc543
-/* The map lock is acquired in write (updating the map)
fcc543
- * It exists a possibility that lock is acquired in write while it is already
fcc543
- * hold in read by this thread (internal search with updating callback)
fcc543
- * In such situation, the we must abandon the read global lock and acquire in write
fcc543
- */
fcc543
-void
fcc543
-vattr_wrlock()
fcc543
-{
fcc543
-    int nb_read_acquire = global_vattr_lock_get_acquired_count();
fcc543
-
fcc543
-    if (nb_read_acquire) {
fcc543
-        /* The lock was acquired in read but we need it in write
fcc543
-         * release it and set the global vattr_lock counter to 0
fcc543
-         */
fcc543
-        slapi_rwlock_unlock(the_map->lock);
fcc543
-        global_vattr_lock_set_acquired_count(0);
fcc543
-    }
fcc543
-    slapi_rwlock_wrlock(the_map->lock);
fcc543
-}
fcc543
-/* The map lock is release from a write write (updating the map)
fcc543
- */
fcc543
-void
fcc543
-vattr_wr_unlock()
fcc543
-{
fcc543
-    int nb_read_acquire = global_vattr_lock_get_acquired_count();
fcc543
-
fcc543
-    if (nb_read_acquire) {
fcc543
-        /* The lock being acquired in write, the private thread counter
fcc543
-         * (that count the number of time it was acquired in read) should be 0
fcc543
-         */
fcc543
-        slapi_log_err(SLAPI_LOG_INFO,
fcc543
-          "vattr_unlock", "The lock was acquired in write. We should not be here\n");
fcc543
-        PR_ASSERT(nb_read_acquire == 0);
fcc543
-    }
fcc543
-    slapi_rwlock_unlock(the_map->lock);
fcc543
-}
fcc543
 /* Called on server shutdown, free all structures, inform service providers that we're going down etc */
fcc543
 void
fcc543
 vattr_cleanup()
fcc543
@@ -2069,11 +1938,11 @@ vattr_map_lookup(const char *type_to_find, vattr_map_entry **result)
fcc543
     }
fcc543
 
fcc543
     /* Get the reader lock */
fcc543
-    vattr_rdlock();
fcc543
+    slapi_rwlock_rdlock(the_map->lock);
fcc543
     *result = (vattr_map_entry *)PL_HashTableLookupConst(the_map->hashtable,
fcc543
                                                          (void *)basetype);
fcc543
     /* Release ze lock */
fcc543
-    vattr_rd_unlock();
fcc543
+    slapi_rwlock_unlock(the_map->lock);
fcc543
 
fcc543
     if (tmp) {
fcc543
         slapi_ch_free_string(&tmp);
fcc543
@@ -2092,13 +1961,13 @@ vattr_map_insert(vattr_map_entry *vae)
fcc543
 {
fcc543
     PR_ASSERT(the_map);
fcc543
     /* Get the writer lock */
fcc543
-    vattr_wrlock();
fcc543
+    slapi_rwlock_wrlock(the_map->lock);
fcc543
     /* Insert the thing */
fcc543
     /* It's illegal to call this function if the entry is already there */
fcc543
     PR_ASSERT(NULL == PL_HashTableLookupConst(the_map->hashtable, (void *)vae->type_name));
fcc543
     PL_HashTableAdd(the_map->hashtable, (void *)vae->type_name, (void *)vae);
fcc543
     /* Unlock and we're done */
fcc543
-    vattr_wr_unlock();
fcc543
+    slapi_rwlock_unlock(the_map->lock);
fcc543
     return 0;
fcc543
 }
fcc543
 
fcc543
@@ -2235,13 +2104,13 @@ schema_changed_callback(Slapi_Entry *e __attribute__((unused)),
fcc543
                         void *caller_data __attribute__((unused)))
fcc543
 {
fcc543
     /* Get the writer lock */
fcc543
-    vattr_wrlock();
fcc543
+    slapi_rwlock_wrlock(the_map->lock);
fcc543
 
fcc543
     /* go through the list */
fcc543
     PL_HashTableEnumerateEntries(the_map->hashtable, vattr_map_entry_rebuild_schema, 0);
fcc543
 
fcc543
     /* Unlock and we're done */
fcc543
-    vattr_wr_unlock();
fcc543
+    slapi_rwlock_unlock(the_map->lock);
fcc543
 }
fcc543
 
fcc543
 
fcc543
@@ -2261,7 +2130,7 @@ slapi_vattr_schema_check_type(Slapi_Entry *e, char *type)
fcc543
                 objAttrValue *obj;
fcc543
 
fcc543
                 if (0 == vattr_map_lookup(type, &map_entry)) {
fcc543
-                    vattr_rdlock();
fcc543
+                    slapi_rwlock_rdlock(the_map->lock);
fcc543
 
fcc543
                     obj = map_entry->objectclasses;
fcc543
 
fcc543
@@ -2278,7 +2147,7 @@ slapi_vattr_schema_check_type(Slapi_Entry *e, char *type)
fcc543
                         obj = obj->pNext;
fcc543
                     }
fcc543
 
fcc543
-                    vattr_rd_unlock();
fcc543
+                    slapi_rwlock_unlock(the_map->lock);
fcc543
                 }
fcc543
 
fcc543
                 slapi_valueset_free(vs);
fcc543
-- 
fcc543
2.31.1
fcc543