X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fats%2Fgnunet-service-ats_addresses_mlp.c;h=175d4f41a79eacf49df43ecdf9d9716191753af1;hb=2cb5aae84e71bab1ed6da6c3651f6373f1d2d36a;hp=a878ddd9e0b2bcb7fe02d99d8e3b1977b2ed9978;hpb=a827aeaf11f556657ef75e6691ff685db14daadf;p=oweals%2Fgnunet.git diff --git a/src/ats/gnunet-service-ats_addresses_mlp.c b/src/ats/gnunet-service-ats_addresses_mlp.c index a878ddd9e..175d4f41a 100644 --- a/src/ats/gnunet-service-ats_addresses_mlp.c +++ b/src/ats/gnunet-service-ats_addresses_mlp.c @@ -29,13 +29,14 @@ #include "gnunet-service-ats_addresses.h" #include "gnunet-service-ats_addresses_mlp.h" #include "gnunet_statistics_service.h" -#if HAVE_LIBGLPK #include "glpk.h" -#endif -#define DEBUG_ATS GNUNET_YES -#define VERY_BIG_DOUBLE_VALUE DBL_MAX +#define WRITE_MLP GNUNET_NO +#define DEBUG_ATS GNUNET_NO +#define VERBOSE_GLPK GNUNET_NO +#define ENABLE_C8 GNUNET_YES +#define ENABLE_C9 GNUNET_YES /** * Translate glpk solver error codes to text * @param retcode return code @@ -148,6 +149,72 @@ mlp_status_to_string (int retcode) return "unknown error"; } +/** + * Translate ATS properties to text + * Just intended for debugging + * + * @param ats_index the ATS index + * @return string with result + */ +const char * +mlp_ats_to_string (int ats_index) +{ + switch (ats_index) { + case GNUNET_ATS_ARRAY_TERMINATOR: + return "GNUNET_ATS_ARRAY_TERMINATOR"; + break; + case GNUNET_ATS_UTILIZATION_UP: + return "GNUNET_ATS_UTILIZATION_UP"; + break; + case GNUNET_ATS_UTILIZATION_DOWN: + return "GNUNET_ATS_UTILIZATION_DOWN"; + break; + case GNUNET_ATS_COST_LAN: + return "GNUNET_ATS_COST_LAN"; + break; + case GNUNET_ATS_COST_WAN: + return "GNUNET_ATS_COST_LAN"; + break; + case GNUNET_ATS_COST_WLAN: + return "GNUNET_ATS_COST_WLAN"; + break; + case GNUNET_ATS_NETWORK_TYPE: + return "GNUNET_ATS_NETWORK_TYPE"; + break; + case GNUNET_ATS_QUALITY_NET_DELAY: + return "GNUNET_ATS_QUALITY_NET_DELAY"; + break; + case GNUNET_ATS_QUALITY_NET_DISTANCE: + return "GNUNET_ATS_QUALITY_NET_DISTANCE"; + break; + default: + return "unknown"; + break; + } + GNUNET_break (0); + return "unknown error"; +} + +/** + * Find a peer in the DLL + * + * @param mlp the mlp handle + * @param peer the peer to find + * @return the peer struct + */ +static struct ATS_Peer * +mlp_find_peer (struct GAS_MLP_Handle *mlp, const struct GNUNET_PeerIdentity *peer) +{ + struct ATS_Peer *res = mlp->peer_head; + while (res != NULL) + { + if (0 == memcmp (peer, &res->id, sizeof (struct GNUNET_PeerIdentity))) + break; + res = res->next; + } + return res; +} + /** * Intercept GLPK terminal output * @param info the mlp handle @@ -158,7 +225,7 @@ static int mlp_term_hook (void *info, const char *s) { /* Not needed atm struct MLP_information *mlp = info; */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", s); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s", s); return 1; } @@ -172,34 +239,189 @@ mlp_delete_problem (struct GAS_MLP_Handle *mlp) { if (mlp != NULL) { - glp_delete_prob(mlp->prob); + if (mlp->prob != NULL) + glp_delete_prob(mlp->prob); /* delete row index */ if (mlp->ia != NULL) + { GNUNET_free (mlp->ia); + mlp->ia = NULL; + } /* delete column index */ if (mlp->ja != NULL) + { GNUNET_free (mlp->ja); + mlp->ja = NULL; + } /* delete coefficients */ if (mlp->ar != NULL) + { GNUNET_free (mlp->ar); + mlp->ar = NULL; + } + mlp->ci = 0; + mlp->prob = NULL; + } +} + +/** + * Add constraints that are iterating over "forall addresses" + * and collects all existing peers for "forall peers" constraints + * + * @param cls GAS_MLP_Handle + * @param key Hashcode + * @param value ATS_Address + * + * @return GNUNET_OK to continue + */ +static int +create_constraint_it (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct GAS_MLP_Handle *mlp = cls; + struct ATS_Address *address = value; + struct MLP_information *mlpi; + unsigned int row_index; + char *name; + + GNUNET_assert (address->mlp_information != NULL); + mlpi = (struct MLP_information *) address->mlp_information; + + /* c 1) bandwidth capping + * b_t + (-M) * n_t <= 0 + */ + row_index = glp_add_rows (mlp->prob, 1); + mlpi->r_c1 = row_index; + /* set row name */ + GNUNET_asprintf(&name, "c1_%s_%s", GNUNET_i2s(&address->peer), address->plugin); + glp_set_row_name (mlp->prob, row_index, name); + GNUNET_free (name); + /* set row bounds: <= 0 */ + glp_set_row_bnds (mlp->prob, row_index, GLP_UP, 0.0, 0.0); + mlp->ia[mlp->ci] = row_index; + mlp->ja[mlp->ci] = mlpi->c_b; + mlp->ar[mlp->ci] = 1; + mlp->ci++; + + mlp->ia[mlp->ci] = row_index; + mlp->ja[mlp->ci] = mlpi->c_n; + mlp->ar[mlp->ci] = -mlp->BIG_M; + mlp->ci++; + + /* c 3) minimum bandwidth + * b_t + (-n_t * b_min) >= 0 + */ + + row_index = glp_add_rows (mlp->prob, 1); + /* set row name */ + GNUNET_asprintf(&name, "c3_%s_%s", GNUNET_i2s(&address->peer), address->plugin); + glp_set_row_name (mlp->prob, row_index, name); + GNUNET_free (name); + mlpi->r_c3 = row_index; + /* set row bounds: >= 0 */ + glp_set_row_bnds (mlp->prob, row_index, GLP_LO, 0.0, 0.0); + + mlp->ia[mlp->ci] = row_index; + mlp->ja[mlp->ci] = mlpi->c_b; + mlp->ar[mlp->ci] = 1; + mlp->ci++; + + mlp->ia[mlp->ci] = row_index; + mlp->ja[mlp->ci] = mlpi->c_n; + mlp->ar[mlp->ci] = - (double) mlp->b_min; + mlp->ci++; + + /* c 4) minimum connections + * (1)*n_1 + ... + (1)*n_m >= n_min + */ + mlp->ia[mlp->ci] = mlp->r_c4; + mlp->ja[mlp->ci] = mlpi->c_n; + mlp->ar[mlp->ci] = 1; + mlp->ci++; + + /* c 6) maximize diversity + * (1)*n_1 + ... + (1)*n_m - d == 0 + */ + mlp->ia[mlp->ci] = mlp->r_c6; + mlp->ja[mlp->ci] = mlpi->c_n; + mlp->ar[mlp->ci] = 1; + mlp->ci++; + + /* c 10) obey network specific quotas + * (1)*b_1 + ... + (1)*b_m <= quota_n + */ + + int cur_row = 0; + int c; + for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++) + { + if (mlp->quota_index[c] == address->atsp_network_type) + { + cur_row = mlp->r_quota[c]; + break; + } + } + + if (cur_row != 0) + { + mlp->ia[mlp->ci] = cur_row; + mlp->ja[mlp->ci] = mlpi->c_b; + mlp->ar[mlp->ci] = 1; + mlp->ci++; + } + else + { + GNUNET_break (0); + } + + return GNUNET_OK; +} + +/** + * Find the required ATS information for an address + * + * @param addr the address + * @param ats_index the desired ATS index + * + * @return the index on success, otherwise GNUNET_SYSERR + */ + +static int +mlp_lookup_ats (struct ATS_Address *addr, int ats_index) +{ + struct GNUNET_ATS_Information * ats = addr->ats; + int c = 0; + int found = GNUNET_NO; + for (c = 0; c < addr->ats_count; c++) + { + if (ats[c].type == ats_index) + { + found = GNUNET_YES; + break; + } } + if (found == GNUNET_YES) + return c; + else + return GNUNET_SYSERR; } /** * Adds the problem constraints for all addresses * Required for problem recreation after address deletion * + * @param mlp the mlp handle * @param addresses all addresses */ static void mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses) { - //double M = VERY_BIG_DOUBLE_VALUE; unsigned int n_addresses; + int c; + char *name; /* Problem matrix*/ n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses); @@ -228,16 +450,37 @@ mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CON * #rows: |ressources| * #indices: |n_addresses| * + * c 10) obey network specific quota + * #rows: |network types + * #indices: |n_addresses| + * * Sum for feasibility constraints: * #rows: 3 * |n_addresses| + |ressources| + |peers| + 1 * #indices: 7 * |n_addresses| * * optimality constraints: - * tbc + * + * c 6) diversity + * #rows: 1 + * #indices: |n_addresses| + 1 + * + * c 7) quality + * #rows: |quality properties| + * #indices: |n_addresses| + |quality properties| + * + * c 8) utilization + * #rows: 1 + * #indices: |n_addresses| + 1 + * + * c 9) relativity + * #rows: |peers| + * #indices: |n_addresses| + |peers| * */ - int pi = (7 * n_addresses); + /* last +1 caused by glpk index starting with one: [1..pi]*/ + int pi = ((7 * n_addresses) + (5 * n_addresses + mlp->m_q + mlp->c_p + 2) + 1); mlp->cm_size = pi; + mlp->ci = 1; /* row index */ int *ia = GNUNET_malloc (pi * sizeof (int)); @@ -251,18 +494,235 @@ mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CON double *ar= GNUNET_malloc (pi * sizeof (double)); mlp->ar = ar; + /* Adding constraint rows + * This constraints are kind of "for all addresses" + * Feasibility constraints: + * + * c 1) bandwidth capping + * c 3) minimum bandwidth + * c 4) minimum number of connections + * c 6) maximize diversity + * c 10) obey network specific quota + */ + + int min = mlp->n_min; + if (mlp->n_min > mlp->c_p) + min = mlp->c_p; + + mlp->r_c4 = glp_add_rows (mlp->prob, 1); + glp_set_row_name (mlp->prob, mlp->r_c4, "c4"); + glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_LO, min, min); + + /* Add row for c6) */ + + mlp->r_c6 = glp_add_rows (mlp->prob, 1); + /* Set type type to fix */ + glp_set_row_bnds (mlp->prob, mlp->r_c6, GLP_FX, 0.0, 0.0); + /* Setting -D */ + ia[mlp->ci] = mlp->r_c6 ; + ja[mlp->ci] = mlp->c_d; + ar[mlp->ci] = -1; + mlp->ci++; + + /* Add rows for c 10) */ + for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++) + { + mlp->r_quota[c] = glp_add_rows (mlp->prob, 1); + char * text; + GNUNET_asprintf(&text, "quota_ats_%i", mlp->quota_index[c]); + glp_set_row_name (mlp->prob, mlp->r_quota[c], text); + GNUNET_free (text); + /* Set bounds to 0 <= x <= quota_out */ + glp_set_row_bnds (mlp->prob, mlp->r_quota[c], GLP_UP, 0.0, mlp->quota_out[c]); + } + + GNUNET_CONTAINER_multihashmap_iterate (addresses, create_constraint_it, mlp); + + /* Adding constraint rows + * This constraints are kind of "for all peers" + * Feasibility constraints: + * + * c 2) 1 address per peer + * sum (n_p1_1 + ... + n_p1_n) = 1 + * + * c 8) utilization + * sum (f_p * b_p1_1 + ... + f_p * b_p1_n) - u = 0 + * + * c 9) relativity + * V p : sum (bt_1 + ... +bt_n) - f_p * r = 0 + * */ + + /* Adding rows for c 8) */ + mlp->r_c8 = glp_add_rows (mlp->prob, mlp->c_p); + glp_set_row_name (mlp->prob, mlp->r_c8, "c8"); + /* Set row bound == 0 */ + glp_set_row_bnds (mlp->prob, mlp->r_c8, GLP_FX, 0.0, 0.0); + /* -u */ + + ia[mlp->ci] = mlp->r_c8; + ja[mlp->ci] = mlp->c_u; + ar[mlp->ci] = -1; + mlp->ci++; + + struct ATS_Peer * peer = mlp->peer_head; + while (peer != NULL) + { + struct ATS_Address *addr = peer->head; + struct MLP_information *mlpi = NULL; + + /* Adding rows for c 2) */ + peer->r_c2 = glp_add_rows (mlp->prob, 1); + GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&peer->id)); + glp_set_row_name (mlp->prob, peer->r_c2, name); + GNUNET_free (name); + /* Set row bound == 1 */ + glp_set_row_bnds (mlp->prob, peer->r_c2, GLP_FX, 1.0, 1.0); + + /* Adding rows for c 9) */ +#if ENABLE_C9 + peer->r_c9 = glp_add_rows (mlp->prob, 1); + GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&peer->id)); + glp_set_row_name (mlp->prob, peer->r_c9, name); + GNUNET_free (name); + /* Set row bound == 0 */ + glp_set_row_bnds (mlp->prob, peer->r_c9, GLP_LO, 0.0, 0.0); + + /* Set -r */ + ia[mlp->ci] = peer->r_c9; + ja[mlp->ci] = mlp->c_r; + ar[mlp->ci] = -1; + mlp->ci++; +#endif + + while (addr != NULL) + { + mlpi = (struct MLP_information *) addr->mlp_information; + + /* coefficient for c 2) */ + + ia[mlp->ci] = peer->r_c2; + ja[mlp->ci] = mlpi->c_n; + ar[mlp->ci] = 1; + mlp->ci++; + + /* coefficient for c 8) */ + ia[mlp->ci] = mlp->r_c8; + ja[mlp->ci] = mlpi->c_b; + ar[mlp->ci] = peer->f; + mlp->ci++; + +#if ENABLE_C9 + /* coefficient for c 9) */ + ia[mlp->ci] = peer->r_c9; + ja[mlp->ci] = mlpi->c_b; + ar[mlp->ci] = 1; + mlp->ci++; +#endif + + addr = addr->next; + } + peer = peer->next; + } + + /* c 7) For all quality metrics */ + + + for (c = 0; c < mlp->m_q; c++) + { + struct ATS_Peer *tp; + struct ATS_Address *ta; + struct MLP_information * mlpi; + double value = 1.0; + + /* Adding rows for c 7) */ + mlp->r_q[c] = glp_add_rows (mlp->prob, 1); + GNUNET_asprintf(&name, "c7_q%i_%s", c, mlp_ats_to_string(mlp->q[c])); + glp_set_row_name (mlp->prob, mlp->r_q[c], name); + GNUNET_free (name); + /* Set row bound == 0 */ + glp_set_row_bnds (mlp->prob, mlp->r_q[c], GLP_LO, 0.0, 0.0); + + ia[mlp->ci] = mlp->r_q[c]; + ja[mlp->ci] = mlp->c_q[c]; + ar[mlp->ci] = -1; + mlp->ci++; + + for (tp = mlp->peer_head; tp != NULL; tp = tp->next) + for (ta = tp->head; ta != NULL; ta = ta->next) + { + mlpi = ta->mlp_information; + value = mlpi->q_averaged[c]; + + mlpi->r_q[c] = mlp->r_q[c]; + + ia[mlp->ci] = mlp->r_q[c]; + ja[mlp->ci] = mlpi->c_b; + ar[mlp->ci] = tp->f * value; + mlp->ci++; + } + } +} + - /* Adding constraint rows */ - /* Feasibility constraints */ +/** + * Add columns for all addresses + * + * @param cls GAS_MLP_Handle + * @param key Hashcode + * @param value ATS_Address + * + * @return GNUNET_OK to continue + */ +static int +create_columns_it (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct GAS_MLP_Handle *mlp = cls; + struct ATS_Address *address = value; + struct MLP_information *mlpi; + unsigned int col; + char *name; - /* c 1) bandwidth capping */ + GNUNET_assert (address->mlp_information != NULL); + mlpi = address->mlp_information; + + /* Add bandwidth column */ + col = glp_add_cols (mlp->prob, 2); + mlpi->c_b = col; + mlpi->c_n = col + 1; + + + GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin); + glp_set_col_name (mlp->prob, mlpi->c_b , name); + GNUNET_free (name); + /* Lower bound == 0 */ + glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0); + /* Continuous value*/ + glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV); + /* Objective function coefficient == 0 */ + glp_set_obj_coef (mlp->prob, mlpi->c_b , 0); + + + /* Add usage column */ + GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin); + glp_set_col_name (mlp->prob, mlpi->c_n, name); + GNUNET_free (name); + /* Limit value : 0 <= value <= 1 */ + glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0); + /* Integer value*/ + glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV); + /* Objective function coefficient == 0 */ + glp_set_obj_coef (mlp->prob, mlpi->c_n, 0); + return GNUNET_OK; } + + /** * Create the MLP problem * * @param mlp the MLP handle + * @param addresses the hashmap containing all adresses * @return GNUNET_OK or GNUNET_SYSERR */ static int @@ -273,6 +733,10 @@ mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHas int c; char *name; + GNUNET_assert (mlp->prob == NULL); + + /* create the glpk problem */ + mlp->prob = glp_create_prob (); /* Set a problem name */ glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution"); @@ -304,6 +768,7 @@ mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHas /* Column lower bound = 0.0 */ glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0); +#if ENABLE_C9 /* Relativity r column */ col = glp_add_cols (mlp->prob, 1); mlp->c_r = col; @@ -313,10 +778,11 @@ mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHas glp_set_obj_coef (mlp->prob, col, mlp->co_R); /* Column lower bound = 0.0 */ glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0); +#endif /* Quality metric columns */ - col = glp_add_cols(mlp->prob, mlp->m); - for (c = 0; c < mlp->m; c++) + col = glp_add_cols(mlp->prob, mlp->m_q); + for (c = 0; c < mlp->m_q; c++) { mlp->c_q[c] = col + c; GNUNET_asprintf (&name, "q_%u", mlp->q[c]); @@ -328,7 +794,14 @@ mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHas glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]); } - /* Add columns for existing addresses */ + /* Add columns for addresses */ + GNUNET_CONTAINER_multihashmap_iterate (addresses, create_columns_it, mlp); + + /* Add constraints */ + mlp_add_constraints_all_addresses (mlp, addresses); + + /* Load the matrix */ + glp_load_matrix(mlp->prob, (mlp->ci-1), mlp->ia, mlp->ja, mlp->ar); return res; } @@ -383,9 +856,9 @@ lp_solv: else { /* Problem was ill-defined, no way to handle that */ - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-mlp", - "Solving LP problem failed: %s\n", mlp_solve_to_string(res)); + "Solving LP problem failed: %i %s\n", res, mlp_solve_to_string(res)); return GNUNET_SYSERR; } } @@ -412,7 +885,7 @@ lp_solv: /* Problem was ill-defined, no way to handle that */ default: - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-mlp", "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res)); return GNUNET_SYSERR; @@ -460,7 +933,7 @@ mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp) else { /* Problem was ill-defined, no way to handle that */ - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-mlp", "Solving MLP problem failed: %s\n", mlp_solve_to_string(res)); return GNUNET_SYSERR; @@ -487,7 +960,7 @@ mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp) /* Problem was ill-defined, no way to handle that */ default: - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-mlp", "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res)); return GNUNET_SYSERR; @@ -497,7 +970,7 @@ mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp) return GNUNET_OK; } -int mlp_solve_problem (struct GAS_MLP_Handle *mlp); +int GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp); static void mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) @@ -509,11 +982,11 @@ mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; -#if DEBUG_ATS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n"); -#endif + if (mlp->addr_in_problem != 0) - mlp_solve_problem(mlp); + GAS_mlp_solve_problem(mlp); } @@ -524,13 +997,85 @@ mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure */ int -mlp_solve_problem (struct GAS_MLP_Handle *mlp) +GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp) { int res; mlp->last_execution = GNUNET_TIME_absolute_get (); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solving\n"); + + +#if WRITE_MLP + char * name; + static int i; + i++; + GNUNET_asprintf(&name, "problem_%i", i); + glp_write_lp (mlp->prob, 0, name); + GNUNET_free (name); +# endif + res = mlp_solve_lp_problem (mlp); - if (res == GNUNET_OK) - res = mlp_solve_mlp_problem (mlp); + +#if WRITE_MLP + GNUNET_asprintf(&name, "problem_%i_lp_solution", i); + glp_print_sol (mlp->prob, name); + GNUNET_free (name); +# endif + + if (res != GNUNET_OK) + { + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "LP Problem solving failed\n"); + + return GNUNET_SYSERR; + } + + res = mlp_solve_mlp_problem (mlp); + +#if WRITE_MLP + GNUNET_asprintf(&name, "problem_%i_mlp_solution", i); + glp_print_mip (mlp->prob, name); + GNUNET_free (name); +# endif + if (res != GNUNET_OK) + { + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP Problem solving failed\n"); + + return GNUNET_SYSERR; + } + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved\n"); + /* Process result */ + struct ATS_Peer *p = NULL; + struct ATS_Address *a = NULL; + struct MLP_information *mlpi = NULL; + + for (p = mlp->peer_head; p != NULL; p = p->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s'\n", GNUNET_i2s (&p->id)); + for (a = p->head; a != NULL; a = a->next) + { + double b = 0.0; + double n = 0.0; + + mlpi = a->mlp_information; + + b = glp_mip_col_val(mlp->prob, mlpi->c_b); + mlpi->b = b; + + n = glp_mip_col_val(mlp->prob, mlpi->c_n); + if (n == 1.0) + mlpi->n = GNUNET_YES; + else + mlpi->n = GNUNET_NO; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tAddress %s %f\n", + (n == 1.0) ? "[x]" : "[ ]", b); + } + } + if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK) { GNUNET_SCHEDULER_cancel(mlp->mlp_task); @@ -543,6 +1088,7 @@ mlp_solve_problem (struct GAS_MLP_Handle *mlp) /** * Init the MLP problem solving component * + * @param cfg the GNUNET_CONFIGURATION_Handle handle * @param stats the GNUNET_STATISTICS handle * @param max_duration maximum numbers of iterations for the LP/MLP Solver * @param max_iterations maximum time limit for the LP/MLP Solver @@ -563,6 +1109,9 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned int b_min; unsigned int n_min; struct GNUNET_TIME_Relative i_exec; + int c; + char * quota_out_str; + char * quota_in_str; /* Init GLPK environment */ GNUNET_assert (glp_init_env() == 0); @@ -571,6 +1120,8 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, mlp->prob = glp_create_prob(); GNUNET_assert (mlp->prob != NULL); + mlp->BIG_M = (double) BIG_M_VALUE; + /* Get diversity coefficient from configuration */ if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", "COEFFICIENT_D", @@ -599,7 +1150,6 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, int i_delay = -1; int i_distance = -1; int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties; - int c; for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++) { /* initialize quality coefficients with default value 1.0 */ @@ -633,7 +1183,9 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, &tmp)) b_min = tmp; else - b_min = 64000; + { + b_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__); + } /* Get minimum number of connections from configuration */ if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", @@ -643,6 +1195,99 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, else n_min = 4; + /* Init network quotas */ + int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType; + for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++) + { + mlp->quota_index[c] = quotas[c]; + static char * entry_in = NULL; + static char * entry_out = NULL; + unsigned long long quota_in = 0; + unsigned long long quota_out = 0; + + switch (quotas[c]) { + case GNUNET_ATS_NET_UNSPECIFIED: + entry_out = "UNSPECIFIED_QUOTA_OUT"; + entry_in = "UNSPECIFIED_QUOTA_IN"; + break; + case GNUNET_ATS_NET_LOOPBACK: + entry_out = "LOOPBACK_QUOTA_OUT"; + entry_in = "LOOPBACK_QUOTA_IN"; + break; + case GNUNET_ATS_NET_LAN: + entry_out = "LAN_QUOTA_OUT"; + entry_in = "LAN_QUOTA_IN"; + break; + case GNUNET_ATS_NET_WAN: + entry_out = "WAN_QUOTA_OUT"; + entry_in = "WAN_QUOTA_IN"; + break; + case GNUNET_ATS_NET_WLAN: + entry_out = "WLAN_QUOTA_OUT"; + entry_in = "WLAN_QUOTA_IN"; + break; + default: + break; + } + + if ((entry_in == NULL) || (entry_out == NULL)) + continue; + + if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_out, "a_out_str)) + { + if (0 == strcmp(quota_out_str, BIG_M_STRING) || + (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str, "a_out))) + quota_out = mlp->BIG_M; + + GNUNET_free (quota_out_str); + quota_out_str = NULL; + } + else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c]) + { + quota_out = 0; + } + else + { + quota_out = mlp->BIG_M; + } + + if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_in, "a_in_str)) + { + if (0 == strcmp(quota_in_str, BIG_M_STRING) || + (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in))) + quota_in = mlp->BIG_M; + + GNUNET_free (quota_in_str); + quota_in_str = NULL; + } + else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c]) + { + quota_in = 0; + } + else + { + quota_in = mlp->BIG_M; + } + + /* Check if defined quota could make problem unsolvable */ + if (((n_min * b_min) > quota_out) && (GNUNET_ATS_NET_UNSPECIFIED != quotas[c])) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Inconsistent quota configuration value `%s': " \ + "outbound quota (%u Bps) too small for combination of minimum connections and minimum bandwidth per peer (%u * %u Bps = %u)\n", entry_out, quota_out, n_min, b_min, n_min * b_min); + + GAS_mlp_done(mlp); + mlp = NULL; + return NULL; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found `%s' quota %llu and `%s' quota %llu\n", + entry_out, quota_out, entry_in, quota_in); + GNUNET_STATISTICS_update ((struct GNUNET_STATISTICS_Handle *) stats, entry_out, quota_out, GNUNET_NO); + GNUNET_STATISTICS_update ((struct GNUNET_STATISTICS_Handle *) stats, entry_in, quota_in, GNUNET_NO); + mlp->quota_out[c] = quota_out; + mlp->quota_in[c] = quota_in; + } + /* Get minimum number of connections from configuration */ if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats", "ATS_EXEC_INTERVAL", @@ -654,26 +1299,28 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats; mlp->max_iterations = max_iterations; mlp->max_exec_duration = max_duration; + mlp->auto_solve = GNUNET_YES; /* Redirect GLPK output to GNUnet logging */ glp_error_hook((void *) mlp, &mlp_term_hook); /* Init LP solving parameters */ glp_init_smcp(&mlp->control_param_lp); -#if DEBUG_MLP - mlp->control_param_lp.msg_lev = GLP_MSG_ALL; -#else + mlp->control_param_lp.msg_lev = GLP_MSG_OFF; +#if VERBOSE_GLPK + mlp->control_param_lp.msg_lev = GLP_MSG_ALL; #endif + mlp->control_param_lp.it_lim = max_iterations; mlp->control_param_lp.tm_lim = max_duration.rel_value; /* Init MLP solving parameters */ glp_init_iocp(&mlp->control_param_mlp); -#if DEBUG_MLP - mlp->control_param_mlp.msg_lev = GLP_MSG_ALL; -#else + mlp->control_param_mlp.msg_lev = GLP_MSG_OFF; +#if VERBOSE_GLPK + mlp->control_param_mlp.msg_lev = GLP_MSG_ALL; #endif mlp->control_param_mlp.tm_lim = max_duration.rel_value; @@ -684,11 +1331,165 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, mlp->co_U = U; mlp->b_min = b_min; mlp->n_min = n_min; - mlp->m = GNUNET_ATS_QualityPropertiesCount; + mlp->m_q = GNUNET_ATS_QualityPropertiesCount; return mlp; } +static void +update_quality (struct GAS_MLP_Handle *mlp, struct ATS_Address * address) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating quality metrics for peer `%s'\n", + GNUNET_i2s (&address->peer)); + + struct MLP_information *mlpi = address->mlp_information; + struct GNUNET_ATS_Information *ats = address->ats; + GNUNET_assert (mlpi != NULL); + + int c; + + for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++) + { + int index = mlp_lookup_ats(address, mlp->q[c]); + + if (index == GNUNET_SYSERR) + continue; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating address for peer `%s' value `%s': %f\n", + GNUNET_i2s (&address->peer), + mlp_ats_to_string(mlp->q[c]), + (double) ats[index].value); + + int i = mlpi->q_avg_i[c]; + double * qp = mlpi->q[c]; + qp[i] = (double) ats[index].value; + + int t; + for (t = 0; t < MLP_AVERAGING_QUEUE_LENGTH; t++) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s': `%s' queue[%u]: %f\n", + GNUNET_i2s (&address->peer), + mlp_ats_to_string(mlp->q[c]), + t, + qp[t]); + } + + if (mlpi->q_avg_i[c] + 1 < (MLP_AVERAGING_QUEUE_LENGTH)) + mlpi->q_avg_i[c] ++; + else + mlpi->q_avg_i[c] = 0; + + + int c2; + int c3; + double avg = 0.0; + switch (mlp->q[c]) + { + case GNUNET_ATS_QUALITY_NET_DELAY: + c3 = 0; + for (c2 = 0; c2 < MLP_AVERAGING_QUEUE_LENGTH; c2++) + { + if (mlpi->q[c][c2] != -1) + { + double * t2 = mlpi->q[c] ; + avg += t2[c2]; + c3 ++; + } + } + if (c3 > 0) + /* avg = 1 / ((q[0] + ... + q[l]) /c3) => c3 / avg*/ + mlpi->q_averaged[c] = (double) c3 / avg; + else + mlpi->q_averaged[c] = 0.0; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s': `%s' average sum: %f, average: %f, weight: %f\n", + GNUNET_i2s (&address->peer), + mlp_ats_to_string(mlp->q[c]), + avg, + avg / (double) c3, + mlpi->q_averaged[c]); + + break; + case GNUNET_ATS_QUALITY_NET_DISTANCE: + c3 = 0; + for (c2 = 0; c2 < MLP_AVERAGING_QUEUE_LENGTH; c2++) + { + if (mlpi->q[c][c2] != -1) + { + double * t2 = mlpi->q[c] ; + avg += t2[c2]; + c3 ++; + } + } + if (c3 > 0) + /* avg = 1 / ((q[0] + ... + q[l]) /c3) => c3 / avg*/ + mlpi->q_averaged[c] = (double) c3 / avg; + else + mlpi->q_averaged[c] = 0.0; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s': `%s' average sum: %f, average: %f, weight: %f\n", + GNUNET_i2s (&address->peer), + mlp_ats_to_string(mlp->q[c]), + avg, + avg / (double) c3, + mlpi->q_averaged[c]); + + break; + default: + break; + } + + if ((mlpi->c_b != 0) && (mlpi->r_q[c] != 0)) + { + + /* Get current number of columns */ + int found = GNUNET_NO; + int cols = glp_get_num_cols(mlp->prob); + int *ind = GNUNET_malloc (cols * sizeof (int) + 1); + double *val = GNUNET_malloc (cols * sizeof (double) + 1); + + /* Get the matrix row of quality */ + int length = glp_get_mat_row(mlp->prob, mlp->r_q[c], ind, val); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "cols %i, length %i c_b %i\n", cols, length, mlpi->c_b); + int c4; + /* Get the index if matrix row of quality */ + for (c4 = 1; c4 <= length; c4++ ) + { + if (mlpi->c_b == ind[c4]) + { + /* Update the value */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating quality `%s' column `%s' row `%s' : %f -> %f\n", + mlp_ats_to_string(mlp->q[c]), + glp_get_col_name (mlp->prob, ind[c4]), + glp_get_row_name (mlp->prob, mlp->r_q[c]), + val[c4], + mlpi->q_averaged[c]); + val[c4] = mlpi->q_averaged[c]; + found = GNUNET_YES; + break; + } + } + + if (found == GNUNET_NO) + { + + ind[length+1] = mlpi->c_b; + val[length+1] = mlpi->q_averaged[c]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%i ind[%i] val[%i]: %i %f\n", length+1, length+1, length+1, mlpi->c_b, mlpi->q_averaged[c]); + glp_set_mat_row (mlp->prob, mlpi->r_q[c], length+1, ind, val); + } + else + { + /* Get the index if matrix row of quality */ + glp_set_mat_row (mlp->prob, mlpi->r_q[c], length, ind, val); + } + + GNUNET_free (ind); + GNUNET_free (val); + } + } +} + /** * Updates a single address in the MLP problem * @@ -700,15 +1501,14 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg, * * @param mlp the MLP Handle * @param addresses the address hashmap + * the address has to be already removed from the hashmap * @param address the address to update */ void GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address) { int new; - int col; struct MLP_information *mlpi; - char * name; GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO); @@ -718,52 +1518,81 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult else new = GNUNET_NO; - if (mlp->prob == NULL) - { - mlp_create_problem(mlp, addresses); - mlp_add_constraints_all_addresses (mlp, addresses); - } - /* Do the update */ if (new == GNUNET_YES) { mlpi = GNUNET_malloc (sizeof (struct MLP_information)); + + int c; + for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++) + { + int c2; + mlpi->r_q[c] = 0; + for (c2 = 0; c2 < MLP_AVERAGING_QUEUE_LENGTH; c2++) + mlpi->q[c][c2] = -1.0; /* -1.0: invalid value */ + mlpi->q_avg_i[c] = 0; + mlpi->q_averaged[c] = 0.0; + } + address->mlp_information = mlpi; mlp->addr_in_problem ++; - /* Add bandwidth column */ - col = glp_add_cols (mlp->prob, 2); - mlpi->c_b = col; - mlpi->c_n = col + 1; + /* Check for and add peer */ + struct ATS_Peer *peer = mlp_find_peer (mlp, &address->peer); + if (peer == NULL) + { - GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin); - glp_set_col_name (mlp->prob, mlpi->c_b , name); - GNUNET_free (name); - /* Lower bound == 0 */ - glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0); - /* Continuous value*/ - glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV); - /* Objective function coefficient == 0 */ - glp_set_obj_coef (mlp->prob, mlpi->c_b , 0); - - /* Add usage column */ - GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin); - glp_set_col_name (mlp->prob, mlpi->c_n, name); - GNUNET_free (name); - /* Limit value : 0 <= value <= 1 */ - glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0); - /* Integer value*/ - glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV); - /* Objective function coefficient == 0 */ - glp_set_obj_coef (mlp->prob, mlpi->c_n, 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding new peer `%s'\n", + GNUNET_i2s (&address->peer)); + + peer = GNUNET_malloc (sizeof (struct ATS_Peer)); + peer->head = NULL; + peer->tail = NULL; + + int c; + for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++) + { + peer->f_q[c] = 1.0; + } + peer->f = 1.0; + + memcpy (&peer->id, &address->peer, sizeof (struct GNUNET_PeerIdentity)); + GNUNET_assert(address->prev == NULL); + GNUNET_assert(address->next == NULL); + GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address); + GNUNET_CONTAINER_DLL_insert (mlp->peer_head, mlp->peer_tail, peer); + mlp->c_p ++; + } + else + { + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding address to peer `%s'\n", + GNUNET_i2s (&address->peer)); - /* Add */ + GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address); + } + + update_quality (mlp, address); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating existing address to peer `%s'\n", + GNUNET_i2s (&address->peer)); + + update_quality (mlp, address); } /* Recalculate */ if (new == GNUNET_YES) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recreating problem: new address\n"); + + mlp_delete_problem (mlp); + mlp_create_problem (mlp, addresses); mlp->presolver_required = GNUNET_YES; - mlp_solve_problem (mlp); + } + if (mlp->auto_solve == GNUNET_YES) + GAS_mlp_solve_problem (mlp); } /** @@ -773,6 +1602,7 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult * * @param mlp the MLP Handle * @param addresses the address hashmap + * the address has to be already removed from the hashmap * @param address the address to delete */ void @@ -789,24 +1619,104 @@ GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult mlp->addr_in_problem --; } + /* Remove from peer list */ + struct ATS_Peer *head = mlp_find_peer (mlp, &address->peer); + GNUNET_assert (head != NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting address for `%s'\n", GNUNET_i2s (&address->peer)); + + GNUNET_CONTAINER_DLL_remove (head->head, head->tail, address); + if ((head->head == NULL) && (head->tail == NULL)) + { + /* No address for peer left, remove peer */ + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting peer `%s'\n", GNUNET_i2s (&address->peer)); + + GNUNET_CONTAINER_DLL_remove (mlp->peer_head, mlp->peer_tail, head); + GNUNET_free (head); + mlp->c_p --; + } + /* Update problem */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recreating problem: new address\n"); - /* Recalculate */ - mlp->presolver_required = GNUNET_YES; - mlp_solve_problem (mlp); + mlp_delete_problem (mlp); + if ((GNUNET_CONTAINER_multihashmap_size (addresses) > 0) && (mlp->c_p > 0)) + { + mlp_create_problem (mlp, addresses); + + /* Recalculate */ + mlp->presolver_required = GNUNET_YES; + if (mlp->auto_solve == GNUNET_YES) + GAS_mlp_solve_problem (mlp); + } } +static int +mlp_get_preferred_address_it (void *cls, const GNUNET_HashCode * key, void *value) +{ + + struct ATS_PreferedAddress *aa = (struct ATS_PreferedAddress *) cls; + struct ATS_Address *addr = value; + struct MLP_information *mlpi = addr->mlp_information; + if (mlpi == NULL) + return GNUNET_YES; + if (mlpi->n == GNUNET_YES) + { + aa->address = addr; + if (mlpi->b > (double) UINT32_MAX) + aa->bandwidth_out = UINT32_MAX; + else + aa->bandwidth_out = (uint32_t) mlpi->b; + aa->bandwidth_in = 0; + return GNUNET_NO; + } + return GNUNET_YES; +} + + /** - * Deletes a single address in the MLP problem + * Get the preferred address for a specific peer * * @param mlp the MLP Handle - * @param addresses the address hashmap - * @param address the address to change the preference + * @param addresses address hashmap + * @param peer the peer + * @return suggested address + */ +struct ATS_PreferedAddress * +GAS_mlp_get_preferred_address (struct GAS_MLP_Handle *mlp, + struct GNUNET_CONTAINER_MultiHashMap * addresses, + const struct GNUNET_PeerIdentity *peer) +{ + struct ATS_PreferedAddress * aa = GNUNET_malloc (sizeof (struct ATS_PreferedAddress)); + aa->address = NULL; + aa->bandwidth_in = 0; + aa->bandwidth_out = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting preferred address for `%s'\n", GNUNET_i2s (peer)); + GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey, mlp_get_preferred_address_it, aa); + return aa; +} + + +/** + * Changes the preferences for a peer in the MLP problem + * + * @param mlp the MLP Handle + * @param peer the peer + * @param kind the kind to change the preference + * @param score the score */ void -GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address) +GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp, + const struct GNUNET_PeerIdentity *peer, + enum GNUNET_ATS_PreferenceKind kind, + float score) { GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO); + + struct ATS_Peer *p = mlp_find_peer (mlp, peer); + p = p; + /* Here we have to do the matching */ } /** @@ -816,12 +1726,26 @@ GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp, struct GNUNET_CON void GAS_mlp_done (struct GAS_MLP_Handle *mlp) { + struct ATS_Peer * peer; + struct ATS_Peer * tmp; + + GNUNET_assert (mlp != NULL); + if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK) { GNUNET_SCHEDULER_cancel(mlp->mlp_task); mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK; } + /* clean up peer list */ + peer = mlp->peer_head; + while (peer != NULL) + { + GNUNET_CONTAINER_DLL_remove(mlp->peer_head, mlp->peer_tail, peer); + tmp = peer->next; + GNUNET_free (peer); + peer = tmp; + } mlp_delete_problem (mlp); /* Clean up GLPK environment */