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>
Reviewed by: Gordon Ross <gordon.w.ross@gmail.com>

@@ -21,10 +21,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 Toomas Soome <tsoome@me.com>
+ * Copyright 2016 RackTop Systems.
  */
 
 /*
  * graph.c - master restarter graph engine
  *

@@ -206,13 +207,16 @@
         (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)))
 
+#define is_inst_bypassed(v) ((v->gv_type == GVT_INST) && \
+        ((v->gv_flags & GV_TODISABLE) || \
+        (v->gv_flags & GV_TOOFFLINE)))
+
 static uu_list_pool_t *graph_edge_pool, *graph_vertex_pool;
 static uu_list_t *dgraph;
 static pthread_mutex_t dgraph_lock;
 
 /*

@@ -1296,11 +1300,11 @@
 
                 if (satbility && s == 0)
                         satisfiable = B_TRUE;
         }
 
-        return (!satbility || satisfiable ? 0 : -1);
+        return ((!satbility || satisfiable) ? 0 : -1);
 }
 
 /*
  * An optional_all dependency only considers elements which are configured,
  * enabled, and not in maintenance.  If any are unsatisfied, then the dependency

@@ -1327,37 +1331,27 @@
             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) {
+                        if (v->gv_state == RESTARTER_STATE_OFFLINE ||
+                            v->gv_state == RESTARTER_STATE_DISABLED) {
                                 /*
-                                 * For offline dependencies, treat unsatisfiable
-                                 * as satisfied.
+                                 * For offline/disabled dependencies,
+                                 * treat unsatisfiable as satisfied.
                                  */
                                 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).
-                                 */
-                                i = 0;
                         } else {
                                 i = dependency_satisfied(v, satbility);
                         }
                         break;
 

@@ -1366,75 +1360,16 @@
                         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;
+                        any_qualified = B_TRUE;
+                        i = optional_all_satisfied(v, satbility);
 
-                        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;
-                        }
-                        break;
-                }
-
                 case GVT_GROUP:
                 default:
 #ifndef NDEBUG
                         uu_warn("%s:%d: Unexpected vertex type %d.\n", __FILE__,
                             __LINE__, v->gv_type);

@@ -1607,28 +1542,39 @@
                         }
                         return (-1);
                 }
 
                 /*
-                 * Any vertex with the GV_TOOFFLINE flag set is guaranteed
-                 * to have its dependencies unsatisfiable.
+                 * Vertices may be transitioning so we try to figure out if
+                 * the end state is likely to satisfy the dependency instead
+                 * of assuming the dependency is unsatisfied/unsatisfiable.
+                 *
+                 * Support for optional_all dependencies depends on us getting
+                 * this right because unsatisfiable dependencies are treated
+                 * as being satisfied.
                  */
-                if (v->gv_flags & GV_TOOFFLINE)
-                        return (-1);
-
                 switch (v->gv_state) {
                 case RESTARTER_STATE_ONLINE:
                 case RESTARTER_STATE_DEGRADED:
+                        if (v->gv_flags & GV_TODISABLE)
+                                return (-1);
+                        if (v->gv_flags & GV_TOOFFLINE)
+                                return (0);
                         return (1);
 
                 case RESTARTER_STATE_OFFLINE:
-                        if (!satbility)
-                                return (0);
+                        if (!satbility || v->gv_flags & GV_TODISABLE)
+                                return (satbility ? -1 : 0);
                         return (instance_satisfied(v, satbility) != -1 ?
                             0 : -1);
 
                 case RESTARTER_STATE_DISABLED:
+                        if (!satbility || !(v->gv_flags & GV_ENABLED))
+                                return (satbility ? -1 : 0);
+                        return (instance_satisfied(v, satbility) != -1 ?
+                            0 : -1);
+
                 case RESTARTER_STATE_MAINT:
                         return (-1);
 
                 case RESTARTER_STATE_UNINIT:
                         return (0);

@@ -1727,10 +1673,13 @@
  */
 /*ARGSUSED*/
 static int
 satbility_cb(graph_vertex_t *v, void *arg)
 {
+        if (is_inst_bypassed(v))
+                return (UU_WALK_NEXT);
+
         if (v->gv_type == GVT_INST)
                 graph_start_if_satisfied(v);
 
         return (UU_WALK_NEXT);
 }

@@ -1741,29 +1690,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 (is_inst_bypassed(v))
+                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",

@@ -1779,17 +1750,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 (is_inst_bypassed(v))
+                return;
+
         switch (v->gv_type) {
         case GVT_INST:
                 /* Restarter */
                 if (err > RERR_NONE && inst_running(v)) {
                         if (err == RERR_RESTART || err == RERR_REFRESH) {

@@ -1813,30 +1794,19 @@
                 abort();
                 /* NOTREACHED */
 
         case GVT_GROUP:
                 if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
-                        graph_walk_dependents(v, propagate_start, NULL);
+                        graph_walk_dependents(v, propagate_start,
+                            (void *)RERR_NONE);
                         break;
                 }
 
                 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__,

@@ -4391,13 +4361,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
+                         * attribute.
                          */
                         if (is_depgrp_bypassed(vv))
                                 continue;
 
                         /*

@@ -4958,11 +4928,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

@@ -5029,11 +4999,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.

@@ -5445,21 +5415,21 @@
         if (v->gv_flags & GV_TOOFFLINE)
                 return (UU_WALK_NEXT);
 
         switch (v->gv_type) {
         case GVT_INST:
-                /* If the instance is already disabled, skip it. */
-                if (!(v->gv_flags & GV_ENABLED))
+                /* If the instance is already offline, skip it. */
+                if (!inst_running(v))
                         return (UU_WALK_NEXT);
 
                 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;
         }