Logo AND Algorithmique Numérique Distribuée

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