@matheusbsbs / matheus-bagliano / commits / 3bd62c2176
Fork 0

git-daemon: rewrite kindergarden, new option --max-connections

Get rid of the fixed array of children and make max-connections dynamic and configurable. Fix the killing code to actually kill the newest connections from duplicate IP-addresses. Avoid forking if too busy already. Signed-off-by: Stephen R. van den Berg <srb@cuci.nl> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stephen R. van den Berg committed Aug 14, 2008 at 20:02 UTC 3bd62c21760f92996569bb9335b399a9545a5c41
2 files changed +82 -140
Documentation/git-daemon.txt
+7 -2
@@ -9,8 +9,9 @@ SYNOPSIS
9 --------
10 [verse]
11 'git daemon' [--verbose] [--syslog] [--export-all]
12 - [--timeout=n] [--init-timeout=n] [--strict-paths]
13 - [--base-path=path] [--user-path | --user-path=path]
12 + [--timeout=n] [--init-timeout=n] [--max-connections=n]
13 + [--strict-paths] [--base-path=path] [--base-path-relaxed]
14 + [--user-path | --user-path=path]
15 [--interpolated-path=pathtemplate]
16 [--reuseaddr] [--detach] [--pid-file=file]
17 [--enable=service] [--disable=service]
@@ -99,6 +100,10 @@ OPTIONS
100 it takes for the server to process the sub-request and time spent
101 waiting for next client's request.
102
103 +--max-connections::
104 + Maximum number of concurrent clients, defaults to 32. Set it to
105 + zero for no limit.
106 +
107 --syslog::
108 Log to syslog instead of stderr. Note that this option does not imply
109 --verbose, thus by default only error conditions will be logged.
daemon.c
+75 -138
@@ -19,8 +19,8 @@ static int reuseaddr;
19
20 static const char daemon_usage[] =
21 "git daemon [--verbose] [--syslog] [--export-all]\n"
22 -" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
23 -" [--base-path=path] [--base-path-relaxed]\n"
22 +" [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
23 +" [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
24 " [--user-path | --user-path=path]\n"
25 " [--interpolated-path=path]\n"
26 " [--reuseaddr] [--detach] [--pid-file=file]\n"
@@ -584,40 +584,35 @@ static int execute(struct sockaddr *addr)
584 return -1;
585 }
586
587 +static int max_connections = 32;
588
588 -/*
589 - * We count spawned/reaped separately, just to avoid any
590 - * races when updating them from signals. The SIGCHLD handler
591 - * will only update children_reaped, and the fork logic will
592 - * only update children_spawned.
593 - *
594 - * MAX_CHILDREN should be a power-of-two to make the modulus
595 - * operation cheap. It should also be at least twice
596 - * the maximum number of connections we will ever allow.
597 - */
598 -#define MAX_CHILDREN 128
599 -
600 -static int max_connections = 25;
601 -
602 -/* These are updated by the signal handler */
603 -static volatile unsigned int children_reaped;
604 -static pid_t dead_child[MAX_CHILDREN];
605 -
606 -/* These are updated by the main loop */
607 -static unsigned int children_spawned;
608 -static unsigned int children_deleted;
589 +static unsigned int live_children;
590
591 static struct child {
592 + struct child *next;
593 pid_t pid;
612 - int addrlen;
594 struct sockaddr_storage address;
614 -} live_child[MAX_CHILDREN];
595 +} *firstborn;
596
616 -static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
597 +static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
598 {
618 - live_child[idx].pid = pid;
619 - live_child[idx].addrlen = addrlen;
620 - memcpy(&live_child[idx].address, addr, addrlen);
599 + struct child *newborn;
600 + newborn = xcalloc(1, sizeof *newborn);
601 + if (newborn) {
602 + struct child **cradle;
603 +
604 + live_children++;
605 + newborn->pid = pid;
606 + memcpy(&newborn->address, addr, addrlen);
607 + for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
608 + if (!memcmp(&(*cradle)->address, &newborn->address,
609 + sizeof newborn->address))
610 + break;
611 + newborn->next = *cradle;
612 + *cradle = newborn;
613 + }
614 + else
615 + logerror("Out of memory spawning new child");
616 }
617
618 /*
@@ -626,142 +621,78 @@ static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
621 * We move everything up by one, since the new "deleted" will
622 * be one higher.
623 */
629 -static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
624 +static void remove_child(pid_t pid)
625 {
631 - struct child n;
626 + struct child **cradle, *blanket;
627
633 - deleted %= MAX_CHILDREN;
634 - spawned %= MAX_CHILDREN;
635 - if (live_child[deleted].pid == pid) {
636 - live_child[deleted].pid = -1;
637 - return;
638 - }
639 - n = live_child[deleted];
640 - for (;;) {
641 - struct child m;
642 - deleted = (deleted + 1) % MAX_CHILDREN;
643 - if (deleted == spawned)
644 - die("could not find dead child %d\n", pid);
645 - m = live_child[deleted];
646 - live_child[deleted] = n;
647 - if (m.pid == pid)
648 - return;
649 - n = m;
650 - }
628 + for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
629 + if (blanket->pid == pid) {
630 + *cradle = blanket->next;
631 + live_children--;
632 + free(blanket);
633 + break;
634 + }
635 }
636
637 /*
638 * This gets called if the number of connections grows
639 * past "max_connections".
640 *
657 - * We _should_ start off by searching for connections
658 - * from the same IP, and if there is some address wth
659 - * multiple connections, we should kill that first.
660 - *
661 - * As it is, we just "randomly" kill 25% of the connections,
662 - * and our pseudo-random generator sucks too. I have no
663 - * shame.
664 - *
665 - * Really, this is just a place-holder for a _real_ algorithm.
641 + * We kill the newest connection from a duplicate IP.
642 */
667 -static void kill_some_children(int signo, unsigned start, unsigned stop)
643 +static void kill_some_child(void)
644 {
669 - start %= MAX_CHILDREN;
670 - stop %= MAX_CHILDREN;
671 - while (start != stop) {
672 - if (!(start & 3))
673 - kill(live_child[start].pid, signo);
674 - start = (start + 1) % MAX_CHILDREN;
675 - }
676 -}
677 -
678 -static void check_dead_children(void)
679 -{
680 - unsigned spawned, reaped, deleted;
681 -
682 - for (;;) {
683 - int status;
684 - pid_t pid = waitpid(-1, &status, WNOHANG);
685 -
686 - if (pid > 0) {
687 - unsigned reaped = children_reaped;
688 - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
689 - pid = -pid;
690 - dead_child[reaped % MAX_CHILDREN] = pid;
691 - children_reaped = reaped + 1;
692 - continue;
693 - }
694 - break;
695 - }
696 -
697 - spawned = children_spawned;
698 - reaped = children_reaped;
699 - deleted = children_deleted;
700 -
701 - while (deleted < reaped) {
702 - pid_t pid = dead_child[deleted % MAX_CHILDREN];
703 - const char *dead = pid < 0 ? " (with error)" : "";
645 + const struct child *blanket;
646
705 - if (pid < 0)
706 - pid = -pid;
647 + if ((blanket = firstborn)) {
648 + const struct child *next;
649
708 - /* XXX: Custom logging, since we don't wanna getpid() */
709 - if (verbose) {
710 - if (log_syslog)
711 - syslog(LOG_INFO, "[%d] Disconnected%s",
712 - pid, dead);
713 - else
714 - fprintf(stderr, "[%d] Disconnected%s\n",
715 - pid, dead);
716 - }
717 - remove_child(pid, deleted, spawned);
718 - deleted++;
650 + for (; (next = blanket->next); blanket = next)
651 + if (!memcmp(&blanket->address, &next->address,
652 + sizeof next->address)) {
653 + kill(blanket->pid, SIGTERM);
654 + break;
655 + }
656 }
720 - children_deleted = deleted;
657 }
658
723 -static void check_max_connections(void)
659 +static void check_dead_children(void)
660 {
725 - for (;;) {
726 - int active;
727 - unsigned spawned, deleted;
728 -
729 - check_dead_children();
730 -
731 - spawned = children_spawned;
732 - deleted = children_deleted;
733 -
734 - active = spawned - deleted;
735 - if (active <= max_connections)
736 - break;
737 -
738 - /* Kill some unstarted connections with SIGTERM */
739 - kill_some_children(SIGTERM, deleted, spawned);
740 - if (active <= max_connections << 1)
741 - break;
661 + int status;
662 + pid_t pid;
663
743 - /* If the SIGTERM thing isn't helping use SIGKILL */
744 - kill_some_children(SIGKILL, deleted, spawned);
745 - sleep(1);
664 + while ((pid = waitpid(-1, &status, WNOHANG))>0) {
665 + const char *dead = "";
666 + remove_child(pid);
667 + if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
668 + dead = " (with error)";
669 + loginfo("[%d] Disconnected%s", (int)pid, dead);
670 }
671 }
672
673 static void handle(int incoming, struct sockaddr *addr, int addrlen)
674 {
751 - pid_t pid = fork();
675 + pid_t pid;
676
753 - if (pid) {
754 - unsigned idx;
677 + if (max_connections && live_children >= max_connections) {
678 + kill_some_child();
679 + sleep(1); /* give it some time to die */
680 + check_dead_children();
681 + if (live_children >= max_connections) {
682 + close(incoming);
683 + logerror("Too many children, dropping connection");
684 + return;
685 + }
686 + }
687
688 + if ((pid = fork())) {
689 close(incoming);
757 - if (pid < 0)
690 + if (pid < 0) {
691 + logerror("Couldn't fork %s", strerror(errno));
692 return;
693 + }
694
760 - idx = children_spawned % MAX_CHILDREN;
761 - children_spawned++;
762 - add_child(idx, pid, addr, addrlen);
763 -
764 - check_max_connections();
695 + add_child(pid, addr, addrlen);
696 return;
697 }
698
@@ -1077,6 +1008,12 @@ int main(int argc, char **argv)
1008 init_timeout = atoi(arg+15);
1009 continue;
1010 }
1011 + if (!prefixcmp(arg, "--max-connections=")) {
1012 + max_connections = atoi(arg+18);
1013 + if (max_connections < 0)
1014 + max_connections = 0; /* unlimited */
1015 + continue;
1016 + }
1017 if (!strcmp(arg, "--strict-paths")) {
1018 strict_paths = 1;
1019 continue;