blob: 610a023c06530161ea78898a828d1896697cf534 [file] [log] [blame]
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +02001#ifndef __XREF_H__
2#define __XREF_H__
3
Thierry FOURNIER952939d2017-09-01 14:17:32 +02004#include <common/hathreads.h>
5
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +02006/* xref is used to create relation between two elements.
7 * Once an element is released, it breaks the relation. If the
8 * relation is already broken, it frees the xref struct.
9 * The pointer between two elements is sort of a refcount with
10 * max value 1. The relation is only between two elements.
11 * The pointer and the type of elements a and b are conventional.
12 */
13
14struct xref {
15 struct xref *peer;
16};
17
Thierry FOURNIER952939d2017-09-01 14:17:32 +020018#define XREF_BUSY ((struct xref *)1)
19
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +020020static inline void xref_create(struct xref *xref_a, struct xref *xref_b)
21{
22 xref_a->peer = xref_b;
23 xref_b->peer = xref_a;
24}
25
Thierry FOURNIER952939d2017-09-01 14:17:32 +020026static inline struct xref *xref_get_peer_and_lock(struct xref *xref)
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +020027{
Thierry FOURNIER952939d2017-09-01 14:17:32 +020028 struct xref *local;
29 struct xref *remote;
30
31 while (1) {
32
33 /* Get the local pointer to the peer. */
Olivier Houchard8beb27e2019-03-08 18:47:29 +010034 local = _HA_ATOMIC_XCHG(&xref->peer, XREF_BUSY);
Olivier Houchardd2b5d162019-03-08 13:47:21 +010035 __ha_barrier_atomic_store();
Thierry FOURNIER952939d2017-09-01 14:17:32 +020036
37 /* If the local pointer is NULL, the peer no longer exists. */
38 if (local == NULL) {
39 xref->peer = NULL;
40 return NULL;
41 }
42
43 /* If the local pointeru is BUSY, the peer try to acquire the
44 * lock. We retry the process.
45 */
46 if (local == XREF_BUSY)
47 continue;
48
Ilya Shipitsin77e3b4a2020-03-10 12:06:11 +050049 /* We are locked, the peer can't disappear, try to acquire
Thierry FOURNIER952939d2017-09-01 14:17:32 +020050 * the pper's lock. Note that remote can't be NULL.
51 */
Olivier Houchard8beb27e2019-03-08 18:47:29 +010052 remote = _HA_ATOMIC_XCHG(&local->peer, XREF_BUSY);
Thierry FOURNIER952939d2017-09-01 14:17:32 +020053
54 /* The remote lock is BUSY, We retry the process. */
55 if (remote == XREF_BUSY) {
56 xref->peer = local;
Olivier Houchardff5dd742019-01-18 17:21:32 +010057 __ha_barrier_store();
Thierry FOURNIER952939d2017-09-01 14:17:32 +020058 continue;
59 }
60
61 /* We have the lock, we return the value of the xref. */
62 return local;
63 }
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +020064}
65
Thierry FOURNIER952939d2017-09-01 14:17:32 +020066static inline void xref_unlock(struct xref *xref, struct xref *peer)
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +020067{
Thierry FOURNIER952939d2017-09-01 14:17:32 +020068 /* Release the peer. */
69 peer->peer = xref;
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +020070
Olivier Houchardff5dd742019-01-18 17:21:32 +010071 __ha_barrier_store();
72
Thierry FOURNIER952939d2017-09-01 14:17:32 +020073 /* Release myself. */
74 xref->peer = peer;
75}
76
77static inline void xref_disconnect(struct xref *xref, struct xref *peer)
78{
79 peer->peer = NULL;
Olivier Houchardff5dd742019-01-18 17:21:32 +010080 __ha_barrier_store();
Thierry FOURNIER3c65b7a2017-08-31 20:35:18 +020081 xref->peer = NULL;
82}
83
84#endif /* __XREF_H__ */