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>

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/svc/startd/graph.c
          +++ new/usr/src/cmd/svc/startd/graph.c
↓ open down ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright (c) 2015, Syneto S.R.L. All rights reserved.
       25 + * Copyright 2016 RackTop Systems.
  25   26   */
  26   27  
  27   28  /*
  28   29   * graph.c - master restarter graph engine
  29   30   *
  30   31   *   The graph engine keeps a dependency graph of all service instances on the
  31   32   *   system, as recorded in the repository.  It decides when services should
  32   33   *   be brought up or down based on service states and dependencies and sends
  33   34   *   commands to restarters to effect any changes.  It also executes
  34   35   *   administrator commands sent by svcadm via the repository.
↓ open down ↓ 165 lines elided ↑ open up ↑
 200  201  /*
 201  202   * Services in these states are not considered 'down' by the
 202  203   * milestone/shutdown code.
 203  204   */
 204  205  #define up_state(state) ((state) == RESTARTER_STATE_ONLINE || \
 205  206          (state) == RESTARTER_STATE_DEGRADED || \
 206  207          (state) == RESTARTER_STATE_OFFLINE)
 207  208  
 208  209  #define is_depgrp_bypassed(v) ((v->gv_type == GVT_GROUP) && \
 209  210          ((v->gv_depgroup == DEPGRP_EXCLUDE_ALL) || \
 210      -        (v->gv_depgroup == DEPGRP_OPTIONAL_ALL) || \
 211  211          (v->gv_restart < RERR_RESTART)))
 212  212  
 213  213  static uu_list_pool_t *graph_edge_pool, *graph_vertex_pool;
 214  214  static uu_list_t *dgraph;
 215  215  static pthread_mutex_t dgraph_lock;
 216  216  
 217  217  /*
 218  218   * milestone indicates the current subgraph.  When NULL, it is the entire
 219  219   * graph.  When MILESTONE_NONE, it is the empty graph.  Otherwise, it is all
 220  220   * services on which the target vertex depends.
↓ open down ↓ 1100 lines elided ↑ open up ↑
1321 1321          any_qualified = B_FALSE;
1322 1322          any_unsatisfied = B_FALSE;
1323 1323  
1324 1324          for (edge = uu_list_first(groupv->gv_dependencies);
1325 1325              edge != NULL;
1326 1326              edge = uu_list_next(groupv->gv_dependencies, edge)) {
1327 1327                  v = edge->ge_vertex;
1328 1328  
1329 1329                  switch (v->gv_type) {
1330 1330                  case GVT_INST:
1331      -                        /* Skip missing or disabled instances */
1332      -                        if ((v->gv_flags & (GV_CONFIGURED | GV_ENABLED)) !=
1333      -                            (GV_CONFIGURED | GV_ENABLED))
     1331 +                        /* Skip missing instances */
     1332 +                        if ((v->gv_flags & GV_CONFIGURED) == 0)
1334 1333                                  continue;
1335 1334  
1336 1335                          if (v->gv_state == RESTARTER_STATE_MAINT)
1337 1336                                  continue;
1338 1337  
1339      -                        if (v->gv_flags & GV_TOOFFLINE)
1340      -                                continue;
1341      -
1342 1338                          any_qualified = B_TRUE;
1343 1339                          if (v->gv_state == RESTARTER_STATE_OFFLINE) {
1344 1340                                  /*
1345 1341                                   * For offline dependencies, treat unsatisfiable
1346 1342                                   * as satisfied.
1347 1343                                   */
1348 1344                                  i = dependency_satisfied(v, B_TRUE);
1349 1345                                  if (i == -1)
1350 1346                                          i = 1;
1351 1347                          } else if (v->gv_state == RESTARTER_STATE_DISABLED) {
1352 1348                                  /*
1353      -                                 * The service is enabled, but hasn't
1354      -                                 * transitioned out of disabled yet.  Treat it
1355      -                                 * as unsatisfied (not unsatisfiable).
     1349 +                                 * If the instance is transitioning out of
     1350 +                                 * disabled the dependency is temporarily
     1351 +                                 * unsatisfied (not unsatisfiable).
1356 1352                                   */
1357      -                                i = 0;
     1353 +                                i = v->gv_flags & GV_ENABLED ? 0 : 1;
1358 1354                          } else {
1359 1355                                  i = dependency_satisfied(v, satbility);
1360 1356                          }
1361 1357                          break;
1362 1358  
1363 1359                  case GVT_FILE:
1364 1360                          any_qualified = B_TRUE;
1365 1361                          i = dependency_satisfied(v, satbility);
1366 1362  
1367 1363                          break;
1368 1364  
1369 1365                  case GVT_SVC: {
1370      -                        boolean_t svc_any_qualified;
1371      -                        boolean_t svc_satisfied;
1372      -                        boolean_t svc_satisfiable;
1373      -                        graph_vertex_t *v2;
1374      -                        graph_edge_t *e2;
1375      -
1376      -                        svc_any_qualified = B_FALSE;
1377      -                        svc_satisfied = B_FALSE;
1378      -                        svc_satisfiable = B_FALSE;
1379      -
1380      -                        for (e2 = uu_list_first(v->gv_dependencies);
1381      -                            e2 != NULL;
1382      -                            e2 = uu_list_next(v->gv_dependencies, e2)) {
1383      -                                v2 = e2->ge_vertex;
1384      -                                assert(v2->gv_type == GVT_INST);
1385      -
1386      -                                if ((v2->gv_flags &
1387      -                                    (GV_CONFIGURED | GV_ENABLED)) !=
1388      -                                    (GV_CONFIGURED | GV_ENABLED))
1389      -                                        continue;
1390      -
1391      -                                if (v2->gv_state == RESTARTER_STATE_MAINT)
1392      -                                        continue;
1393      -
1394      -                                if (v2->gv_flags & GV_TOOFFLINE)
1395      -                                        continue;
1396      -
1397      -                                svc_any_qualified = B_TRUE;
1398      -
1399      -                                if (v2->gv_state == RESTARTER_STATE_OFFLINE) {
1400      -                                        /*
1401      -                                         * For offline dependencies, treat
1402      -                                         * unsatisfiable as satisfied.
1403      -                                         */
1404      -                                        i = dependency_satisfied(v2, B_TRUE);
1405      -                                        if (i == -1)
1406      -                                                i = 1;
1407      -                                } else if (v2->gv_state ==
1408      -                                    RESTARTER_STATE_DISABLED) {
1409      -                                        i = 0;
1410      -                                } else {
1411      -                                        i = dependency_satisfied(v2, satbility);
1412      -                                }
1413      -
1414      -                                if (i == 1) {
1415      -                                        svc_satisfied = B_TRUE;
1416      -                                        break;
1417      -                                }
1418      -                                if (i == 0)
1419      -                                        svc_satisfiable = B_TRUE;
1420      -                        }
1421      -
1422      -                        if (!svc_any_qualified)
1423      -                                continue;
1424 1366                          any_qualified = B_TRUE;
1425      -                        if (svc_satisfied) {
1426      -                                i = 1;
1427      -                        } else if (svc_satisfiable) {
1428      -                                i = 0;
1429      -                        } else {
1430      -                                i = -1;
1431      -                        }
     1367 +                        i = optional_all_satisfied(v, satbility);
     1368 +
1432 1369                          break;
1433 1370                  }
1434 1371  
1435 1372                  case GVT_GROUP:
1436 1373                  default:
1437 1374  #ifndef NDEBUG
1438 1375                          uu_warn("%s:%d: Unexpected vertex type %d.\n", __FILE__,
1439 1376                              __LINE__, v->gv_type);
1440 1377  #endif
1441 1378                          abort();
↓ open down ↓ 159 lines elided ↑ open up ↑
1601 1538                                  /*
1602 1539                                   * A dependency on an instance with GV_DEATHROW
1603 1540                                   * flag is always considered as satisfied.
1604 1541                                   */
1605 1542                                  return (1);
1606 1543                          }
1607 1544                          return (-1);
1608 1545                  }
1609 1546  
1610 1547                  /*
1611      -                 * Any vertex with the GV_TOOFFLINE flag set is guaranteed
1612      -                 * to have its dependencies unsatisfiable.
     1548 +                 * Any vertex with the GV_TODISABLE flag set is guaranteed
     1549 +                 * to have its dependencies unsatisfiable.  Any vertex with
     1550 +                 * GV_TOOFFLINE may be satisfied after it transitions.
1613 1551                   */
1614      -                if (v->gv_flags & GV_TOOFFLINE)
     1552 +                if (v->gv_flags & GV_TODISABLE)
1615 1553                          return (-1);
     1554 +                if (v->gv_flags & GV_TOOFFLINE)
     1555 +                        return (0);
1616 1556  
1617 1557                  switch (v->gv_state) {
1618 1558                  case RESTARTER_STATE_ONLINE:
1619 1559                  case RESTARTER_STATE_DEGRADED:
1620 1560                          return (1);
1621 1561  
1622 1562                  case RESTARTER_STATE_OFFLINE:
1623 1563                          if (!satbility)
1624 1564                                  return (0);
1625 1565                          return (instance_satisfied(v, satbility) != -1 ?
↓ open down ↓ 95 lines elided ↑ open up ↑
1721 1661   * an optional_all dependency.  It's not possible for an optional_all dependency
1722 1662   * to change satisfiability without also coming online, in which case we get a
1723 1663   * start event and propagation continues naturally.  However, it does no harm to
1724 1664   * continue propagating satisfiability (as it is a relatively rare event), and
1725 1665   * keeps the walker code simple and generic.
1726 1666   */
1727 1667  /*ARGSUSED*/
1728 1668  static int
1729 1669  satbility_cb(graph_vertex_t *v, void *arg)
1730 1670  {
     1671 +        if (v->gv_flags & GV_TOOFFLINE)
     1672 +                return (UU_WALK_NEXT);
     1673 +
1731 1674          if (v->gv_type == GVT_INST)
1732 1675                  graph_start_if_satisfied(v);
1733 1676  
1734 1677          return (UU_WALK_NEXT);
1735 1678  }
1736 1679  
1737 1680  static void
1738 1681  propagate_satbility(graph_vertex_t *v)
1739 1682  {
1740 1683          graph_walk(v, WALK_DEPENDENTS, satbility_cb, NULL, NULL);
1741 1684  }
1742 1685  
1743 1686  static void propagate_stop(graph_vertex_t *, void *);
1744 1687  
1745      -/* ARGSUSED */
     1688 +/*
     1689 + * propagate_start()
     1690 + *
     1691 + * This function is used to propagate a start event to the dependents of the
     1692 + * given vertex.  Any dependents that are offline but have their dependencies
     1693 + * satisfied are started.  Any dependents that are online and have restart_on
     1694 + * set to "restart" or "refresh" are restarted because their dependencies have
     1695 + * just changed.  This only happens with optional_all dependencies.
     1696 + */
1746 1697  static void
1747 1698  propagate_start(graph_vertex_t *v, void *arg)
1748 1699  {
     1700 +        restarter_error_t err = (restarter_error_t)arg;
     1701 +
     1702 +        if (v->gv_flags & GV_TOOFFLINE)
     1703 +                return;
     1704 +
1749 1705          switch (v->gv_type) {
1750 1706          case GVT_INST:
1751      -                graph_start_if_satisfied(v);
     1707 +                /* Restarter */
     1708 +                if (inst_running(v)) {
     1709 +                        if (err == RERR_RESTART || err == RERR_REFRESH) {
     1710 +                                vertex_send_event(v,
     1711 +                                    RESTARTER_EVENT_TYPE_STOP_RESET);
     1712 +                        }
     1713 +                } else {
     1714 +                        graph_start_if_satisfied(v);
     1715 +                }
1752 1716                  break;
1753 1717  
1754 1718          case GVT_GROUP:
1755 1719                  if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
1756 1720                          graph_walk_dependents(v, propagate_stop,
1757 1721                              (void *)RERR_RESTART);
1758 1722                          break;
1759 1723                  }
     1724 +                err = v->gv_restart;
1760 1725                  /* FALLTHROUGH */
1761 1726  
1762 1727          case GVT_SVC:
1763      -                graph_walk_dependents(v, propagate_start, NULL);
     1728 +                graph_walk_dependents(v, propagate_start, (void *)err);
1764 1729                  break;
1765 1730  
1766 1731          case GVT_FILE:
1767 1732  #ifndef NDEBUG
1768 1733                  uu_warn("%s:%d: propagate_start() encountered GVT_FILE.\n",
1769 1734                      __FILE__, __LINE__);
1770 1735  #endif
1771 1736                  abort();
1772 1737                  /* NOTREACHED */
1773 1738  
1774 1739          default:
1775 1740  #ifndef NDEBUG
1776 1741                  uu_warn("%s:%d: Unknown vertex type %d.\n", __FILE__, __LINE__,
1777 1742                      v->gv_type);
1778 1743  #endif
1779 1744                  abort();
1780 1745          }
1781 1746  }
1782 1747  
     1748 +/*
     1749 + * propagate_stop()
     1750 + *
     1751 + * This function is used to propagate a stop event to the dependents of the
     1752 + * given vertex.  Any dependents that are online (or in degraded state) with
     1753 + * the restart_on property set to "restart" or "refresh" will be stopped as
     1754 + * their dependencies have just changed, propagate_start() will start them
     1755 + * again once their dependencies have been re-satisfied.
     1756 + */
1783 1757  static void
1784 1758  propagate_stop(graph_vertex_t *v, void *arg)
1785 1759  {
1786      -        graph_edge_t *e;
1787      -        graph_vertex_t *svc;
1788 1760          restarter_error_t err = (restarter_error_t)arg;
1789 1761  
     1762 +        if (v->gv_flags & GV_TOOFFLINE)
     1763 +                return;
     1764 +
1790 1765          switch (v->gv_type) {
1791 1766          case GVT_INST:
1792 1767                  /* Restarter */
1793 1768                  if (err > RERR_NONE && inst_running(v)) {
1794 1769                          if (err == RERR_RESTART || err == RERR_REFRESH) {
1795 1770                                  vertex_send_event(v,
1796 1771                                      RESTARTER_EVENT_TYPE_STOP_RESET);
1797 1772                          } else {
1798 1773                                  vertex_send_event(v, RESTARTER_EVENT_TYPE_STOP);
1799 1774                          }
↓ open down ↓ 14 lines elided ↑ open up ↑
1814 1789  
1815 1790          case GVT_GROUP:
1816 1791                  if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
1817 1792                          graph_walk_dependents(v, propagate_start, NULL);
1818 1793                          break;
1819 1794                  }
1820 1795  
1821 1796                  if (err == RERR_NONE || err > v->gv_restart)
1822 1797                          break;
1823 1798  
1824      -                assert(uu_list_numnodes(v->gv_dependents) == 1);
1825      -                e = uu_list_first(v->gv_dependents);
1826      -                svc = e->ge_vertex;
1827      -
1828      -                if (inst_running(svc)) {
1829      -                        if (err == RERR_RESTART || err == RERR_REFRESH) {
1830      -                                vertex_send_event(svc,
1831      -                                    RESTARTER_EVENT_TYPE_STOP_RESET);
1832      -                        } else {
1833      -                                vertex_send_event(svc,
1834      -                                    RESTARTER_EVENT_TYPE_STOP);
1835      -                        }
1836      -                }
     1799 +                graph_walk_dependents(v, propagate_stop, arg);
1837 1800                  break;
1838 1801  
1839 1802          default:
1840 1803  #ifndef NDEBUG
1841 1804                  uu_warn("%s:%d: Unknown vertex type %d.\n", __FILE__, __LINE__,
1842 1805                      v->gv_type);
1843 1806  #endif
1844 1807                  abort();
1845 1808          }
1846 1809  }
↓ open down ↓ 2544 lines elided ↑ open up ↑
4391 4354                                  continue;
4392 4355  
4393 4356                          if ((vv->gv_flags & GV_TOOFFLINE) == 0)
4394 4357                                  continue;
4395 4358  
4396 4359                          if ((vv->gv_state == RESTARTER_STATE_ONLINE) ||
4397 4360                              (vv->gv_state == RESTARTER_STATE_DEGRADED))
4398 4361                                  return (B_FALSE);
4399 4362                  } else {
4400 4363                          /*
4401      -                         * Skip all excluded and optional_all dependencies
4402      -                         * and decide whether to offline the service based
4403      -                         * on restart_on attribute.
     4364 +                         * Skip all excluded dependents and decide whether
     4365 +                         * to offline the service based on the restart_on
     4366 +                         * on attribute.
4404 4367                           */
4405 4368                          if (is_depgrp_bypassed(vv))
4406 4369                                  continue;
4407 4370  
4408 4371                          /*
4409 4372                           * For dependency groups or service vertices, keep
4410 4373                           * traversing to see if instances are running.
4411 4374                           */
4412 4375                          if (insubtree_dependents_down(vv) == B_FALSE)
4413 4376                                  return (B_FALSE);
↓ open down ↓ 544 lines elided ↑ open up ↑
4958 4921   * a state which can satisfy optional dependencies, like disabled or
4959 4922   * maintenance.
4960 4923   */
4961 4924  void
4962 4925  graph_transition_propagate(graph_vertex_t *v, propagate_event_t type,
4963 4926      restarter_error_t rerr)
4964 4927  {
4965 4928          if (type == PROPAGATE_STOP) {
4966 4929                  graph_walk_dependents(v, propagate_stop, (void *)rerr);
4967 4930          } else if (type == PROPAGATE_START || type == PROPAGATE_SAT) {
4968      -                graph_walk_dependents(v, propagate_start, NULL);
     4931 +                graph_walk_dependents(v, propagate_start, (void *)RERR_NONE);
4969 4932  
4970 4933                  if (type == PROPAGATE_SAT)
4971 4934                          propagate_satbility(v);
4972 4935          } else {
4973 4936  #ifndef NDEBUG
4974 4937                  uu_warn("%s:%d: Unexpected type value %d.\n",  __FILE__,
4975 4938                      __LINE__, type);
4976 4939  #endif
4977 4940                  abort();
4978 4941          }
↓ open down ↓ 50 lines elided ↑ open up ↑
5029 4992           * event -- deletion isn't a fault, just a normal stop.  This gives
5030 4993           * dependent services the chance to do a clean shutdown.  Then, mark
5031 4994           * the service as unconfigured and propagate the start event for the
5032 4995           * optional_all dependencies that might have become satisfied.
5033 4996           */
5034 4997          graph_walk_dependents(v, propagate_stop, (void *)RERR_RESTART);
5035 4998  
5036 4999          v->gv_flags &= ~GV_CONFIGURED;
5037 5000          v->gv_flags &= ~GV_DEATHROW;
5038 5001  
5039      -        graph_walk_dependents(v, propagate_start, NULL);
     5002 +        graph_walk_dependents(v, propagate_start, (void *)RERR_NONE);
5040 5003          propagate_satbility(v);
5041 5004  
5042 5005          /*
5043 5006           * If there are no (non-service) dependents, the vertex can be
5044 5007           * completely removed.
5045 5008           */
5046 5009          if (v != milestone && v->gv_refs == 0 &&
5047 5010              uu_list_numnodes(v->gv_dependents) == 1)
5048 5011                  remove_inst_vertex(v);
5049 5012  
↓ open down ↓ 404 lines elided ↑ open up ↑
5454 5417          case GVT_INST:
5455 5418                  /* If the instance is already disabled, skip it. */
5456 5419                  if (!(v->gv_flags & GV_ENABLED))
5457 5420                          return (UU_WALK_NEXT);
5458 5421  
5459 5422                  v->gv_flags |= GV_TOOFFLINE;
5460 5423                  log_framework(LOG_DEBUG, "%s added to subtree\n", v->gv_name);
5461 5424                  break;
5462 5425          case GVT_GROUP:
5463 5426                  /*
5464      -                 * Skip all excluded and optional_all dependencies and decide
5465      -                 * whether to offline the service based on restart_on attribute.
     5427 +                 * Skip all excluded dependents and decide whether to offline
     5428 +                 * the service based on the restart_on attribute.
5466 5429                   */
5467 5430                  if (is_depgrp_bypassed(v))
5468 5431                          return (UU_WALK_NEXT);
5469 5432                  break;
5470 5433          }
5471 5434  
5472 5435          r = uu_list_walk(v->gv_dependents, (uu_walk_fn_t *)mark_subtree, arg,
5473 5436              0);
5474 5437          assert(r == 0);
5475 5438          return (UU_WALK_NEXT);
↓ open down ↓ 1457 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX