Print this page
7267 SMF is fast and loose with optional dependencies
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Albert Lee <trisk@omniti.com>

@@ -20,10 +20,11 @@
  */
 
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2015, Syneto S.R.L. All rights reserved.
+ * Copyright 2016 RackTop Systems.
  */
 
 /*
  * graph.c - master restarter graph engine
  *

@@ -205,11 +206,10 @@
         (state) == RESTARTER_STATE_DEGRADED || \
         (state) == RESTARTER_STATE_OFFLINE)
 
 #define is_depgrp_bypassed(v) ((v->gv_type == GVT_GROUP) && \
         ((v->gv_depgroup == DEPGRP_EXCLUDE_ALL) || \
-        (v->gv_depgroup == DEPGRP_OPTIONAL_ALL) || \
         (v->gv_restart < RERR_RESTART)))
 
 static uu_list_pool_t *graph_edge_pool, *graph_vertex_pool;
 static uu_list_t *dgraph;
 static pthread_mutex_t dgraph_lock;

@@ -1326,21 +1326,17 @@
             edge = uu_list_next(groupv->gv_dependencies, edge)) {
                 v = edge->ge_vertex;
 
                 switch (v->gv_type) {
                 case GVT_INST:
-                        /* Skip missing or disabled instances */
-                        if ((v->gv_flags & (GV_CONFIGURED | GV_ENABLED)) !=
-                            (GV_CONFIGURED | GV_ENABLED))
+                        /* Skip missing instances */
+                        if ((v->gv_flags & GV_CONFIGURED) == 0)
                                 continue;
 
                         if (v->gv_state == RESTARTER_STATE_MAINT)
                                 continue;
 
-                        if (v->gv_flags & GV_TOOFFLINE)
-                                continue;
-
                         any_qualified = B_TRUE;
                         if (v->gv_state == RESTARTER_STATE_OFFLINE) {
                                 /*
                                  * For offline dependencies, treat unsatisfiable
                                  * as satisfied.

@@ -1348,15 +1344,15 @@
                                 i = dependency_satisfied(v, B_TRUE);
                                 if (i == -1)
                                         i = 1;
                         } else if (v->gv_state == RESTARTER_STATE_DISABLED) {
                                 /*
-                                 * The service is enabled, but hasn't
-                                 * transitioned out of disabled yet.  Treat it
-                                 * as unsatisfied (not unsatisfiable).
+                                 * If the instance is transitioning out of
+                                 * disabled the dependency is temporarily
+                                 * unsatisfied (not unsatisfiable).
                                  */
-                                i = 0;
+                                i = v->gv_flags & GV_ENABLED ? 0 : 1;
                         } else {
                                 i = dependency_satisfied(v, satbility);
                         }
                         break;
 

@@ -1365,72 +1361,13 @@
                         i = dependency_satisfied(v, satbility);
 
                         break;
 
                 case GVT_SVC: {
-                        boolean_t svc_any_qualified;
-                        boolean_t svc_satisfied;
-                        boolean_t svc_satisfiable;
-                        graph_vertex_t *v2;
-                        graph_edge_t *e2;
-
-                        svc_any_qualified = B_FALSE;
-                        svc_satisfied = B_FALSE;
-                        svc_satisfiable = B_FALSE;
-
-                        for (e2 = uu_list_first(v->gv_dependencies);
-                            e2 != NULL;
-                            e2 = uu_list_next(v->gv_dependencies, e2)) {
-                                v2 = e2->ge_vertex;
-                                assert(v2->gv_type == GVT_INST);
-
-                                if ((v2->gv_flags &
-                                    (GV_CONFIGURED | GV_ENABLED)) !=
-                                    (GV_CONFIGURED | GV_ENABLED))
-                                        continue;
-
-                                if (v2->gv_state == RESTARTER_STATE_MAINT)
-                                        continue;
-
-                                if (v2->gv_flags & GV_TOOFFLINE)
-                                        continue;
-
-                                svc_any_qualified = B_TRUE;
-
-                                if (v2->gv_state == RESTARTER_STATE_OFFLINE) {
-                                        /*
-                                         * For offline dependencies, treat
-                                         * unsatisfiable as satisfied.
-                                         */
-                                        i = dependency_satisfied(v2, B_TRUE);
-                                        if (i == -1)
-                                                i = 1;
-                                } else if (v2->gv_state ==
-                                    RESTARTER_STATE_DISABLED) {
-                                        i = 0;
-                                } else {
-                                        i = dependency_satisfied(v2, satbility);
-                                }
-
-                                if (i == 1) {
-                                        svc_satisfied = B_TRUE;
-                                        break;
-                                }
-                                if (i == 0)
-                                        svc_satisfiable = B_TRUE;
-                        }
-
-                        if (!svc_any_qualified)
-                                continue;
                         any_qualified = B_TRUE;
-                        if (svc_satisfied) {
-                                i = 1;
-                        } else if (svc_satisfiable) {
-                                i = 0;
-                        } else {
-                                i = -1;
-                        }
+                        i = optional_all_satisfied(v, satbility);
+
                         break;
                 }
 
                 case GVT_GROUP:
                 default:

@@ -1606,15 +1543,18 @@
                         }
                         return (-1);
                 }
 
                 /*
-                 * Any vertex with the GV_TOOFFLINE flag set is guaranteed
-                 * to have its dependencies unsatisfiable.
+                 * Any vertex with the GV_TODISABLE flag set is guaranteed
+                 * to have its dependencies unsatisfiable.  Any vertex with
+                 * GV_TOOFFLINE may be satisfied after it transitions.
                  */
-                if (v->gv_flags & GV_TOOFFLINE)
+                if (v->gv_flags & GV_TODISABLE)
                         return (-1);
+                if (v->gv_flags & GV_TOOFFLINE)
+                        return (0);
 
                 switch (v->gv_state) {
                 case RESTARTER_STATE_ONLINE:
                 case RESTARTER_STATE_DEGRADED:
                         return (1);

@@ -1726,10 +1666,13 @@
  */
 /*ARGSUSED*/
 static int
 satbility_cb(graph_vertex_t *v, void *arg)
 {
+        if (v->gv_flags & GV_TOOFFLINE)
+                return (UU_WALK_NEXT);
+
         if (v->gv_type == GVT_INST)
                 graph_start_if_satisfied(v);
 
         return (UU_WALK_NEXT);
 }

@@ -1740,29 +1683,51 @@
         graph_walk(v, WALK_DEPENDENTS, satbility_cb, NULL, NULL);
 }
 
 static void propagate_stop(graph_vertex_t *, void *);
 
-/* ARGSUSED */
+/*
+ * propagate_start()
+ *
+ * This function is used to propagate a start event to the dependents of the
+ * given vertex.  Any dependents that are offline but have their dependencies
+ * satisfied are started.  Any dependents that are online and have restart_on
+ * set to "restart" or "refresh" are restarted because their dependencies have
+ * just changed.  This only happens with optional_all dependencies.
+ */
 static void
 propagate_start(graph_vertex_t *v, void *arg)
 {
+        restarter_error_t err = (restarter_error_t)arg;
+
+        if (v->gv_flags & GV_TOOFFLINE)
+                return;
+
         switch (v->gv_type) {
         case GVT_INST:
+                /* Restarter */
+                if (inst_running(v)) {
+                        if (err == RERR_RESTART || err == RERR_REFRESH) {
+                                vertex_send_event(v,
+                                    RESTARTER_EVENT_TYPE_STOP_RESET);
+                        }
+                } else {
                 graph_start_if_satisfied(v);
+                }
                 break;
 
         case GVT_GROUP:
                 if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
                         graph_walk_dependents(v, propagate_stop,
                             (void *)RERR_RESTART);
                         break;
                 }
+                err = v->gv_restart;
                 /* FALLTHROUGH */
 
         case GVT_SVC:
-                graph_walk_dependents(v, propagate_start, NULL);
+                graph_walk_dependents(v, propagate_start, (void *)err);
                 break;
 
         case GVT_FILE:
 #ifndef NDEBUG
                 uu_warn("%s:%d: propagate_start() encountered GVT_FILE.\n",

@@ -1778,17 +1743,27 @@
 #endif
                 abort();
         }
 }
 
+/*
+ * propagate_stop()
+ *
+ * This function is used to propagate a stop event to the dependents of the
+ * given vertex.  Any dependents that are online (or in degraded state) with
+ * the restart_on property set to "restart" or "refresh" will be stopped as
+ * their dependencies have just changed, propagate_start() will start them
+ * again once their dependencies have been re-satisfied.
+ */
 static void
 propagate_stop(graph_vertex_t *v, void *arg)
 {
-        graph_edge_t *e;
-        graph_vertex_t *svc;
         restarter_error_t err = (restarter_error_t)arg;
 
+        if (v->gv_flags & GV_TOOFFLINE)
+                return;
+
         switch (v->gv_type) {
         case GVT_INST:
                 /* Restarter */
                 if (err > RERR_NONE && inst_running(v)) {
                         if (err == RERR_RESTART || err == RERR_REFRESH) {

@@ -1819,23 +1794,11 @@
                 }
 
                 if (err == RERR_NONE || err > v->gv_restart)
                         break;
 
-                assert(uu_list_numnodes(v->gv_dependents) == 1);
-                e = uu_list_first(v->gv_dependents);
-                svc = e->ge_vertex;
-
-                if (inst_running(svc)) {
-                        if (err == RERR_RESTART || err == RERR_REFRESH) {
-                                vertex_send_event(svc,
-                                    RESTARTER_EVENT_TYPE_STOP_RESET);
-                        } else {
-                                vertex_send_event(svc,
-                                    RESTARTER_EVENT_TYPE_STOP);
-                        }
-                }
+                graph_walk_dependents(v, propagate_stop, arg);
                 break;
 
         default:
 #ifndef NDEBUG
                 uu_warn("%s:%d: Unknown vertex type %d.\n", __FILE__, __LINE__,

@@ -4396,13 +4359,13 @@
                         if ((vv->gv_state == RESTARTER_STATE_ONLINE) ||
                             (vv->gv_state == RESTARTER_STATE_DEGRADED))
                                 return (B_FALSE);
                 } else {
                         /*
-                         * Skip all excluded and optional_all dependencies
-                         * and decide whether to offline the service based
-                         * on restart_on attribute.
+                         * Skip all excluded dependents and decide whether
+                         * to offline the service based on the restart_on
+                         * on attribute.
                          */
                         if (is_depgrp_bypassed(vv))
                                 continue;
 
                         /*

@@ -4963,11 +4926,11 @@
     restarter_error_t rerr)
 {
         if (type == PROPAGATE_STOP) {
                 graph_walk_dependents(v, propagate_stop, (void *)rerr);
         } else if (type == PROPAGATE_START || type == PROPAGATE_SAT) {
-                graph_walk_dependents(v, propagate_start, NULL);
+                graph_walk_dependents(v, propagate_start, (void *)RERR_NONE);
 
                 if (type == PROPAGATE_SAT)
                         propagate_satbility(v);
         } else {
 #ifndef NDEBUG

@@ -5034,11 +4997,11 @@
         graph_walk_dependents(v, propagate_stop, (void *)RERR_RESTART);
 
         v->gv_flags &= ~GV_CONFIGURED;
         v->gv_flags &= ~GV_DEATHROW;
 
-        graph_walk_dependents(v, propagate_start, NULL);
+        graph_walk_dependents(v, propagate_start, (void *)RERR_NONE);
         propagate_satbility(v);
 
         /*
          * If there are no (non-service) dependents, the vertex can be
          * completely removed.

@@ -5459,12 +5422,12 @@
                 v->gv_flags |= GV_TOOFFLINE;
                 log_framework(LOG_DEBUG, "%s added to subtree\n", v->gv_name);
                 break;
         case GVT_GROUP:
                 /*
-                 * Skip all excluded and optional_all dependencies and decide
-                 * whether to offline the service based on restart_on attribute.
+                 * Skip all excluded dependents and decide whether to offline
+                 * the service based on the restart_on attribute.
                  */
                 if (is_depgrp_bypassed(v))
                         return (UU_WALK_NEXT);
                 break;
         }