mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 15:17:01 +02:00
BUG/MINOR: guid/server: ensure thread-safety on GUID insert/delete
Since 3.0, it is possible to assign a GUID to proxies, listeners and servers. These objects are stored in a global tree guid_tree. Proxies and listeners are static. However, servers may be added or deleted at runtime, which imply that guid_tree must be protected. Fix this by declaring a read-write lock to protect tree access. For now, only guid_insert() and guid_remove() are protected using a write lock. Outside of these, GUID tree is not accessed at runtime. If server CLI commands are extended to support GUID as server identifier, lookup operation should be extended with a read lock protection. Note that during stat-file preloading, GUID tree is accessed for lookup. However, as it is performed on startup which is single threaded, there is no need for lock here. A BUG_ON() has been added to ensure this precondition remains true. This bug could caused a segfault when using dynamic servers with GUID. However, it was never reproduced for now. This must be backported up to 3.0. To avoid a conflict issue, the previous cleanup patch can be merged before it.
This commit is contained in:
parent
b70880cdc9
commit
8e0e7d9d1a
@ -1,7 +1,11 @@
|
|||||||
#ifndef _HAPROXY_GUID_H
|
#ifndef _HAPROXY_GUID_H
|
||||||
#define _HAPROXY_GUID_H
|
#define _HAPROXY_GUID_H
|
||||||
|
|
||||||
|
#include <haproxy/api-t.h>
|
||||||
#include <haproxy/guid-t.h>
|
#include <haproxy/guid-t.h>
|
||||||
|
#include <haproxy/thread-t.h>
|
||||||
|
|
||||||
|
__decl_thread(extern HA_RWLOCK_T guid_lock);
|
||||||
|
|
||||||
void guid_init(struct guid_node *node);
|
void guid_init(struct guid_node *node);
|
||||||
int guid_insert(enum obj_type *obj_type, const char *uid, char **errmsg);
|
int guid_insert(enum obj_type *obj_type, const char *uid, char **errmsg);
|
||||||
|
@ -205,6 +205,7 @@ enum lock_label {
|
|||||||
OCSP_LOCK,
|
OCSP_LOCK,
|
||||||
QC_CID_LOCK,
|
QC_CID_LOCK,
|
||||||
CACHE_LOCK,
|
CACHE_LOCK,
|
||||||
|
GUID_LOCK,
|
||||||
OTHER_LOCK,
|
OTHER_LOCK,
|
||||||
/* WT: make sure never to use these ones outside of development,
|
/* WT: make sure never to use these ones outside of development,
|
||||||
* we need them for lock profiling!
|
* we need them for lock profiling!
|
||||||
|
14
src/guid.c
14
src/guid.c
@ -6,9 +6,11 @@
|
|||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/server-t.h>
|
#include <haproxy/server-t.h>
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
|
#include <haproxy/thread.h>
|
||||||
|
|
||||||
/* GUID global tree */
|
/* GUID global tree */
|
||||||
struct eb_root guid_tree = EB_ROOT_UNIQUE;
|
struct eb_root guid_tree = EB_ROOT_UNIQUE;
|
||||||
|
__decl_thread(HA_RWLOCK_T guid_lock);
|
||||||
|
|
||||||
/* Initialize <guid> members. */
|
/* Initialize <guid> members. */
|
||||||
void guid_init(struct guid_node *guid)
|
void guid_init(struct guid_node *guid)
|
||||||
@ -60,13 +62,17 @@ int guid_insert(enum obj_type *objt, const char *uid, char **errmsg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
guid->node.key = key;
|
guid->node.key = key;
|
||||||
|
|
||||||
|
HA_RWLOCK_WRLOCK(GUID_LOCK, &guid_lock);
|
||||||
node = ebis_insert(&guid_tree, &guid->node);
|
node = ebis_insert(&guid_tree, &guid->node);
|
||||||
if (node != &guid->node) {
|
if (node != &guid->node) {
|
||||||
dup = ebpt_entry(node, struct guid_node, node);
|
dup = ebpt_entry(node, struct guid_node, node);
|
||||||
|
HA_RWLOCK_WRUNLOCK(GUID_LOCK, &guid_lock);
|
||||||
dup_name = guid_name(dup);
|
dup_name = guid_name(dup);
|
||||||
memprintf(errmsg, "duplicate entry with %s", dup_name);
|
memprintf(errmsg, "duplicate entry with %s", dup_name);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
HA_RWLOCK_WRUNLOCK(GUID_LOCK, &guid_lock);
|
||||||
|
|
||||||
guid->obj_type = objt;
|
guid->obj_type = objt;
|
||||||
return 0;
|
return 0;
|
||||||
@ -82,8 +88,10 @@ int guid_insert(enum obj_type *objt, const char *uid, char **errmsg)
|
|||||||
*/
|
*/
|
||||||
void guid_remove(struct guid_node *guid)
|
void guid_remove(struct guid_node *guid)
|
||||||
{
|
{
|
||||||
|
HA_RWLOCK_WRLOCK(GUID_LOCK, &guid_lock);
|
||||||
ebpt_delete(&guid->node);
|
ebpt_delete(&guid->node);
|
||||||
ha_free(&guid->node.key);
|
ha_free(&guid->node.key);
|
||||||
|
HA_RWLOCK_WRUNLOCK(GUID_LOCK, &guid_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve an instance from GUID global tree with key <uid>.
|
/* Retrieve an instance from GUID global tree with key <uid>.
|
||||||
@ -95,6 +103,12 @@ struct guid_node *guid_lookup(const char *uid)
|
|||||||
struct ebpt_node *node = NULL;
|
struct ebpt_node *node = NULL;
|
||||||
struct guid_node *guid = NULL;
|
struct guid_node *guid = NULL;
|
||||||
|
|
||||||
|
/* For now, guid_lookup() is only used during startup in single-thread
|
||||||
|
* mode. If this is not the case anymore, GUID tree access must be
|
||||||
|
* protected with the read-write lock.
|
||||||
|
*/
|
||||||
|
BUG_ON(!(global.mode & MODE_STARTING));
|
||||||
|
|
||||||
node = ebis_lookup(&guid_tree, uid);
|
node = ebis_lookup(&guid_tree, uid);
|
||||||
if (node)
|
if (node)
|
||||||
guid = ebpt_entry(node, struct guid_node, node);
|
guid = ebpt_entry(node, struct guid_node, node);
|
||||||
|
@ -462,6 +462,7 @@ static const char *lock_label(enum lock_label label)
|
|||||||
case OCSP_LOCK: return "OCSP";
|
case OCSP_LOCK: return "OCSP";
|
||||||
case QC_CID_LOCK: return "QC_CID";
|
case QC_CID_LOCK: return "QC_CID";
|
||||||
case CACHE_LOCK: return "CACHE";
|
case CACHE_LOCK: return "CACHE";
|
||||||
|
case GUID_LOCK: return "GUID";
|
||||||
case OTHER_LOCK: return "OTHER";
|
case OTHER_LOCK: return "OTHER";
|
||||||
case DEBUG1_LOCK: return "DEBUG1";
|
case DEBUG1_LOCK: return "DEBUG1";
|
||||||
case DEBUG2_LOCK: return "DEBUG2";
|
case DEBUG2_LOCK: return "DEBUG2";
|
||||||
|
Loading…
Reference in New Issue
Block a user