X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-rtnl%2Fsd-rtnl.c;h=551e95b592d87eb48a3e34a21cb3ed80f13e29d4;hb=3dd215e056ee9ff23175eca66686ff9b7a566dbf;hp=e5610b43358d97058e340b8ae2808b0fc85268df;hpb=03e334a1c7dc8c20c38902aa039440763acc9b17;p=elogind.git diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index e5610b433..551e95b59 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -111,34 +111,98 @@ sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) { } sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) { + unsigned long refs; - if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) { + if (!rtnl) + return NULL; + + /* + * If our ref-cnt is exactly the number of internally queued messages + * plus the ref-cnt to be dropped, then we know there's no external + * reference to us. Hence, we look through all queued messages and if + * they also have no external references, we're about to drop the last + * ref. Flush the queues so the REFCNT_DEC() below will drop to 0. + * We must be careful not to introduce inter-message references or this + * logic will fall apart.. + */ + + refs = rtnl->rqueue_size + rtnl->wqueue_size + 1; + + if (REFCNT_GET(rtnl->n_ref) <= refs) { struct match_callback *f; + bool q = true; unsigned i; - for (i = 0; i < rtnl->rqueue_size; i++) - sd_rtnl_message_unref(rtnl->rqueue[i]); - free(rtnl->rqueue); + for (i = 0; i < rtnl->rqueue_size; i++) { + if (REFCNT_GET(rtnl->rqueue[i]->n_ref) > 1) { + q = false; + break; + } else if (rtnl->rqueue[i]->rtnl != rtnl) + --refs; + } + + if (q) { + for (i = 0; i < rtnl->wqueue_size; i++) { + if (REFCNT_GET(rtnl->wqueue[i]->n_ref) > 1) { + q = false; + break; + } else if (rtnl->wqueue[i]->rtnl != rtnl) + --refs; + } + } + + if (q && REFCNT_GET(rtnl->n_ref) == refs) { + /* Drop our own ref early to avoid recursion from: + * sd_rtnl_message_unref() + * sd_rtnl_unref() + * These must enter sd_rtnl_unref() with a ref-cnt + * smaller than us. */ + REFCNT_DEC(rtnl->n_ref); - for (i = 0; i < rtnl->wqueue_size; i++) - sd_rtnl_message_unref(rtnl->wqueue[i]); - free(rtnl->wqueue); + for (i = 0; i < rtnl->rqueue_size; i++) + sd_rtnl_message_unref(rtnl->rqueue[i]); + free(rtnl->rqueue); - hashmap_free_free(rtnl->reply_callbacks); - prioq_free(rtnl->reply_callbacks_prioq); + for (i = 0; i < rtnl->wqueue_size; i++) + sd_rtnl_message_unref(rtnl->wqueue[i]); + free(rtnl->wqueue); - while ((f = rtnl->match_callbacks)) { - LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f); - free(f); - } + assert_se(REFCNT_GET(rtnl->n_ref) == 0); - safe_close(rtnl->fd); - free(rtnl); + hashmap_free_free(rtnl->reply_callbacks); + prioq_free(rtnl->reply_callbacks_prioq); + + while ((f = rtnl->match_callbacks)) { + LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f); + free(f); + } + + safe_close(rtnl->fd); + free(rtnl); + + return NULL; + } } + assert_se(REFCNT_GET(rtnl->n_ref) > 0); + REFCNT_DEC(rtnl->n_ref); + return NULL; } +static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) { + assert(rtnl); + assert(!rtnl_pid_changed(rtnl)); + assert(m); + assert(m->hdr); + + m->hdr->nlmsg_seq = rtnl->serial++; + + rtnl_message_seal(m); + + return; +} + int sd_rtnl_send(sd_rtnl *nl, sd_rtnl_message *message, uint32_t *serial) { @@ -147,10 +211,9 @@ int sd_rtnl_send(sd_rtnl *nl, assert_return(nl, -EINVAL); assert_return(!rtnl_pid_changed(nl), -ECHILD); assert_return(message, -EINVAL); + assert_return(!message->sealed, -EPERM); - r = rtnl_message_seal(nl, message); - if (r < 0) - return r; + rtnl_seal_message(nl, message); if (nl->wqueue_size <= 0) { /* send directly */ @@ -203,10 +266,8 @@ static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) { /* Try to read a new message */ r = socket_read_message(rtnl, &z); - if (r < 0) + if (r <= 0) return r; - if (r == 0) - return 0; *message = z; @@ -787,7 +848,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) { if (r < 0) goto fail; - r = sd_event_add_monotonic(rtnl->event, &rtnl->time_event_source, 0, 0, time_callback, rtnl); + r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl); if (r < 0) goto fail;