mirror of
https://github.com/samba-team/samba.git
synced 2024-12-28 07:21:54 +03:00
r7206: changed the messaging library to use sendto instead of a connected
send on the unix domain datagram socket. This gains us about 50% in speed, and also means that we don't run the risk of running out of file descriptors due to heavy messaging traffic. We now use a single file descriptor no matter how many messages are pending to any number of servers.
This commit is contained in:
parent
35ef6e3b15
commit
2369170fc1
@ -31,9 +31,6 @@
|
|||||||
/* change the message version with any incompatible changes in the protocol */
|
/* change the message version with any incompatible changes in the protocol */
|
||||||
#define MESSAGING_VERSION 1
|
#define MESSAGING_VERSION 1
|
||||||
|
|
||||||
/* the number of microseconds to backoff in retrying to send a message */
|
|
||||||
#define MESSAGING_BACKOFF 250000
|
|
||||||
|
|
||||||
/* maximum message size */
|
/* maximum message size */
|
||||||
#define MESSAGING_MAX_SIZE 512
|
#define MESSAGING_MAX_SIZE 512
|
||||||
|
|
||||||
@ -42,6 +39,7 @@ struct messaging_context {
|
|||||||
struct socket_context *sock;
|
struct socket_context *sock;
|
||||||
const char *path;
|
const char *path;
|
||||||
struct dispatch_fn *dispatch;
|
struct dispatch_fn *dispatch;
|
||||||
|
struct messaging_rec *pending;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct event_context *ev;
|
struct event_context *ev;
|
||||||
@ -61,8 +59,8 @@ struct dispatch_fn {
|
|||||||
|
|
||||||
/* an individual message */
|
/* an individual message */
|
||||||
struct messaging_rec {
|
struct messaging_rec {
|
||||||
|
struct messaging_rec *next, *prev;
|
||||||
struct messaging_context *msg;
|
struct messaging_context *msg;
|
||||||
struct socket_context *sock;
|
|
||||||
const char *path;
|
const char *path;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -76,6 +74,7 @@ struct messaging_rec {
|
|||||||
DATA_BLOB data;
|
DATA_BLOB data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A useful function for testing the message system.
|
A useful function for testing the message system.
|
||||||
*/
|
*/
|
||||||
@ -111,19 +110,67 @@ static void messaging_dispatch(struct messaging_context *msg, struct messaging_r
|
|||||||
d->fn(msg, d->private, d->msg_type, rec->header.from, &rec->data);
|
d->fn(msg, d->private, d->msg_type, rec->header.from, &rec->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->header.length = 0;
|
rec->header.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
handle a new incoming connection
|
try to send the message
|
||||||
*/
|
*/
|
||||||
static void messaging_recv_handler(struct event_context *ev, struct fd_event *fde,
|
static NTSTATUS try_send(struct messaging_rec *rec)
|
||||||
uint16_t flags, void *private)
|
{
|
||||||
|
struct messaging_context *msg = rec->msg;
|
||||||
|
DATA_BLOB blob;
|
||||||
|
size_t nsent;
|
||||||
|
void *priv;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
blob = data_blob_talloc(rec, NULL, sizeof(rec->header) + rec->data.length);
|
||||||
|
NT_STATUS_HAVE_NO_MEMORY(blob.data);
|
||||||
|
|
||||||
|
memcpy(blob.data, &rec->header, sizeof(rec->header));
|
||||||
|
memcpy(blob.data + sizeof(rec->header), rec->data.data, rec->data.length);
|
||||||
|
|
||||||
|
/* we send with privileges so messages work from any context */
|
||||||
|
priv = root_privileges();
|
||||||
|
status = socket_sendto(msg->sock, &blob, &nsent, 0, rec->path, 0);
|
||||||
|
talloc_free(priv);
|
||||||
|
|
||||||
|
data_blob_free(&blob);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
handle a socket write event
|
||||||
|
*/
|
||||||
|
static void messaging_send_handler(struct messaging_context *msg)
|
||||||
|
{
|
||||||
|
while (msg->pending) {
|
||||||
|
struct messaging_rec *rec = msg->pending;
|
||||||
|
NTSTATUS status;
|
||||||
|
status = try_send(rec);
|
||||||
|
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
DEBUG(1,("messaging: Lost message from %u to %u of type %u - %s\n",
|
||||||
|
rec->header.from, rec->header.to, rec->header.msg_type,
|
||||||
|
nt_errstr(status)));
|
||||||
|
}
|
||||||
|
DLIST_REMOVE(msg->pending, rec);
|
||||||
|
talloc_free(rec);
|
||||||
|
}
|
||||||
|
if (msg->pending == NULL) {
|
||||||
|
EVENT_FD_NOT_WRITEABLE(msg->event.fde);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
handle a new incoming packet
|
||||||
|
*/
|
||||||
|
static void messaging_recv_handler(struct messaging_context *msg)
|
||||||
{
|
{
|
||||||
struct messaging_context *msg = talloc_get_type(private,
|
|
||||||
struct messaging_context);
|
|
||||||
struct messaging_rec *rec;
|
struct messaging_rec *rec;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
uint8_t data[MESSAGING_MAX_SIZE];
|
uint8_t data[MESSAGING_MAX_SIZE];
|
||||||
@ -146,7 +193,6 @@ static void messaging_recv_handler(struct event_context *ev, struct fd_event *fd
|
|||||||
|
|
||||||
rec->msg = msg;
|
rec->msg = msg;
|
||||||
rec->path = msg->path;
|
rec->path = msg->path;
|
||||||
rec->sock = NULL;
|
|
||||||
|
|
||||||
memcpy(&rec->header, data, sizeof(rec->header));
|
memcpy(&rec->header, data, sizeof(rec->header));
|
||||||
if (msize != sizeof(rec->header) + rec->header.length) {
|
if (msize != sizeof(rec->header) + rec->header.length) {
|
||||||
@ -166,6 +212,24 @@ static void messaging_recv_handler(struct event_context *ev, struct fd_event *fd
|
|||||||
talloc_free(rec);
|
talloc_free(rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
handle a socket event
|
||||||
|
*/
|
||||||
|
static void messaging_handler(struct event_context *ev, struct fd_event *fde,
|
||||||
|
uint16_t flags, void *private)
|
||||||
|
{
|
||||||
|
struct messaging_context *msg = talloc_get_type(private,
|
||||||
|
struct messaging_context);
|
||||||
|
if (flags & EVENT_FD_WRITE) {
|
||||||
|
messaging_send_handler(msg);
|
||||||
|
}
|
||||||
|
if (flags & EVENT_FD_READ) {
|
||||||
|
messaging_recv_handler(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Register a dispatch function for a particular message type.
|
Register a dispatch function for a particular message type.
|
||||||
*/
|
*/
|
||||||
@ -200,89 +264,11 @@ void messaging_deregister(struct messaging_context *msg, uint32_t msg_type, void
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
handle IO for sending a message
|
|
||||||
*/
|
|
||||||
static void messaging_send_handler(struct event_context *ev, struct fd_event *fde,
|
|
||||||
uint16_t flags, void *private)
|
|
||||||
{
|
|
||||||
struct messaging_rec *rec = talloc_get_type(private, struct messaging_rec);
|
|
||||||
uint8_t data[MESSAGING_MAX_SIZE];
|
|
||||||
DATA_BLOB blob;
|
|
||||||
size_t nsent;
|
|
||||||
NTSTATUS status;
|
|
||||||
|
|
||||||
memcpy(data, &rec->header, sizeof(rec->header));
|
|
||||||
memcpy(data + sizeof(rec->header), rec->data.data, rec->data.length);
|
|
||||||
|
|
||||||
blob.data = data;
|
|
||||||
blob.length = sizeof(rec->header) + rec->header.length;
|
|
||||||
|
|
||||||
status = socket_send(rec->sock, &blob, &nsent, 0);
|
|
||||||
if (NT_STATUS_IS_ERR(status)) {
|
|
||||||
DEBUG(3,("Unable to send message of type %d length %d - %s\n",
|
|
||||||
rec->header.msg_type,
|
|
||||||
rec->header.length,
|
|
||||||
nt_errstr(status)));
|
|
||||||
talloc_free(rec);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NT_STATUS_IS_OK(status)) {
|
|
||||||
talloc_free(rec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
wrapper around socket_connect with raised privileges
|
|
||||||
*/
|
|
||||||
static NTSTATUS try_connect(struct messaging_rec *rec)
|
|
||||||
{
|
|
||||||
NTSTATUS status;
|
|
||||||
void *priv = root_privileges();
|
|
||||||
status = socket_connect(rec->sock, NULL, 0, rec->path, 0, 0);
|
|
||||||
talloc_free(priv);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
when the servers listen queue is full we use this to backoff the message
|
|
||||||
*/
|
|
||||||
static void messaging_backoff_handler(struct event_context *ev, struct timed_event *te,
|
|
||||||
struct timeval t, void *private)
|
|
||||||
{
|
|
||||||
struct messaging_rec *rec = talloc_get_type(private, struct messaging_rec);
|
|
||||||
struct messaging_context *msg = rec->msg;
|
|
||||||
NTSTATUS status;
|
|
||||||
|
|
||||||
status = try_connect(rec);
|
|
||||||
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
|
||||||
/* backoff again */
|
|
||||||
event_add_timed(msg->event.ev, rec,
|
|
||||||
timeval_add(&t, 0, MESSAGING_BACKOFF),
|
|
||||||
messaging_backoff_handler, rec);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
|
||||||
DEBUG(1,("messaging: Lost message from %u to %u of type %u after backoff - %s\n",
|
|
||||||
rec->header.from, rec->header.to, rec->header.msg_type, nt_errstr(status)));
|
|
||||||
talloc_free(rec);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_add_fd(msg->event.ev, rec, socket_get_fd(rec->sock),
|
|
||||||
EVENT_FD_WRITE, messaging_send_handler, rec);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Send a message to a particular server
|
Send a message to a particular server
|
||||||
*/
|
*/
|
||||||
NTSTATUS messaging_send(struct messaging_context *msg, uint32_t server, uint32_t msg_type, DATA_BLOB *data)
|
NTSTATUS messaging_send(struct messaging_context *msg, uint32_t server,
|
||||||
|
uint32_t msg_type, DATA_BLOB *data)
|
||||||
{
|
{
|
||||||
struct messaging_rec *rec;
|
struct messaging_rec *rec;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
@ -292,45 +278,32 @@ NTSTATUS messaging_send(struct messaging_context *msg, uint32_t server, uint32_t
|
|||||||
return NT_STATUS_NO_MEMORY;
|
return NT_STATUS_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->msg = msg;
|
rec->msg = msg;
|
||||||
rec->header.version = MESSAGING_VERSION;
|
rec->header.version = MESSAGING_VERSION;
|
||||||
rec->header.msg_type = msg_type;
|
rec->header.msg_type = msg_type;
|
||||||
rec->header.from = msg->server_id;
|
rec->header.from = msg->server_id;
|
||||||
rec->header.to = server;
|
rec->header.to = server;
|
||||||
rec->header.length = data?data->length:0;
|
rec->header.length = data?data->length:0;
|
||||||
if (rec->header.length != 0) {
|
if (rec->header.length != 0) {
|
||||||
rec->data = data_blob_talloc(rec, data->data, data->length);
|
rec->data = data_blob_talloc(rec, data->data, data->length);
|
||||||
} else {
|
} else {
|
||||||
rec->data = data_blob(NULL, 0);
|
rec->data = data_blob(NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
status = socket_create("unix", SOCKET_TYPE_DGRAM, &rec->sock, 0);
|
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
|
||||||
talloc_free(rec);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
talloc_steal(rec, rec->sock);
|
|
||||||
|
|
||||||
rec->path = messaging_path(rec, server);
|
rec->path = messaging_path(rec, server);
|
||||||
|
|
||||||
status = try_connect(rec);
|
status = try_send(rec);
|
||||||
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
||||||
/* backoff on this message - the servers listen queue is full */
|
if (msg->pending == NULL) {
|
||||||
event_add_timed(msg->event.ev, rec,
|
EVENT_FD_WRITEABLE(msg->event.fde);
|
||||||
timeval_current_ofs(0, MESSAGING_BACKOFF),
|
}
|
||||||
messaging_backoff_handler, rec);
|
DLIST_ADD(msg->pending, rec);
|
||||||
return NT_STATUS_OK;
|
return NT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
talloc_free(rec);
|
||||||
talloc_free(rec);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_add_fd(msg->event.ev, rec, socket_get_fd(rec->sock),
|
return status;
|
||||||
EVENT_FD_WRITE, messaging_send_handler, rec);
|
|
||||||
|
|
||||||
return NT_STATUS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -381,6 +354,7 @@ struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx, uint32_t server_id
|
|||||||
msg->path = messaging_path(msg, server_id);
|
msg->path = messaging_path(msg, server_id);
|
||||||
msg->server_id = server_id;
|
msg->server_id = server_id;
|
||||||
msg->dispatch = NULL;
|
msg->dispatch = NULL;
|
||||||
|
msg->pending = NULL;
|
||||||
|
|
||||||
status = socket_create("unix", SOCKET_TYPE_DGRAM, &msg->sock, 0);
|
status = socket_create("unix", SOCKET_TYPE_DGRAM, &msg->sock, 0);
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
@ -399,9 +373,12 @@ struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx, uint32_t server_id
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* it needs to be non blocking for sends */
|
||||||
|
set_blocking(socket_get_fd(msg->sock), False);
|
||||||
|
|
||||||
msg->event.ev = talloc_reference(msg, ev);
|
msg->event.ev = talloc_reference(msg, ev);
|
||||||
msg->event.fde = event_add_fd(ev, msg, socket_get_fd(msg->sock),
|
msg->event.fde = event_add_fd(ev, msg, socket_get_fd(msg->sock),
|
||||||
EVENT_FD_READ, messaging_recv_handler, msg);
|
EVENT_FD_READ, messaging_handler, msg);
|
||||||
|
|
||||||
talloc_set_destructor(msg, messaging_destructor);
|
talloc_set_destructor(msg, messaging_destructor);
|
||||||
|
|
||||||
@ -409,5 +386,3 @@ struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx, uint32_t server_id
|
|||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user