Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
NetZoneImpl: simpler constructor
[simgrid.git] / src / kernel / routing / NetZoneImpl.cpp
1 /* Copyright (c) 2006-2021. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "simgrid/kernel/routing/NetZoneImpl.hpp"
7 #include "simgrid/kernel/routing/NetPoint.hpp"
8 #include "simgrid/s4u/Engine.hpp"
9 #include "simgrid/s4u/Host.hpp"
10 #include "src/kernel/EngineImpl.hpp"
11 #include "src/kernel/resource/DiskImpl.hpp"
12 #include "src/surf/HostImpl.hpp"
13 #include "src/surf/cpu_interface.hpp"
14 #include "src/surf/network_interface.hpp"
15 #include "src/surf/xml/platf_private.hpp"
16 #include "surf/surf.hpp"
17
18 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(surf_route);
19
20 namespace simgrid {
21 namespace kernel {
22 namespace routing {
23
24 NetZoneImpl::NetZoneImpl(const std::string& name) : piface_(this), name_(name)
25 {
26   xbt_assert(nullptr == s4u::Engine::get_instance()->netpoint_by_name_or_null(get_name()),
27              "Refusing to create a second NetZone called '%s'.", get_cname());
28   netpoint_     = new NetPoint(name_, NetPoint::Type::NetZone);
29   cpu_model_vm_ = static_cast<simgrid::kernel::resource::CpuModel*>(
30       simgrid::kernel::EngineImpl::get_instance()->get_default_model(simgrid::kernel::resource::Model::Type::CPU_VM));
31   cpu_model_pm_ = static_cast<simgrid::kernel::resource::CpuModel*>(
32       simgrid::kernel::EngineImpl::get_instance()->get_default_model(simgrid::kernel::resource::Model::Type::CPU_PM));
33   disk_model_ = static_cast<simgrid::kernel::resource::DiskModel*>(
34       simgrid::kernel::EngineImpl::get_instance()->get_default_model(simgrid::kernel::resource::Model::Type::DISK));
35   host_model_ = static_cast<simgrid::surf::HostModel*>(
36       simgrid::kernel::EngineImpl::get_instance()->get_default_model(simgrid::kernel::resource::Model::Type::HOST));
37   XBT_DEBUG("NetZone '%s' created with the id '%u'", get_cname(), netpoint_->id());
38 }
39
40 NetZoneImpl::~NetZoneImpl()
41 {
42   for (auto const& nz : children_)
43     delete nz;
44
45   for (auto const& kv : bypass_routes_)
46     delete kv.second;
47
48   s4u::Engine::get_instance()->netpoint_unregister(netpoint_);
49 }
50
51 /** @brief Returns the list of the hosts found in this NetZone (not recursively)
52  *
53  * Only the hosts that are directly contained in this NetZone are retrieved,
54  * not the ones contained in sub-netzones.
55  */
56 std::vector<s4u::Host*> NetZoneImpl::get_all_hosts() const
57 {
58   std::vector<s4u::Host*> res;
59   for (auto const& card : get_vertices()) {
60     s4u::Host* host = s4u::Host::by_name_or_null(card->get_name());
61     if (host != nullptr)
62       res.push_back(host);
63   }
64   return res;
65 }
66 int NetZoneImpl::get_host_count() const
67 {
68   int count = 0;
69   for (auto const& card : get_vertices()) {
70     const s4u::Host* host = s4u::Host::by_name_or_null(card->get_name());
71     if (host != nullptr)
72       count++;
73   }
74   return count;
75 }
76
77 s4u::Disk* NetZoneImpl::create_disk(const std::string& name, double read_bandwidth, double write_bandwidth)
78 {
79   auto* l = disk_model_->create_disk(name, read_bandwidth, write_bandwidth);
80
81   return l->get_iface();
82 }
83
84 s4u::Link* NetZoneImpl::create_link(const std::string& name, const std::vector<double>& bandwidths,
85                                     s4u::Link::SharingPolicy policy)
86 {
87   auto* l = network_model_->create_link(name, bandwidths, policy);
88
89   return l->get_iface();
90 }
91
92 s4u::Host* NetZoneImpl::create_host(const std::string& name, const std::vector<double>& speed_per_pstate,
93                                     int core_amount)
94 {
95   if (hierarchy_ == RoutingMode::unset)
96     hierarchy_ = RoutingMode::base;
97
98   auto* res = new s4u::Host(name);
99   res->set_netpoint((new NetPoint(name, NetPoint::Type::Host))->set_englobing_zone(this));
100
101   cpu_model_pm_->create_cpu(res, speed_per_pstate)->set_core_count(core_amount)->seal();
102
103   return res;
104 }
105
106 int NetZoneImpl::add_component(NetPoint* elm)
107 {
108   vertices_.push_back(elm);
109   return vertices_.size() - 1; // The rank of the newly created object
110 }
111
112 void NetZoneImpl::add_route(NetPoint* /*src*/, NetPoint* /*dst*/, NetPoint* /*gw_src*/, NetPoint* /*gw_dst*/,
113                             std::vector<resource::LinkImpl*>& /*link_list*/, bool /*symmetrical*/)
114 {
115   xbt_die("NetZone '%s' does not accept new routes (wrong class).", get_cname());
116 }
117
118 void NetZoneImpl::add_bypass_route(NetPoint* src, NetPoint* dst, NetPoint* gw_src, NetPoint* gw_dst,
119                                    std::vector<resource::LinkImpl*>& link_list, bool /* symmetrical */)
120 {
121   /* Argument validity checks */
122   if (gw_dst) {
123     XBT_DEBUG("Load bypassNetzoneRoute from %s@%s to %s@%s", src->get_cname(), gw_src->get_cname(), dst->get_cname(),
124               gw_dst->get_cname());
125     xbt_assert(not link_list.empty(), "Bypass route between %s@%s and %s@%s cannot be empty.", src->get_cname(),
126                gw_src->get_cname(), dst->get_cname(), gw_dst->get_cname());
127     xbt_assert(bypass_routes_.find({src, dst}) == bypass_routes_.end(),
128                "The bypass route between %s@%s and %s@%s already exists.", src->get_cname(), gw_src->get_cname(),
129                dst->get_cname(), gw_dst->get_cname());
130   } else {
131     XBT_DEBUG("Load bypassRoute from %s to %s", src->get_cname(), dst->get_cname());
132     xbt_assert(not link_list.empty(), "Bypass route between %s and %s cannot be empty.", src->get_cname(),
133                dst->get_cname());
134     xbt_assert(bypass_routes_.find({src, dst}) == bypass_routes_.end(),
135                "The bypass route between %s and %s already exists.", src->get_cname(), dst->get_cname());
136   }
137
138   /* Build a copy that will be stored in the dict */
139   auto* newRoute = new BypassRoute(gw_src, gw_dst);
140   for (auto const& link : link_list)
141     newRoute->links.push_back(link);
142
143   /* Store it */
144   bypass_routes_.insert({{src, dst}, newRoute});
145 }
146
147 /** @brief Get the common ancestor and its first children in each line leading to src and dst
148  *
149  * In the recursive case, this sets common_ancestor, src_ancestor and dst_ancestor are set as follows.
150  * @verbatim
151  *         platform root
152  *               |
153  *              ...                <- possibly long path
154  *               |
155  *         common_ancestor
156  *           /        \
157  *          /          \
158  *         /            \          <- direct links
159  *        /              \
160  *       /                \
161  *  src_ancestor     dst_ancestor  <- must be different in the recursive case
162  *      |                   |
163  *     ...                 ...     <-- possibly long paths (one hop or more)
164  *      |                   |
165  *     src                 dst
166  *  @endverbatim
167  *
168  *  In the base case (when src and dst are in the same netzone), things are as follows:
169  *  @verbatim
170  *                  platform root
171  *                        |
172  *                       ...                      <-- possibly long path
173  *                        |
174  * common_ancestor==src_ancestor==dst_ancestor    <-- all the same value
175  *                   /        \
176  *                  /          \                  <-- direct links (exactly one hop)
177  *                 /            \
178  *              src              dst
179  *  @endverbatim
180  *
181  * A specific recursive case occurs when src is the ancestor of dst. In this case,
182  * the base case routing should be used so the common_ancestor is specifically set
183  * to src_ancestor==dst_ancestor.
184  * Naturally, things are completely symmetrical if dst is the ancestor of src.
185  * @verbatim
186  *            platform root
187  *                  |
188  *                 ...                <-- possibly long path
189  *                  |
190  *  src == src_ancestor==dst_ancestor==common_ancestor <-- same value
191  *                  |
192  *                 ...                <-- possibly long path (one hop or more)
193  *                  |
194  *                 dst
195  *  @endverbatim
196  */
197 static void find_common_ancestors(NetPoint* src, NetPoint* dst,
198                                   /* OUT */ NetZoneImpl** common_ancestor, NetZoneImpl** src_ancestor,
199                                   NetZoneImpl** dst_ancestor)
200 {
201   /* Deal with the easy base case */
202   if (src->get_englobing_zone() == dst->get_englobing_zone()) {
203     *common_ancestor = src->get_englobing_zone();
204     *src_ancestor    = *common_ancestor;
205     *dst_ancestor    = *common_ancestor;
206     return;
207   }
208
209   /* engage the full recursive search */
210
211   /* (1) find the path to root of src and dst*/
212   const NetZoneImpl* src_as = src->get_englobing_zone();
213   const NetZoneImpl* dst_as = dst->get_englobing_zone();
214
215   xbt_assert(src_as, "Host %s must be in a netzone", src->get_cname());
216   xbt_assert(dst_as, "Host %s must be in a netzone", dst->get_cname());
217
218   /* (2) find the path to the root routing component */
219   std::vector<NetZoneImpl*> path_src;
220   NetZoneImpl* current = src->get_englobing_zone();
221   while (current != nullptr) {
222     path_src.push_back(current);
223     current = current->get_father();
224   }
225   std::vector<NetZoneImpl*> path_dst;
226   current = dst->get_englobing_zone();
227   while (current != nullptr) {
228     path_dst.push_back(current);
229     current = current->get_father();
230   }
231
232   /* (3) find the common father.
233    * Before that, index_src and index_dst may be different, they both point to nullptr in path_src/path_dst
234    * So we move them down simultaneously as long as they point to the same content.
235    *
236    * This works because all SimGrid platform have a unique root element (that is the last element of both paths).
237    */
238   NetZoneImpl* father = nullptr; // the netzone we dropped on the previous loop iteration
239   while (path_src.size() > 1 && path_dst.size() > 1 &&
240          path_src.at(path_src.size() - 1) == path_dst.at(path_dst.size() - 1)) {
241     father = path_src.at(path_src.size() - 1);
242     path_src.pop_back();
243     path_dst.pop_back();
244   }
245
246   /* (4) we found the difference at least. Finalize the returned values */
247   *src_ancestor = path_src.at(path_src.size() - 1); /* the first different father of src */
248   *dst_ancestor = path_dst.at(path_dst.size() - 1); /* the first different father of dst */
249   if (*src_ancestor == *dst_ancestor) {             // src is the ancestor of dst, or the contrary
250     *common_ancestor = *src_ancestor;
251   } else {
252     *common_ancestor = father;
253   }
254 }
255
256 /* PRECONDITION: this is the common ancestor of src and dst */
257 bool NetZoneImpl::get_bypass_route(NetPoint* src, NetPoint* dst,
258                                    /* OUT */ std::vector<resource::LinkImpl*>& links, double* latency)
259 {
260   // If never set a bypass route return nullptr without any further computations
261   if (bypass_routes_.empty())
262     return false;
263
264   /* Base case, no recursion is needed */
265   if (dst->get_englobing_zone() == this && src->get_englobing_zone() == this) {
266     if (bypass_routes_.find({src, dst}) != bypass_routes_.end()) {
267       const BypassRoute* bypassedRoute = bypass_routes_.at({src, dst});
268       for (resource::LinkImpl* const& link : bypassedRoute->links) {
269         links.push_back(link);
270         if (latency)
271           *latency += link->get_latency();
272       }
273       XBT_DEBUG("Found a bypass route from '%s' to '%s' with %zu links", src->get_cname(), dst->get_cname(),
274                 bypassedRoute->links.size());
275       return true;
276     }
277     return false;
278   }
279
280   /* Engage recursive search */
281
282   /* (1) find the path to the root routing component */
283   std::vector<NetZoneImpl*> path_src;
284   NetZoneImpl* current = src->get_englobing_zone();
285   while (current != nullptr) {
286     path_src.push_back(current);
287     current = current->father_;
288   }
289
290   std::vector<NetZoneImpl*> path_dst;
291   current = dst->get_englobing_zone();
292   while (current != nullptr) {
293     path_dst.push_back(current);
294     current = current->father_;
295   }
296
297   /* (2) find the common father */
298   while (path_src.size() > 1 && path_dst.size() > 1 &&
299          path_src.at(path_src.size() - 1) == path_dst.at(path_dst.size() - 1)) {
300     path_src.pop_back();
301     path_dst.pop_back();
302   }
303
304   int max_index_src = path_src.size() - 1;
305   int max_index_dst = path_dst.size() - 1;
306
307   int max_index = std::max(max_index_src, max_index_dst);
308
309   /* (3) Search for a bypass making the path up to the ancestor useless */
310   const BypassRoute* bypassedRoute = nullptr;
311   std::pair<kernel::routing::NetPoint*, kernel::routing::NetPoint*> key;
312   for (int max = 0; max <= max_index && not bypassedRoute; max++) {
313     for (int i = 0; i < max && not bypassedRoute; i++) {
314       if (i <= max_index_src && max <= max_index_dst) {
315         key      = {path_src.at(i)->netpoint_, path_dst.at(max)->netpoint_};
316         auto bpr = bypass_routes_.find(key);
317         if (bpr != bypass_routes_.end()) {
318           bypassedRoute = bpr->second;
319         }
320       }
321       if (not bypassedRoute && max <= max_index_src && i <= max_index_dst) {
322         key      = {path_src.at(max)->netpoint_, path_dst.at(i)->netpoint_};
323         auto bpr = bypass_routes_.find(key);
324         if (bpr != bypass_routes_.end()) {
325           bypassedRoute = bpr->second;
326         }
327       }
328     }
329
330     if (not bypassedRoute && max <= max_index_src && max <= max_index_dst) {
331       key      = {path_src.at(max)->netpoint_, path_dst.at(max)->netpoint_};
332       auto bpr = bypass_routes_.find(key);
333       if (bpr != bypass_routes_.end()) {
334         bypassedRoute = bpr->second;
335       }
336     }
337   }
338
339   /* (4) If we have the bypass, use it. If not, caller will do the Right Thing. */
340   if (bypassedRoute) {
341     XBT_DEBUG("Found a bypass route from '%s' to '%s' with %zu links. We may have to complete it with recursive "
342               "calls to getRoute",
343               src->get_cname(), dst->get_cname(), bypassedRoute->links.size());
344     if (src != key.first)
345       get_global_route(src, bypassedRoute->gw_src, links, latency);
346     for (resource::LinkImpl* const& link : bypassedRoute->links) {
347       links.push_back(link);
348       if (latency)
349         *latency += link->get_latency();
350     }
351     if (dst != key.second)
352       get_global_route(bypassedRoute->gw_dst, dst, links, latency);
353     return true;
354   }
355   XBT_DEBUG("No bypass route from '%s' to '%s'.", src->get_cname(), dst->get_cname());
356   return false;
357 }
358
359 void NetZoneImpl::get_global_route(NetPoint* src, NetPoint* dst,
360                                    /* OUT */ std::vector<resource::LinkImpl*>& links, double* latency)
361 {
362   RouteCreationArgs route;
363
364   XBT_DEBUG("Resolve route from '%s' to '%s'", src->get_cname(), dst->get_cname());
365
366   /* Find how src and dst are interconnected */
367   NetZoneImpl* common_ancestor;
368   NetZoneImpl* src_ancestor;
369   NetZoneImpl* dst_ancestor;
370   find_common_ancestors(src, dst, &common_ancestor, &src_ancestor, &dst_ancestor);
371   XBT_DEBUG("elements_father: common ancestor '%s' src ancestor '%s' dst ancestor '%s'", common_ancestor->get_cname(),
372             src_ancestor->get_cname(), dst_ancestor->get_cname());
373
374   /* Check whether a direct bypass is defined. If so, use it and bail out */
375   if (common_ancestor->get_bypass_route(src, dst, links, latency))
376     return;
377
378   /* If src and dst are in the same netzone, life is good */
379   if (src_ancestor == dst_ancestor) { /* SURF_ROUTING_BASE */
380     route.link_list = std::move(links);
381     common_ancestor->get_local_route(src, dst, &route, latency);
382     links = std::move(route.link_list);
383     return;
384   }
385
386   /* Not in the same netzone, no bypass. We'll have to find our path between the netzones recursively */
387
388   common_ancestor->get_local_route(src_ancestor->netpoint_, dst_ancestor->netpoint_, &route, latency);
389   xbt_assert((route.gw_src != nullptr) && (route.gw_dst != nullptr), "Bad gateways for route from '%s' to '%s'.",
390              src->get_cname(), dst->get_cname());
391
392   /* If source gateway is not our source, we have to recursively find our way up to this point */
393   if (src != route.gw_src)
394     get_global_route(src, route.gw_src, links, latency);
395   for (auto const& link : route.link_list)
396     links.push_back(link);
397
398   /* If dest gateway is not our destination, we have to recursively find our way from this point */
399   if (route.gw_dst != dst)
400     get_global_route(route.gw_dst, dst, links, latency);
401 }
402
403 void NetZoneImpl::seal()
404 {
405   do_seal(); // derived class' specific sealing procedure
406   sealed_ = true;
407 }
408
409 void NetZoneImpl::set_parent(NetZoneImpl* parent)
410 {
411   xbt_assert(sealed_ == false, "Impossible to set father to an already sealed NetZone(%s)", this->get_cname());
412   father_ = parent;
413   netpoint_->set_englobing_zone(father_);
414 }
415
416 void NetZoneImpl::set_network_model(simgrid::kernel::resource::NetworkModel* netmodel)
417 {
418   network_model_ = netmodel;
419 }
420
421 } // namespace routing
422 } // namespace kernel
423 } // namespace simgrid