#define _PROTO_PROTOCOL_H
#include <sys/socket.h>
+#include <common/hathreads.h>
#include <types/protocol.h>
extern struct protocol *__protocol_by_family[AF_CUST_MAX];
+__decl_hathreads(extern HA_SPINLOCK_T proto_lock);
/* Registers the protocol <proto> */
void protocol_register(struct protocol *proto);
int (*pause)(struct listener *l); /* temporarily pause this listener for a soft restart */
void (*add)(struct listener *l, int port); /* add a listener for this protocol and port */
- struct list listeners; /* list of listeners using this protocol */
- int nb_listeners; /* number of listeners */
- struct list list; /* list of registered protocols */
+ struct list listeners; /* list of listeners using this protocol (under proto_lock) */
+ int nb_listeners; /* number of listeners (under proto_lock) */
+ struct list list; /* list of registered protocols (under proto_lock) */
};
#define CONNECT_HAS_DATA 0x00000001 /* There's data available to be sent */
* used as a protocol's generic enable_all() primitive, for use after the
* fork(). It puts the listeners into LI_READY or LI_FULL states depending on
* their number of connections. It always returns ERR_NONE.
+ *
+ * Must be called with proto_lock held.
+ *
*/
int enable_all_listeners(struct protocol *proto)
{
* the polling lists when they are in the LI_READY or LI_FULL states. It is
* intended to be used as a protocol's generic disable_all() primitive. It puts
* the listeners into LI_LISTEN, and always returns ERR_NONE.
+ *
+ * Must be called with proto_lock held.
+ *
*/
int disable_all_listeners(struct protocol *proto)
{
/* This function closes all listening sockets bound to the protocol <proto>,
* and the listeners end in LI_ASSIGNED state if they were higher. It does not
* detach them from the protocol. It always returns ERR_NONE.
+ *
+ * Must be called with proto_lock held.
+ *
*/
int unbind_all_listeners(struct protocol *proto)
{
* number of listeners is updated, as well as the global number of listeners
* and jobs. Note that the listener must have previously been unbound. This
* is the generic function to use to remove a listener.
+ *
+ * Will grab the proto_lock.
+ *
*/
void delete_listener(struct listener *listener)
{
HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
if (listener->state == LI_ASSIGNED) {
listener->state = LI_INIT;
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
LIST_DEL(&listener->proto_list);
listener->proto->nb_listeners--;
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
_HA_ATOMIC_SUB(&jobs, 1);
_HA_ATOMIC_SUB(&listeners, 1);
}
/* Add <listener> to the list of sockpair listeners (port is ignored). The
* listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
* The number of listeners for the protocol is updated.
+ *
+ * Must be called with proto_lock held.
+ *
*/
static void sockpair_add_listener(struct listener *listener, int port)
{
* loose them across the fork(). A call to uxst_enable_listeners() is needed
* to complete initialization.
*
+ * Must be called with proto_lock held.
+ *
* The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
*/
static int sockpair_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
* The sockets will be registered but not added to any fd_set, in order not to
* loose them across the fork(). A call to enable_all_listeners() is needed
* to complete initialization. The return value is composed from ERR_*.
+ *
+ * Must be called with proto_lock held.
+ *
*/
static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
{
/* Add <listener> to the list of tcpv4 listeners, on port <port>. The
* listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
* The number of listeners for the protocol is updated.
+ *
+ * Must be called with proto_lock held.
+ *
*/
static void tcpv4_add_listener(struct listener *listener, int port)
{
/* Add <listener> to the list of tcpv6 listeners, on port <port>. The
* listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
* The number of listeners for the protocol is updated.
+ *
+ * Must be called with proto_lock held.
+ *
*/
static void tcpv6_add_listener(struct listener *listener, int port)
{
/* Add <listener> to the list of unix stream listeners (port is ignored). The
* listener's state is automatically updated from LI_INIT to LI_ASSIGNED.
* The number of listeners for the protocol is updated.
+ *
+ * Must be called with proto_lock held.
+ *
*/
static void uxst_add_listener(struct listener *listener, int port)
{
* loose them across the fork(). A call to uxst_enable_listeners() is needed
* to complete initialization.
*
+ * Must be called with proto_lock held.
+ *
* The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
*/
static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen)
/* This function stops all listening UNIX sockets bound to the protocol
* <proto>. It does not detaches them from the protocol.
* It always returns ERR_NONE.
+ *
+ * Must be called with proto_lock held.
+ *
*/
static int uxst_unbind_listeners(struct protocol *proto)
{
#include <common/mini-clist.h>
#include <common/standard.h>
-#include <types/protocol.h>
+#include <proto/protocol.h>
/* List head of all registered protocols */
static struct list protocols = LIST_HEAD_INIT(protocols);
struct protocol *__protocol_by_family[AF_CUST_MAX] = { };
+/* This is the global spinlock we may need to register/unregister listeners or
+ * protocols. Its main purpose is in fact to serialize the rare stop/deinit()
+ * phases.
+ */
+__decl_spinlock(proto_lock);
+
/* Registers the protocol <proto> */
void protocol_register(struct protocol *proto)
{
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
LIST_ADDQ(&protocols, &proto->list);
if (proto->sock_domain >= 0 && proto->sock_domain < AF_CUST_MAX)
__protocol_by_family[proto->sock_domain] = proto;
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
}
/* Unregisters the protocol <proto>. Note that all listeners must have
*/
void protocol_unregister(struct protocol *proto)
{
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
LIST_DEL(&proto->list);
LIST_INIT(&proto->list);
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
}
/* binds all listeners of all registered protocols. Returns a composition
int err;
err = 0;
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
list_for_each_entry(proto, &protocols, list) {
if (proto->bind_all) {
err |= proto->bind_all(proto, errmsg, errlen);
break;
}
}
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
return err;
}
int err;
err = 0;
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
list_for_each_entry(proto, &protocols, list) {
if (proto->unbind_all) {
err |= proto->unbind_all(proto);
}
}
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
return err;
}
int err;
err = 0;
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
list_for_each_entry(proto, &protocols, list) {
if (proto->enable_all) {
err |= proto->enable_all(proto);
}
}
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
return err;
}
int err;
err = 0;
+ HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
list_for_each_entry(proto, &protocols, list) {
if (proto->disable_all) {
err |= proto->disable_all(proto);
}
}
+ HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
return err;
}