Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / src / kernel / routing / NetZoneImpl.cpp
1 /* Copyright (c) 2006-2016. 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 "src/kernel/routing/NetZoneImpl.hpp"
7 #include "simgrid/s4u/engine.hpp"
8 #include "simgrid/s4u/host.hpp"
9 #include "src/kernel/routing/NetCard.hpp"
10 #include "src/surf/cpu_interface.hpp"
11 #include "src/surf/network_interface.hpp"
12
13 #include "xbt/log.h"
14
15 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(surf_route);
16
17 namespace simgrid {
18 namespace kernel {
19 namespace routing {
20
21 class BypassRoute {
22 public:
23   explicit BypassRoute(NetCard* gwSrc, NetCard* gwDst) : gw_src(gwSrc), gw_dst(gwDst) {}
24   const NetCard* gw_src;
25   const NetCard* gw_dst;
26   std::vector<Link*> links;
27 };
28
29 NetZoneImpl::NetZoneImpl(NetZone* father, const char* name) : NetZone(father, name)
30 {
31   xbt_assert(nullptr == simgrid::s4u::Engine::instance()->netcardByNameOrNull(name),
32              "Refusing to create a second NetZone called '%s'.", name);
33
34   netcard_ = new NetCard(name, NetCard::Type::NetZone, static_cast<NetZoneImpl*>(father));
35   XBT_DEBUG("NetZone '%s' created with the id '%d'", name, netcard_->id());
36 }
37 NetZoneImpl::~NetZoneImpl()
38 {
39   for (auto& kv : bypassRoutes_)
40     delete kv.second;
41
42   simgrid::s4u::Engine::instance()->netcardUnregister(netcard_);
43 }
44
45 simgrid::s4u::Host* NetZoneImpl::createHost(const char* name, std::vector<double>* speedPerPstate, int coreAmount)
46 {
47   simgrid::s4u::Host* res = new simgrid::s4u::Host(name);
48
49   if (hierarchy_ == RoutingMode::unset)
50     hierarchy_ = RoutingMode::base;
51
52   res->pimpl_netcard = new NetCard(name, NetCard::Type::Host, this);
53
54   surf_cpu_model_pm->createCpu(res, speedPerPstate, coreAmount);
55
56   return res;
57 }
58
59 void NetZoneImpl::addBypassRoute(sg_platf_route_cbarg_t e_route)
60 {
61   /* Argument validity checks */
62   if (e_route->gw_dst) {
63     XBT_DEBUG("Load bypassASroute from %s@%s to %s@%s", e_route->src->cname(), e_route->gw_src->cname(),
64               e_route->dst->cname(), e_route->gw_dst->cname());
65     xbt_assert(!e_route->link_list->empty(), "Bypass route between %s@%s and %s@%s cannot be empty.",
66                e_route->src->cname(), e_route->gw_src->cname(), e_route->dst->cname(), e_route->gw_dst->cname());
67     xbt_assert(bypassRoutes_.find({e_route->src, e_route->dst}) == bypassRoutes_.end(),
68                "The bypass route between %s@%s and %s@%s already exists.", e_route->src->cname(),
69                e_route->gw_src->cname(), e_route->dst->cname(), e_route->gw_dst->cname());
70   } else {
71     XBT_DEBUG("Load bypassRoute from %s to %s", e_route->src->cname(), e_route->dst->cname());
72     xbt_assert(!e_route->link_list->empty(), "Bypass route between %s and %s cannot be empty.", e_route->src->cname(),
73                e_route->dst->cname());
74     xbt_assert(bypassRoutes_.find({e_route->src, e_route->dst}) == bypassRoutes_.end(),
75                "The bypass route between %s and %s already exists.", e_route->src->cname(), e_route->dst->cname());
76   }
77
78   /* Build a copy that will be stored in the dict */
79   kernel::routing::BypassRoute* newRoute = new kernel::routing::BypassRoute(e_route->gw_src, e_route->gw_dst);
80   for (auto link : *e_route->link_list)
81     newRoute->links.push_back(link);
82
83   /* Store it */
84   bypassRoutes_.insert({{e_route->src, e_route->dst}, newRoute});
85 }
86
87 /** @brief Get the common ancestor and its first children in each line leading to src and dst
88  *
89  * In the recursive case, this sets common_ancestor, src_ancestor and dst_ancestor are set as follows.
90  * @verbatim
91  *         platform root
92  *               |
93  *              ...                <- possibly long path
94  *               |
95  *         common_ancestor
96  *           /        \
97  *          /          \
98  *         /            \          <- direct links
99  *        /              \
100  *       /                \
101  *  src_ancestor     dst_ancestor  <- must be different in the recursive case
102  *      |                   |
103  *     ...                 ...     <-- possibly long paths (one hop or more)
104  *      |                   |
105  *     src                 dst
106  *  @endverbatim
107  *
108  *  In the base case (when src and dst are in the same AS), things are as follows:
109  *  @verbatim
110  *                  platform root
111  *                        |
112  *                       ...                      <-- possibly long path
113  *                        |
114  * common_ancestor==src_ancestor==dst_ancestor    <-- all the same value
115  *                   /        \
116  *                  /          \                  <-- direct links (exactly one hop)
117  *                 /            \
118  *              src              dst
119  *  @endverbatim
120  *
121  * A specific recursive case occurs when src is the ancestor of dst. In this case,
122  * the base case routing should be used so the common_ancestor is specifically set
123  * to src_ancestor==dst_ancestor.
124  * Naturally, things are completely symmetrical if dst is the ancestor of src.
125  * @verbatim
126  *            platform root
127  *                  |
128  *                 ...                <-- possibly long path
129  *                  |
130  *  src == src_ancestor==dst_ancestor==common_ancestor <-- same value
131  *                  |
132  *                 ...                <-- possibly long path (one hop or more)
133  *                  |
134  *                 dst
135  *  @endverbatim
136  */
137 static void find_common_ancestors(NetCard* src, NetCard* dst,
138                                   /* OUT */ NetZoneImpl** common_ancestor, NetZoneImpl** src_ancestor,
139                                   NetZoneImpl** dst_ancestor)
140 {
141   /* Deal with the easy base case */
142   if (src->netzone() == dst->netzone()) {
143     *common_ancestor = src->netzone();
144     *src_ancestor    = *common_ancestor;
145     *dst_ancestor    = *common_ancestor;
146     return;
147   }
148
149   /* engage the full recursive search */
150
151   /* (1) find the path to root of src and dst*/
152   NetZoneImpl* src_as = src->netzone();
153   NetZoneImpl* dst_as = dst->netzone();
154
155   xbt_assert(src_as, "Host %s must be in an AS", src->cname());
156   xbt_assert(dst_as, "Host %s must be in an AS", dst->cname());
157
158   /* (2) find the path to the root routing component */
159   std::vector<NetZoneImpl*> path_src;
160   NetZoneImpl* current = src->netzone();
161   while (current != nullptr) {
162     path_src.push_back(current);
163     current = static_cast<NetZoneImpl*>(current->father());
164   }
165   std::vector<NetZoneImpl*> path_dst;
166   current = dst->netzone();
167   while (current != nullptr) {
168     path_dst.push_back(current);
169     current = static_cast<NetZoneImpl*>(current->father());
170   }
171
172   /* (3) find the common father.
173    * Before that, index_src and index_dst may be different, they both point to nullptr in path_src/path_dst
174    * So we move them down simultaneously as long as they point to the same content.
175    *
176    * This works because all SimGrid platform have a unique root element (that is the last element of both paths).
177    */
178   NetZoneImpl* father = nullptr; // the netzone we dropped on the previous loop iteration
179   while (path_src.size() > 1 && path_dst.size() > 1 &&
180          path_src.at(path_src.size() - 1) == path_dst.at(path_dst.size() - 1)) {
181     father = path_src.at(path_src.size() - 1);
182     path_src.pop_back();
183     path_dst.pop_back();
184   }
185
186   /* (4) we found the difference at least. Finalize the returned values */
187   *src_ancestor = path_src.at(path_src.size() - 1); /* the first different father of src */
188   *dst_ancestor = path_dst.at(path_dst.size() - 1); /* the first different father of dst */
189   if (*src_ancestor == *dst_ancestor) {             // src is the ancestor of dst, or the contrary
190     *common_ancestor = *src_ancestor;
191   } else {
192     *common_ancestor = father;
193   }
194 }
195
196 /* PRECONDITION: this is the common ancestor of src and dst */
197 bool NetZoneImpl::getBypassRoute(routing::NetCard* src, routing::NetCard* dst,
198                                  /* OUT */ std::vector<surf::Link*>* links, double* latency)
199 {
200   // If never set a bypass route return nullptr without any further computations
201   if (bypassRoutes_.empty())
202     return false;
203
204   /* Base case, no recursion is needed */
205   if (dst->netzone() == this && src->netzone() == this) {
206     if (bypassRoutes_.find({src, dst}) != bypassRoutes_.end()) {
207       BypassRoute* bypassedRoute = bypassRoutes_.at({src, dst});
208       for (surf::Link* link : bypassedRoute->links) {
209         links->push_back(link);
210         if (latency)
211           *latency += link->latency();
212       }
213       XBT_DEBUG("Found a bypass route from '%s' to '%s' with %zu links", src->cname(), dst->cname(),
214                 bypassedRoute->links.size());
215       return true;
216     }
217     return false;
218   }
219
220   /* Engage recursive search */
221
222   /* (1) find the path to the root routing component */
223   std::vector<NetZoneImpl*> path_src;
224   NetZone* current = src->netzone();
225   while (current != nullptr) {
226     path_src.push_back(static_cast<NetZoneImpl*>(current));
227     current = current->father_;
228   }
229
230   std::vector<NetZoneImpl*> path_dst;
231   current = dst->netzone();
232   while (current != nullptr) {
233     path_dst.push_back(static_cast<NetZoneImpl*>(current));
234     current = current->father_;
235   }
236
237   /* (2) find the common father */
238   while (path_src.size() > 1 && path_dst.size() > 1 &&
239          path_src.at(path_src.size() - 1) == path_dst.at(path_dst.size() - 1)) {
240     path_src.pop_back();
241     path_dst.pop_back();
242   }
243
244   int max_index_src = path_src.size() - 1;
245   int max_index_dst = path_dst.size() - 1;
246
247   int max_index = std::max(max_index_src, max_index_dst);
248
249   /* (3) Search for a bypass making the path up to the ancestor useless */
250   BypassRoute* bypassedRoute = nullptr;
251   std::pair<kernel::routing::NetCard*, kernel::routing::NetCard*> key;
252   for (int max = 0; max <= max_index; max++) {
253     for (int i = 0; i < max; i++) {
254       if (i <= max_index_src && max <= max_index_dst) {
255         key = {path_src.at(i)->netcard_, path_dst.at(max)->netcard_};
256         if (bypassRoutes_.find(key) != bypassRoutes_.end()) {
257           bypassedRoute = bypassRoutes_.at(key);
258           break;
259         }
260       }
261       if (max <= max_index_src && i <= max_index_dst) {
262         key = {path_src.at(max)->netcard_, path_dst.at(i)->netcard_};
263         if (bypassRoutes_.find(key) != bypassRoutes_.end()) {
264           bypassedRoute = bypassRoutes_.at(key);
265           break;
266         }
267       }
268     }
269
270     if (bypassedRoute)
271       break;
272
273     if (max <= max_index_src && max <= max_index_dst) {
274       key = {path_src.at(max)->netcard_, path_dst.at(max)->netcard_};
275       if (bypassRoutes_.find(key) != bypassRoutes_.end()) {
276         bypassedRoute = bypassRoutes_.at(key);
277         break;
278       }
279     }
280   }
281
282   /* (4) If we have the bypass, use it. If not, caller will do the Right Thing. */
283   if (bypassedRoute) {
284     XBT_DEBUG("Found a bypass route from '%s' to '%s' with %zu links. We may have to complete it with recursive "
285               "calls to getRoute",
286               src->cname(), dst->cname(), bypassedRoute->links.size());
287     if (src != key.first)
288       getGlobalRoute(src, const_cast<NetCard*>(bypassedRoute->gw_src), links, latency);
289     for (surf::Link* link : bypassedRoute->links) {
290       links->push_back(link);
291       if (latency)
292         *latency += link->latency();
293     }
294     if (dst != key.second)
295       getGlobalRoute(const_cast<NetCard*>(bypassedRoute->gw_dst), dst, links, latency);
296     return true;
297   }
298   XBT_DEBUG("No bypass route from '%s' to '%s'.", src->cname(), dst->cname());
299   return false;
300 }
301
302 void NetZoneImpl::getGlobalRoute(routing::NetCard* src, routing::NetCard* dst,
303                                  /* OUT */ std::vector<surf::Link*>* links, double* latency)
304 {
305   s_sg_platf_route_cbarg_t route;
306   memset(&route, 0, sizeof(route));
307
308   XBT_DEBUG("Resolve route from '%s' to '%s'", src->cname(), dst->cname());
309
310   /* Find how src and dst are interconnected */
311   NetZoneImpl *common_ancestor, *src_ancestor, *dst_ancestor;
312   find_common_ancestors(src, dst, &common_ancestor, &src_ancestor, &dst_ancestor);
313   XBT_DEBUG("elements_father: common ancestor '%s' src ancestor '%s' dst ancestor '%s'", common_ancestor->name(),
314             src_ancestor->name(), dst_ancestor->name());
315
316   /* Check whether a direct bypass is defined. If so, use it and bail out */
317   if (common_ancestor->getBypassRoute(src, dst, links, latency))
318     return;
319
320   /* If src and dst are in the same AS, life is good */
321   if (src_ancestor == dst_ancestor) { /* SURF_ROUTING_BASE */
322     route.link_list = links;
323     common_ancestor->getLocalRoute(src, dst, &route, latency);
324     return;
325   }
326
327   /* Not in the same AS, no bypass. We'll have to find our path between the ASes recursively*/
328
329   route.link_list = new std::vector<surf::Link*>();
330
331   common_ancestor->getLocalRoute(src_ancestor->netcard_, dst_ancestor->netcard_, &route, latency);
332   xbt_assert((route.gw_src != nullptr) && (route.gw_dst != nullptr), "bad gateways for route from \"%s\" to \"%s\"",
333              src->cname(), dst->cname());
334
335   /* If source gateway is not our source, we have to recursively find our way up to this point */
336   if (src != route.gw_src)
337     getGlobalRoute(src, route.gw_src, links, latency);
338   for (auto link : *route.link_list)
339     links->push_back(link);
340   delete route.link_list;
341
342   /* If dest gateway is not our destination, we have to recursively find our way from this point */
343   if (route.gw_dst != dst)
344     getGlobalRoute(route.gw_dst, dst, links, latency);
345 }
346 }
347 }
348 } // namespace