From f394d1bae02fd51a7b02135857ef77975381eb18 Mon Sep 17 00:00:00 2001 From: weiyanhua Date: Mon, 22 Jul 2019 18:08:36 +0800 Subject: [PATCH 1/4] make it possible to bind laddr to lcore by adding laddr_lcore_mapping option. In this mode, dest addr is needed in FDIR instead of dest addr and dest port mask. NOTICE: number of laddrs should be greater or equal to number of slave lcores. Or, some slave lcores will have no laddr causing FNAT fowarding failed in those slave lcores. Co-authored-by: kldeng Co-authored-by: lixiaoxiao --- conf/dpvs.bond.conf.sample | 1 + conf/dpvs.conf.items | 1 + conf/dpvs.conf.sample | 1 + conf/dpvs.conf.single-bond.sample | 1 + conf/dpvs.conf.single-nic.sample | 1 + include/ipvs/service.h | 9 + include/sa_pool.h | 8 + src/ipvs/ip_vs_laddr.c | 324 ++++++++++++++++++++++++++++-- src/ipvs/ip_vs_service.c | 6 + src/netif.c | 10 +- src/sa_pool.c | 226 ++++++++++++++++----- 11 files changed, 512 insertions(+), 76 deletions(-) diff --git a/conf/dpvs.bond.conf.sample b/conf/dpvs.bond.conf.sample index 0281a6a5a..f68376c41 100644 --- a/conf/dpvs.bond.conf.sample +++ b/conf/dpvs.bond.conf.sample @@ -371,4 +371,5 @@ ipvs_defs { ! sa_pool config sa_pool { pool_hash_size 16 + pool_mode laddr_lcore_mapping } diff --git a/conf/dpvs.conf.items b/conf/dpvs.conf.items index 36cbda4dd..3ec6805a3 100644 --- a/conf/dpvs.conf.items +++ b/conf/dpvs.conf.items @@ -245,4 +245,5 @@ ipvs_defs { sa_pool { pool_hash_size 16 <16, 1-128> + pool_mode laddr_lcore_mapping } diff --git a/conf/dpvs.conf.sample b/conf/dpvs.conf.sample index 80f525b38..0c0034641 100644 --- a/conf/dpvs.conf.sample +++ b/conf/dpvs.conf.sample @@ -324,4 +324,5 @@ ipvs_defs { ! sa_pool config sa_pool { pool_hash_size 16 + pool_mode laddr_lcore_mapping } diff --git a/conf/dpvs.conf.single-bond.sample b/conf/dpvs.conf.single-bond.sample index 0d67e4fcb..3940c7c26 100644 --- a/conf/dpvs.conf.single-bond.sample +++ b/conf/dpvs.conf.single-bond.sample @@ -276,4 +276,5 @@ ipvs_defs { ! sa_pool config sa_pool { pool_hash_size 16 + pool_mode laddr_lcore_mapping } diff --git a/conf/dpvs.conf.single-nic.sample b/conf/dpvs.conf.single-nic.sample index beba1a114..f0eb444ba 100644 --- a/conf/dpvs.conf.single-nic.sample +++ b/conf/dpvs.conf.single-nic.sample @@ -249,4 +249,5 @@ ipvs_defs { ! sa_pool config sa_pool { pool_hash_size 16 + pool_mode laddr_lcore_mapping } diff --git a/include/ipvs/service.h b/include/ipvs/service.h index c8d997260..862bea181 100644 --- a/include/ipvs/service.h +++ b/include/ipvs/service.h @@ -44,6 +44,12 @@ rte_rwlock_t __dp_vs_svc_lock; +struct laddr_list_pre_lcore { + struct list_head laddr_list; /* local address (LIP) pool */ + struct list_head *laddr_curr; + uint32_t num_laddrs; +}; + /* virtual service */ struct dp_vs_service { struct list_head s_list; /* node for normal service table */ @@ -88,6 +94,9 @@ struct dp_vs_service { rte_rwlock_t laddr_lock; uint32_t num_laddrs; + struct laddr_list_pre_lcore pre_list[RTE_MAX_LCORE]; +#define this_pre_list pre_list[rte_lcore_id()] + /* ... flags, timer ... */ } __rte_cache_aligned; #endif diff --git a/include/sa_pool.h b/include/sa_pool.h index 7a136aebb..65387e191 100644 --- a/include/sa_pool.h +++ b/include/sa_pool.h @@ -42,12 +42,20 @@ #ifndef __DPVS_SA_POOL__ #define __DPVS_SA_POOL__ +enum { + LADDR_LCORE_MAPPING_POOL_MODE, + LPORT_LCORE_MAPPING_POOL_MODE, +}; + struct sa_pool_stats { uint32_t used_cnt; uint32_t free_cnt; uint32_t miss_cnt; }; +extern uint8_t sa_pool_mode; +#define SA_POOL_MODE sa_pool_mode + int sa_pool_init(void); int sa_pool_term(void); diff --git a/src/ipvs/ip_vs_laddr.c b/src/ipvs/ip_vs_laddr.c index 836d49f09..4d67579d1 100644 --- a/src/ipvs/ip_vs_laddr.c +++ b/src/ipvs/ip_vs_laddr.c @@ -108,6 +108,7 @@ struct dp_vs_laddr { }; static uint32_t dp_vs_laddr_max_trails = 16; +static uint64_t lcore_mask; static inline int __laddr_step(struct dp_vs_service *svc) { @@ -124,7 +125,7 @@ static inline int __laddr_step(struct dp_vs_service *svc) return 1; } -static inline struct dp_vs_laddr *__get_laddr(struct dp_vs_service *svc) +static inline struct dp_vs_laddr *__get_laddr_port_mode(struct dp_vs_service *svc) { int step; struct dp_vs_laddr *laddr = NULL; @@ -153,6 +154,45 @@ static inline struct dp_vs_laddr *__get_laddr(struct dp_vs_service *svc) return laddr; } +static inline struct dp_vs_laddr *__get_laddr_addr_mode(struct dp_vs_service *svc) +{ + struct dp_vs_laddr *laddr = NULL; + + /* if list not inited ? list_empty() returns true ! */ + assert(svc->this_pre_list.laddr_list.next); + + if (list_empty(&svc->this_pre_list.laddr_list)) { + return NULL; + } + + /* In LADDR_LCORE_MAPPING_POOL_MODE, the iteration step is different + * between laddr_list and realserver rr/wrr scheduler internally since every + * laddr is bound to a dedicated lcore. So we don't need to get a random + * laddr_list step any more. + **/ + if (unlikely(!svc->this_pre_list.laddr_curr)) + svc->this_pre_list.laddr_curr = svc->this_pre_list.laddr_list.next; + else + svc->this_pre_list.laddr_curr = svc->this_pre_list.laddr_curr->next; + + if (svc->this_pre_list.laddr_curr == &svc->this_pre_list.laddr_list) + svc->this_pre_list.laddr_curr = svc->this_pre_list.laddr_list.next; + + laddr = list_entry(svc->this_pre_list.laddr_curr, struct dp_vs_laddr, list); + rte_atomic32_inc(&laddr->refcnt); + + return laddr; +} + +static inline struct dp_vs_laddr *__get_laddr(struct dp_vs_service *svc) +{ + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) { + return __get_laddr_port_mode(svc); + } else { + return __get_laddr_addr_mode(svc); + } +} + static inline void put_laddr(struct dp_vs_laddr *laddr) { /* use lock if other field need by changed */ @@ -164,8 +204,10 @@ int dp_vs_laddr_bind(struct dp_vs_conn *conn, struct dp_vs_service *svc) { struct dp_vs_laddr *laddr = NULL; int i; + int num_laddrs = 0; uint16_t sport = 0; struct sockaddr_storage dsin, ssin; + struct inet_ifaddr *ifa; if (!conn || !conn->dest || !svc) return EDPVS_INVAL; @@ -182,7 +224,11 @@ int dp_vs_laddr_bind(struct dp_vs_conn *conn, struct dp_vs_service *svc) * 2. we uses svc->num_laddrs; */ rte_rwlock_write_lock(&svc->laddr_lock); - for (i = 0; i < dp_vs_laddr_max_trails && i < svc->num_laddrs; i++) { + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) + num_laddrs = svc->num_laddrs; + else + num_laddrs = svc->this_pre_list.num_laddrs; + for (i = 0; i < dp_vs_laddr_max_trails && i < num_laddrs; i++) { /* select a local IP from service */ laddr = __get_laddr(svc); if (!laddr) { @@ -191,6 +237,21 @@ int dp_vs_laddr_bind(struct dp_vs_conn *conn, struct dp_vs_service *svc) return EDPVS_RESOURCE; } + if (SA_POOL_MODE == LADDR_LCORE_MAPPING_POOL_MODE) { + ifa = inet_addr_ifa_get(conn->af, laddr->iface, &laddr->addr); + assert(ifa); + if (!ifa->this_sa_pool) { +#ifdef CONFIG_DPVS_IPVS_DEBUG + char buf[64]; + if (inet_ntop(conn->af, &laddr->addr, buf, sizeof(buf)) == NULL) + snprintf(buf, sizeof(buf), "::"); + RTE_LOG(DEBUG, IPVS, "%s: %s is not assigned on [%d], " + "try next laddr.\n",__func__, buf, rte_lcore_id()); +#endif + continue; + } + } + memset(&dsin, 0, sizeof(struct sockaddr_storage)); memset(&ssin, 0, sizeof(struct sockaddr_storage)); @@ -299,11 +360,71 @@ int dp_vs_laddr_unbind(struct dp_vs_conn *conn) return EDPVS_OK; } +static int __dp_vs_laddr_add_port_mode(struct dp_vs_service *svc, + int af, struct dp_vs_laddr *new) +{ + struct dp_vs_laddr *curr; + + rte_rwlock_write_lock(&svc->laddr_lock); + list_for_each_entry(curr, &svc->laddr_list, list) { + if (af == curr->af && inet_addr_equal(af, &curr->addr, &new->addr)) { + rte_rwlock_write_unlock(&svc->laddr_lock); + //rte_free(new); + return EDPVS_EXIST; + } + } + + list_add_tail(&new->list, &svc->laddr_list); + svc->num_laddrs++; + + rte_rwlock_write_unlock(&svc->laddr_lock); + return EDPVS_OK; +} + +static int __dp_vs_laddr_add_addr_mode(struct dp_vs_service *svc, + int af, struct dp_vs_laddr *new) +{ + struct dp_vs_laddr *curr; + struct inet_ifaddr *ifa; + int cid = 0; + + rte_rwlock_write_lock(&svc->laddr_lock); + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + list_for_each_entry(curr, &svc->pre_list[cid].laddr_list, list) { + if (af == curr->af && inet_addr_equal(af, &curr->addr, &new->addr)) { + rte_rwlock_write_unlock(&svc->laddr_lock); + //rte_free(new); + return EDPVS_EXIST; + } + } + } + + ifa = inet_addr_ifa_get(af, new->iface, &new->addr); + if (!ifa) { + rte_rwlock_write_unlock(&svc->laddr_lock); + return EDPVS_NOTEXIST; + } + + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + /* skip master and unused cores */ + if (cid > 64 || !(lcore_mask & (1L << cid))) + continue; + if (ifa->sa_pools[cid]) { + list_add_tail(&new->list, &svc->pre_list[cid].laddr_list); + svc->pre_list[cid].num_laddrs++; + } + } + + rte_rwlock_write_unlock(&svc->laddr_lock); + return EDPVS_OK; +} + int dp_vs_laddr_add(struct dp_vs_service *svc, int af, const union inet_addr *addr, const char *ifname) { - struct dp_vs_laddr *new, *curr; + struct dp_vs_laddr *new; + int err = 0; if (!svc || !addr) return EDPVS_INVAL; @@ -325,23 +446,19 @@ int dp_vs_laddr_add(struct dp_vs_service *svc, return EDPVS_NOTEXIST; } - rte_rwlock_write_lock(&svc->laddr_lock); - list_for_each_entry(curr, &svc->laddr_list, list) { - if (af == curr->af && inet_addr_equal(af, &curr->addr, &new->addr)) { - rte_rwlock_write_unlock(&svc->laddr_lock); - rte_free(new); - return EDPVS_EXIST; - } + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) { + err = __dp_vs_laddr_add_port_mode(svc, af, new); + } else { + err = __dp_vs_laddr_add_addr_mode(svc, af, new); } - list_add_tail(&new->list, &svc->laddr_list); - svc->num_laddrs++; - rte_rwlock_write_unlock(&svc->laddr_lock); - - return EDPVS_OK; + if (err != EDPVS_OK) + rte_free(new); + return err; } -int dp_vs_laddr_del(struct dp_vs_service *svc, int af, const union inet_addr *addr) +static int __dp_vs_laddr_del_port_mode(struct dp_vs_service *svc, int af, + const union inet_addr *addr) { struct dp_vs_laddr *laddr, *next; int err = EDPVS_NOTEXIST; @@ -378,8 +495,65 @@ int dp_vs_laddr_del(struct dp_vs_service *svc, int af, const union inet_addr *ad return err; } -/* if success, it depend on caller to free @addrs by rte_free() */ -static int dp_vs_laddr_getall(struct dp_vs_service *svc, +static int __dp_vs_laddr_del_addr_mode(struct dp_vs_service *svc, int af, + const union inet_addr *addr) +{ + struct dp_vs_laddr *laddr, *next; + int cid = 0; + int err = EDPVS_NOTEXIST; + + if (!svc || !addr) + return EDPVS_INVAL; + + rte_rwlock_write_lock(&svc->laddr_lock); + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + /* skip master and unused cores */ + if (cid > 64 || !(lcore_mask & (1L << cid))) + continue; + list_for_each_entry_safe(laddr, next, &svc->pre_list[cid].laddr_list, list) { + if (!((af == laddr->af) && inet_addr_equal(af, &laddr->addr, addr))) + continue; + + /* found */ + if (rte_atomic32_read(&laddr->refcnt) == 0) { + /* update svc->curr_laddr */ + if (svc->pre_list[cid].laddr_curr == &laddr->list) + svc->pre_list[cid].laddr_curr = laddr->list.next; + list_del(&laddr->list); + rte_free(laddr); + svc->pre_list[cid].num_laddrs--; + err = EDPVS_OK; + } else { + /* XXX: move to trash list and implement an garbage collector, + * or just try del again ? */ + err = EDPVS_BUSY; + } + break; + } + } + + rte_rwlock_write_unlock(&svc->laddr_lock); + + if (err == EDPVS_BUSY) + RTE_LOG(DEBUG, IPVS, "%s: laddr is in use.\n", __func__); + + return err; +} + +int dp_vs_laddr_del(struct dp_vs_service *svc, int af, const union inet_addr *addr) +{ + int err = 0; + + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) { + err = __dp_vs_laddr_del_port_mode(svc,af, addr); + } else { + err = __dp_vs_laddr_del_addr_mode(svc, af, addr); + } + + return err; +} + +static int __dp_vs_laddr_getall_port_mode(struct dp_vs_service *svc, struct dp_vs_laddr_entry **addrs, size_t *naddr) { struct dp_vs_laddr *laddr; @@ -416,7 +590,69 @@ static int dp_vs_laddr_getall(struct dp_vs_service *svc, return EDPVS_OK; } -int dp_vs_laddr_flush(struct dp_vs_service *svc) +static int __dp_vs_laddr_getall_addr_mode(struct dp_vs_service *svc, + struct dp_vs_laddr_entry **addrs, size_t *naddr) +{ + struct dp_vs_laddr *laddr; + int i = 0; + int cid = 0; + int num_laddrs = 0; + + if (!svc || !addrs || !naddr) + return EDPVS_INVAL; + + rte_rwlock_write_lock(&svc->laddr_lock); + + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + num_laddrs += svc->pre_list[cid].num_laddrs; + } + + if (num_laddrs > 0) { + *naddr = num_laddrs; + *addrs = rte_malloc_socket(0, sizeof(struct dp_vs_laddr_entry) * num_laddrs, + RTE_CACHE_LINE_SIZE, rte_socket_id()); + if (!(*addrs)) { + rte_rwlock_write_unlock(&svc->laddr_lock); + return EDPVS_NOMEM; + } + + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + /* skip master and unused cores */ + if (cid > 64 || !(lcore_mask & (1L << cid))) + continue; + list_for_each_entry(laddr, &svc->pre_list[cid].laddr_list, list) { + assert(i < *naddr); + (*addrs)[i].af = laddr->af; + (*addrs)[i].addr = laddr->addr; + (*addrs)[i].nconns = rte_atomic32_read(&laddr->conn_counts); + i++; + } + } + } else { + *naddr = 0; + *addrs = NULL; + } + + rte_rwlock_write_unlock(&svc->laddr_lock); + return EDPVS_OK; +} + +/* if success, it depend on caller to free @addrs by rte_free() */ +static int dp_vs_laddr_getall(struct dp_vs_service *svc, + struct dp_vs_laddr_entry **addrs, size_t *naddr) +{ + int err = EDPVS_OK; + + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) { + err = __dp_vs_laddr_getall_port_mode(svc, addrs, naddr); + } else { + err = __dp_vs_laddr_getall_addr_mode(svc, addrs, naddr); + } + + return err; +} + +static int __dp_vs_laddr_flush_port_mode(struct dp_vs_service *svc) { struct dp_vs_laddr *laddr, *next; int err = EDPVS_OK; @@ -440,7 +676,55 @@ int dp_vs_laddr_flush(struct dp_vs_service *svc) err = EDPVS_BUSY; } } + rte_rwlock_write_unlock(&svc->laddr_lock); + return err; +} + +static int __dp_vs_laddr_flush_addr_mode(struct dp_vs_service *svc) +{ + struct dp_vs_laddr *laddr, *next; + int cid = 0; + int err = EDPVS_OK; + + if (!svc) + return EDPVS_INVAL; + + rte_rwlock_write_lock(&svc->laddr_lock); + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + /* skip master and unused cores */ + if (cid > 64 || !(lcore_mask & (1L << cid))) + continue; + list_for_each_entry_safe(laddr, next, &svc->pre_list[cid].laddr_list, list) { + if (rte_atomic32_read(&laddr->refcnt) == 0) { + list_del(&laddr->list); + rte_free(laddr); + svc->pre_list[cid].num_laddrs--; + } else { + char buf[64]; + + if (inet_ntop(laddr->af, &laddr->addr, buf, sizeof(buf)) == NULL) + snprintf(buf, sizeof(buf), "::"); + + RTE_LOG(DEBUG, IPVS, "%s: laddr %s is in use.\n", __func__, buf); + err = EDPVS_BUSY; + } + } + } + + rte_rwlock_write_unlock(&svc->laddr_lock); + return err; +} + +int dp_vs_laddr_flush(struct dp_vs_service *svc) +{ + int err = EDPVS_OK; + + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) { + err = __dp_vs_laddr_flush_port_mode(svc); + } else { + err = __dp_vs_laddr_flush_addr_mode(svc); + } return err; } @@ -571,6 +855,8 @@ int dp_vs_laddr_init(void) if ((err = sockopt_register(&laddr_sockopts)) != EDPVS_OK) return err; + /* enabled lcore should not change after init */ + netif_get_slave_lcores(NULL, &lcore_mask); return EDPVS_OK; } diff --git a/src/ipvs/ip_vs_service.c b/src/ipvs/ip_vs_service.c index 60a8637ee..f30b48428 100644 --- a/src/ipvs/ip_vs_service.c +++ b/src/ipvs/ip_vs_service.c @@ -471,6 +471,7 @@ int dp_vs_add_service(struct dp_vs_service_conf *u, { int ret = 0; int size; + int cid = 0; struct dp_vs_scheduler *sched = NULL; struct dp_vs_service *svc = NULL; @@ -522,6 +523,11 @@ int dp_vs_add_service(struct dp_vs_service_conf *u, svc->num_laddrs = 0; svc->laddr_curr = &svc->laddr_list; + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + INIT_LIST_HEAD(&svc->pre_list[cid].laddr_list); + svc->pre_list[cid].laddr_curr = &svc->pre_list[cid].laddr_list; + svc->pre_list[cid].num_laddrs = 0; + } INIT_LIST_HEAD(&svc->dests); rte_rwlock_init(&svc->sched_lock); diff --git a/src/netif.c b/src/netif.c index 21e4544e2..2af2fe1e4 100644 --- a/src/netif.c +++ b/src/netif.c @@ -37,6 +37,7 @@ #include "timer.h" #include "parser/parser.h" #include "neigh.h" +#include "sa_pool.h" #include #include @@ -138,6 +139,8 @@ static struct list_head worker_list; /* lcore configurations from cfgfile */ #define NETIF_PORT_TABLE_MASK (NETIF_PORT_TABLE_BUCKETS - 1) static struct list_head port_tab[NETIF_PORT_TABLE_BUCKETS]; /* hashed by id */ static struct list_head port_ntab[NETIF_PORT_TABLE_BUCKETS]; /* hashed by name */ +uint32_t lcore_ids[RTE_MAX_LCORE]; + /* Note: Lockless, NIC can only be registered on initialization stage and * unregistered on cleanup stage */ @@ -1289,6 +1292,7 @@ void netif_get_slave_lcores(uint8_t *nb, uint64_t *mask) while (lcore_conf[i].nports > 0) { slave_lcore_nb++; slave_lcore_mask |= (1L << lcore_conf[i].id); + lcore_ids[i] = lcore_conf[i].id; i++; } @@ -3476,7 +3480,8 @@ int netif_port_start(struct netif_port *port) } // device configure - if ((ret = netif_port_fdir_dstport_mask_set(port)) != EDPVS_OK) + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE && + (ret = netif_port_fdir_dstport_mask_set(port)) != EDPVS_OK) return ret; ret = rte_eth_dev_configure(port->id, port->nrxq, port->ntxq, &port->dev_conf); if (ret < 0 ) { @@ -3768,9 +3773,10 @@ static struct rte_eth_conf default_port_conf = { .dst_ip = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, }, .src_port_mask = 0x0000, + .dst_port_mask = 0x0000, /* to be changed according to slave lcore number in use */ - .dst_port_mask = 0x00F8, + // .dst_port_mask = 0x00F8, .mac_addr_byte_mask = 0x00, .tunnel_type_mask = 0, diff --git a/src/sa_pool.c b/src/sa_pool.c index 35da2fcde..efdef0b1b 100644 --- a/src/sa_pool.c +++ b/src/sa_pool.c @@ -69,6 +69,9 @@ #define SAPOOL_MIN_HASH_SZ 1 #define SAPOOL_MAX_HASH_SZ 128 +#define LPORT_LCORE_MAPPING_POOL_MODE_NAME "lport_lcore_mapping" +#define LADDR_LCORE_MAPPING_POOL_MODE_NAME "laddr_lcore_mapping" + enum { SA_F_USED = 0x01, }; @@ -99,8 +102,8 @@ struct sa_entry_pool { /* another way is use total_used/free_cnt in sa_pool, * so that we need not travels the hash to get stats. * we use cnt here, since we may need per-pool stats. */ - rte_atomic16_t used_cnt; - rte_atomic16_t free_cnt; + rte_atomic32_t used_cnt; + rte_atomic32_t free_cnt; uint32_t miss_cnt; }; @@ -139,8 +142,74 @@ static uint8_t sa_nlcore; static uint64_t sa_lcore_mask; static uint8_t sa_pool_hash_size = SAPOOL_DEF_HASH_SZ; +uint8_t sa_pool_mode = LPORT_LCORE_MAPPING_POOL_MODE; +extern uint32_t lcore_ids[RTE_MAX_LCORE]; -static int __add_del_filter(int af, struct netif_port *dev, lcoreid_t cid, +static int __add_del_filter_addr_mode(int af, struct netif_port *dev, lcoreid_t cid, + const union inet_addr *dip, + uint32_t filter_id[MAX_FDIR_PROTO], bool add) +{ + struct rte_eth_fdir_filter filt = { + .action.behavior = RTE_ETH_FDIR_ACCEPT, + .action.report_status = RTE_ETH_FDIR_REPORT_ID, + .soft_id = filter_id[0], + }; + + if (af == AF_INET) { + filt.input.flow_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER; + filt.input.flow.ip4_flow.dst_ip = dip->in.s_addr; + } else if (af == AF_INET6) { + filt.input.flow_type = RTE_ETH_FLOW_NONFRAG_IPV6_OTHER; + memcpy(filt.input.flow.ipv6_flow.dst_ip, &dip->in6, sizeof(struct in6_addr)); + } else { + return EDPVS_NOTSUPP; + } + + queueid_t queue; + int err; + enum rte_filter_op op; +#ifdef CONFIG_DPVS_SAPOOL_DEBUG + char ipaddr[64]; +#endif + + if (dev->netif_ops && dev->netif_ops->op_filter_supported) { + if (dev->netif_ops->op_filter_supported(dev, RTE_ETH_FILTER_FDIR) < 0) { + if (dev->nrxq <= 1) + return EDPVS_OK; + RTE_LOG(ERR, SAPOOL, "%s: FDIR is not supported by device %s. Only" + " single rxq can be configured.\n", __func__, dev->name); + return EDPVS_NOTSUPP; + } + } else { + RTE_LOG(ERR, SAPOOL, "%s: FDIR support of device %s is not known.\n", + __func__, dev->name); + return EDPVS_INVAL; + } + + err = netif_get_queue(dev, cid, &queue); + if (err != EDPVS_OK) + return err; + + filt.action.rx_queue = queue; + op = add ? RTE_ETH_FILTER_ADD : RTE_ETH_FILTER_DELETE; + + err = netif_fdir_filter_set(dev, op, &filt); + if (err != EDPVS_OK) + return err; + +#ifdef CONFIG_DPVS_SAPOOL_DEBUG + RTE_LOG(DEBUG, SAPOOL, "FDIR: %s %s %s TCP/UDP " + "ip %s queue %d lcore %2d filterID %d\n", + add ? "add" : "del", dev->name, + af == AF_INET ? "IPv4" : "IPv6", + inet_ntop(af, dip, ipaddr, sizeof(ipaddr)) ? : "::", + queue, cid, filter_id[0]); +#endif + + return err; +} + +static int __add_del_filter_port_mode(int af, struct netif_port *dev, lcoreid_t cid, const union inet_addr *dip, __be16 dport, uint32_t filter_id[MAX_FDIR_PROTO], bool add) { @@ -230,6 +299,16 @@ static int __add_del_filter(int af, struct netif_port *dev, lcoreid_t cid, return err; } +static int __add_del_filter(int af, struct netif_port *dev, lcoreid_t cid, + const union inet_addr *dip, __be16 dport, + uint32_t filter_id[MAX_FDIR_PROTO], bool add) +{ + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) + return __add_del_filter_port_mode(af, dev, cid, dip, dport, filter_id, add); + else + return __add_del_filter_addr_mode(af, dev, cid, dip, filter_id, add); +} + static inline int sa_add_filter(int af, struct netif_port *dev, lcoreid_t cid, const union inet_addr *dip, __be16 dport, uint32_t filter_id[MAX_FDIR_PROTO]) @@ -264,13 +343,13 @@ static int sa_pool_alloc_hash(struct sa_pool *ap, uint8_t hash_sz, INIT_LIST_HEAD(&pool->used_enties); INIT_LIST_HEAD(&pool->free_enties); - rte_atomic16_set(&pool->used_cnt, 0); - rte_atomic16_set(&pool->free_cnt, 0); + rte_atomic32_set(&pool->used_cnt, 0); + rte_atomic32_set(&pool->free_cnt, 0); for (port = ap->low; port <= ap->high; port++) { struct sa_entry *sa; - - if (fdir->mask && + if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE && + fdir->mask && ((uint16_t)port & fdir->mask) != ntohs(fdir->port_base)) continue; @@ -278,7 +357,7 @@ static int sa_pool_alloc_hash(struct sa_pool *ap, uint8_t hash_sz, sa->addr = ap->ifa->addr; sa->port = htons((uint16_t)port); list_add_tail(&sa->list, &pool->free_enties); - rte_atomic16_inc(&pool->free_cnt); + rte_atomic32_inc(&pool->free_cnt); } } @@ -292,12 +371,54 @@ static int sa_pool_free_hash(struct sa_pool *ap) return EDPVS_OK; } -int sa_pool_create(struct inet_ifaddr *ifa, uint16_t low, uint16_t high) +static int __sa_pool_create(struct inet_ifaddr *ifa, lcoreid_t cid, + uint16_t low, uint16_t high) { + uint32_t filtids[MAX_FDIR_PROTO]; + struct sa_fdir *fdir = &sa_fdirs[cid]; struct sa_pool *ap; int err; - lcoreid_t cid; + ap = rte_zmalloc(NULL, sizeof(struct sa_pool), 0); + if (!ap) { + err = EDPVS_NOMEM; + goto errout; + } + + ap->ifa = ifa; + ap->low = low; + ap->high = high; + rte_atomic32_set(&ap->refcnt, 0); + err = sa_pool_alloc_hash(ap, sa_pool_hash_size, fdir); + if (err != EDPVS_OK) { + rte_free(ap); + goto errout; + } + + /* if add filter failed, waste some soft-id is acceptable. */ + filtids[0] = fdir->soft_id++; + filtids[1] = fdir->soft_id++; + err = sa_add_filter(ifa->af, ifa->idev->dev, cid, &ifa->addr, + fdir->port_base, filtids); + if (err != EDPVS_OK) { + sa_pool_free_hash(ap); + rte_free(ap); + goto errout; + } + ap->filter_id[0] = filtids[0]; + ap->filter_id[1] = filtids[1]; + + ifa->sa_pools[cid] = ap; + return EDPVS_OK; +errout: + return err; +} + +int sa_pool_create(struct inet_ifaddr *ifa, uint16_t low, uint16_t high) +{ + int err; + lcoreid_t cid; + static unsigned idx = 0; low = low ? : DEF_MIN_PORT; high = high ? : DEF_MAX_PORT; @@ -306,47 +427,25 @@ int sa_pool_create(struct inet_ifaddr *ifa, uint16_t low, uint16_t high) return EDPVS_INVAL; } - for (cid = 0; cid < RTE_MAX_LCORE; cid++) { - uint32_t filtids[MAX_FDIR_PROTO]; - struct sa_fdir *fdir = &sa_fdirs[cid]; - - /* skip master and unused cores */ - if (cid > 64 || !(sa_lcore_mask & (1L << cid))) - continue; - assert(rte_lcore_is_enabled(cid) && cid != rte_get_master_lcore()); - - ap = rte_zmalloc(NULL, sizeof(struct sa_pool), 0); - if (!ap) { - err = EDPVS_NOMEM; - goto errout; - } - - ap->ifa = ifa; - ap->low = low; - ap->high = high; - rte_atomic32_set(&ap->refcnt, 0); + if (SA_POOL_MODE == LADDR_LCORE_MAPPING_POOL_MODE) { + cid = lcore_ids[(idx++) % sa_nlcore]; + err = __sa_pool_create(ifa, cid, low, high); - err = sa_pool_alloc_hash(ap, sa_pool_hash_size, fdir); - if (err != EDPVS_OK) { - rte_free(ap); + if (idx >= sa_nlcore) + idx = 0; + if (err != EDPVS_OK) goto errout; - } - - /* if add filter failed, waste some soft-id is acceptable. */ - filtids[0] = fdir->soft_id++; - filtids[1] = fdir->soft_id++; + } else if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) { + for (cid = 0; cid < RTE_MAX_LCORE; cid++) { + /* skip master and unused cores */ + if (cid > 64 || !(sa_lcore_mask & (1L << cid))) + continue; + assert(rte_lcore_is_enabled(cid) && cid != rte_get_master_lcore()); + err = __sa_pool_create(ifa, cid, low, high); - err = sa_add_filter(ifa->af, ifa->idev->dev, cid, &ifa->addr, - fdir->port_base, filtids); - if (err != EDPVS_OK) { - sa_pool_free_hash(ap); - rte_free(ap); - goto errout; + if (err != EDPVS_OK) + goto errout; } - ap->filter_id[0] = filtids[0]; - ap->filter_id[1] = filtids[1]; - - ifa->sa_pools[cid] = ap; } #ifdef CONFIG_DPVS_SAPOOL_DEBUG @@ -443,8 +542,8 @@ static inline int sa_pool_fetch(struct sa_entry_pool *pool, if (!ent) { #ifdef CONFIG_DPVS_SAPOOL_DEBUG RTE_LOG(DEBUG, SAPOOL, "%s: no entry (used/free %d/%d)\n", __func__, - rte_atomic16_read(&pool->used_cnt), - rte_atomic16_read(&pool->free_cnt)); + rte_atomic32_read(&pool->used_cnt), + rte_atomic32_read(&pool->free_cnt)); #endif pool->miss_cnt++; return EDPVS_RESOURCE; @@ -464,8 +563,8 @@ static inline int sa_pool_fetch(struct sa_entry_pool *pool, ent->flags |= SA_F_USED; list_move_tail(&ent->list, &pool->used_enties); - rte_atomic16_inc(&pool->used_cnt); - rte_atomic16_dec(&pool->free_cnt); + rte_atomic32_inc(&pool->used_cnt); + rte_atomic32_dec(&pool->free_cnt); #ifdef CONFIG_DPVS_SAPOOL_DEBUG RTE_LOG(DEBUG, SAPOOL, "%s: %s:%d fetched!\n", __func__, @@ -515,8 +614,8 @@ static inline int sa_pool_release(struct sa_entry_pool *pool, ent->flags &= (~SA_F_USED); list_move_tail(&ent->list, &pool->free_enties); - rte_atomic16_dec(&pool->used_cnt); - rte_atomic16_inc(&pool->free_cnt); + rte_atomic32_dec(&pool->used_cnt); + rte_atomic32_inc(&pool->free_cnt); #ifdef CONFIG_DPVS_SAPOOL_DEBUG RTE_LOG(DEBUG, SAPOOL, "%s: %s:%d released!\n", __func__, @@ -830,8 +929,8 @@ static int sa_msg_get_stats(struct dpvs_msg *msg) pool = &ifa->this_sa_pool->pool_hash[hash]; assert(pool); - stats->used_cnt += rte_atomic16_read(&pool->used_cnt); - stats->free_cnt += rte_atomic16_read(&pool->free_cnt); + stats->used_cnt += rte_atomic32_read(&pool->used_cnt); + stats->free_cnt += rte_atomic32_read(&pool->free_cnt); stats->miss_cnt += pool->miss_cnt; } @@ -894,6 +993,22 @@ int sa_pool_term(void) /* * config file */ +static void pool_mode_handler(vector_t tokens) +{ + char *str = set_value(tokens); + assert(str); + + if (!strcmp(str, LADDR_LCORE_MAPPING_POOL_MODE_NAME)) + sa_pool_mode = LADDR_LCORE_MAPPING_POOL_MODE; + else if (!strcmp(str, LPORT_LCORE_MAPPING_POOL_MODE_NAME)) + sa_pool_mode = LPORT_LCORE_MAPPING_POOL_MODE; + else + RTE_LOG(WARNING, SAPOOL, "invalid pool_mode %s, use default %s\n", + str, LPORT_LCORE_MAPPING_POOL_MODE_NAME); + + FREE_PTR(str); +} + static void sa_pool_hash_size_conf(vector_t tokens) { char *str = set_value(tokens); @@ -915,5 +1030,6 @@ static void sa_pool_hash_size_conf(vector_t tokens) void install_sa_pool_keywords(void) { install_keyword_root("sa_pool", NULL); + install_keyword("pool_mode", pool_mode_handler, KW_TYPE_INIT); install_keyword("pool_hash_size", sa_pool_hash_size_conf, KW_TYPE_INIT); } From 066bc173dc4436fb32a9faf9362d7638e382da54 Mon Sep 17 00:00:00 2001 From: Deng Kailang Date: Thu, 11 Jul 2019 12:09:04 +0800 Subject: [PATCH 2/4] session_sync: Add session sync to decrease the connection break due to the DPVS node failure in the cluster. 1.there are two synchronization modes: full and incremental. 2.incremental synchronization is used for new session. 3.full synchronization is used for existing session. Co-authored-by: lixiaoxiao lixiaoxiao@360.cn --- conf/dpvs.conf.sample | 16 + include/ipvs/conn.h | 11 + include/ipvs/dest.h | 4 + include/ipvs/service.h | 3 +- include/ipvs/sync.h | 176 +++ include/ipvs/sync_msg.h | 40 + include/netif.h | 1 + include/sa_pool.h | 4 + src/cfgfile.c | 5 +- src/config.mk | 2 + src/ipvs/ip_vs_conn.c | 207 +++- src/ipvs/ip_vs_core.c | 11 +- src/ipvs/ip_vs_dest.c | 16 + src/ipvs/ip_vs_proto_tcp.c | 4 + src/ipvs/ip_vs_service.c | 6 + src/ipvs/ip_vs_sync.c | 1093 +++++++++++++++++ src/ipvs/ip_vs_sync_msg.c | 337 +++++ src/netif.c | 54 +- src/sa_pool.c | 74 +- tools/ipvsadm/ipvsadm.c | 13 +- .../keepalived/keepalived/libipvs-2.6/dp_vs.h | 1 + .../keepalived/libipvs-2.6/libipvs.c | 4 + .../keepalived/libipvs-2.6/libipvs.h | 1 + 23 files changed, 2038 insertions(+), 45 deletions(-) create mode 100644 include/ipvs/sync.h create mode 100644 include/ipvs/sync_msg.h create mode 100644 src/ipvs/ip_vs_sync.c create mode 100644 src/ipvs/ip_vs_sync_msg.c diff --git a/conf/dpvs.conf.sample b/conf/dpvs.conf.sample index 0c0034641..ac7dda987 100644 --- a/conf/dpvs.conf.sample +++ b/conf/dpvs.conf.sample @@ -326,3 +326,19 @@ sa_pool { pool_hash_size 16 pool_mode laddr_lcore_mapping } + +session_sync { + sync_session_enable + sync_session_elapse 2 !secondes elapsed since the connection is established + sync_buff_delay 2 + laddr_ifname dpdk0 + sync_id 10 + + socket { + mcast_addr 224.0.1.100 + mcast_port 8088 + mcast_ttl 20 + mtu 1500 + unicast_port 8089 + } +} diff --git a/include/ipvs/conn.h b/include/ipvs/conn.h index 356c3084e..9270f8d78 100644 --- a/include/ipvs/conn.h +++ b/include/ipvs/conn.h @@ -36,6 +36,7 @@ enum { }; enum { + DPVS_CONN_F_SYNCED = 0x0020, DPVS_CONN_F_HASHED = 0x0040, DPVS_CONN_F_REDIRECT_HASHED = 0x0080, DPVS_CONN_F_INACTIVE = 0x0100, @@ -95,6 +96,11 @@ struct dp_vs_conn { rte_atomic32_t refcnt; struct dpvs_timer timer; struct timeval timeout; + + struct dpvs_timer conn_sync_timer; + struct timeval conn_sync_timeout; + queueid_t qid; /* used in session synchronization*/ + lcoreid_t lcore; struct dp_vs_dest *dest; /* real server */ void *prot_data; /* protocol specific data */ @@ -180,6 +186,11 @@ dp_vs_conn_new(struct rte_mbuf *mbuf, uint32_t flags); int dp_vs_conn_del(struct dp_vs_conn *conn); +struct dp_vs_conn * dp_vs_conn_copy_from_sync(void *sync_conn, + struct dp_vs_dest *dest); + +int dp_vs_conn_lcore_tx(lcoreid_t cid); + struct dp_vs_conn * dp_vs_conn_get(int af, uint16_t proto, const union inet_addr *saddr, diff --git a/include/ipvs/dest.h b/include/ipvs/dest.h index bbcf8eb8f..43afc46b9 100644 --- a/include/ipvs/dest.h +++ b/include/ipvs/dest.h @@ -181,6 +181,10 @@ int dp_vs_new_dest(struct dp_vs_service *svc, struct dp_vs_dest_conf *udest, struct dp_vs_dest *dp_vs_lookup_dest(int af, struct dp_vs_service *svc, const union inet_addr *daddr, uint16_t dport); +struct dp_vs_dest *dp_vs_find_dest(int af, const union inet_addr *daddr, + uint16_t dport, const union inet_addr *vaddr, + uint16_t vport, uint16_t protocol); + struct dp_vs_dest *dp_vs_trash_get_dest(struct dp_vs_service *svc, const union inet_addr *daddr, uint16_t dport); diff --git a/include/ipvs/service.h b/include/ipvs/service.h index 862bea181..27bc2bf25 100644 --- a/include/ipvs/service.h +++ b/include/ipvs/service.h @@ -241,6 +241,7 @@ enum{ DPVS_SO_SET_EDITDEST, DPVS_SO_SET_DELDEST, DPVS_SO_SET_GRATARP, + DPVS_SO_SET_CONN_SYNC, }; enum{ @@ -253,7 +254,7 @@ enum{ #define SOCKOPT_SVC_BASE DPVS_SO_SET_FLUSH -#define SOCKOPT_SVC_SET_CMD_MAX DPVS_SO_SET_GRATARP +#define SOCKOPT_SVC_SET_CMD_MAX DPVS_SO_SET_CONN_SYNC #define SOCKOPT_SVC_GET_CMD_MAX DPVS_SO_GET_DESTS #define MAX_ARG_LEN (sizeof(struct dp_vs_service_user) + \ diff --git a/include/ipvs/sync.h b/include/ipvs/sync.h new file mode 100644 index 000000000..104ebdb2c --- /dev/null +++ b/include/ipvs/sync.h @@ -0,0 +1,176 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2017 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __DPVS_SYNC_H__ +#define __DPVS_SYNC_H__ + +#include "common.h" +#include "list.h" +#include "ipvs/conn.h" + +#define RTE_LOGTYPE_SYNC RTE_LOGTYPE_USER1 + +#define CONN_SYNC_RING_SIZE 2048 + +/* maximum pkt number at a single burst */ +#define CONN_SYNC_MAX_PKT_BURST 32 + +/* + * DPVS sync connection entry + */ +struct dp_vs_sync_conn { + uint8_t reserved; + + int af; + uint8_t proto; + union inet_addr caddr; /* Client address */ + union inet_addr vaddr; /* Virtual address */ + union inet_addr laddr; /* director Local address */ + union inet_addr daddr; /* Destination (RS) address */ + uint16_t cport; + uint16_t vport; + uint16_t lport; + uint16_t dport; + queueid_t qid; + lcoreid_t lcore; + + /* Flags and state transition */ + uint16_t flags; /* status flags */ + uint16_t state; /* state info */ + + /* The sequence options start here */ + struct dp_vs_seq fnat_seq; + uint32_t rs_end_seq; + uint32_t rs_end_ack; +}; + +struct dp_vs_sync_head { + uint8_t type; + uint8_t syncid; + uint16_t size; +}; + +struct dp_vs_sync_mesg { + struct dp_vs_sync_head head; + uint8_t nr_conns; +}; + +struct dp_vs_sync_nego { + struct dp_vs_sync_head head; + uint32_t code; + uint8_t peer_syncid; + uint64_t uptime; +}; + +struct dp_vs_sync_peer { + uint8_t syncid; + uint64_t uptime; + struct sockaddr_in addr; +}; + +struct dp_vs_sync_buff { + struct list_head list; + uint64_t firstuse; + + /* pointers for the message data */ + struct dp_vs_sync_mesg *mesg; + unsigned char *head; + unsigned char *end; +}; + +struct dp_vs_sync_fwd_core { + int cid; + int last_index; + bool start; + bool end; +}; + +struct dp_vs_sync_core { + int core_cnt; + struct dp_vs_sync_fwd_core fwd_core[DPVS_MAX_LCORE]; +}; + +typedef enum { + DP_VS_SYNC_MCAST = 0, + DP_VS_SYNC_UNICAST = 1, + DP_VS_SYNC_MAX = 2, +} dp_vs_sync_type; + +struct dp_vs_sync_conf { + lcoreid_t sync_rx_lcore; + lcoreid_t sync_tx_lcore; + int syncid; + int sync_enable; + int sync_conn_elapse; + int sync_buff_delay; + int sync_per_time_cnt; + int send_mesg_maxlen; + int recv_mesg_maxlen; + char laddr_ifname[IFNAMSIZ]; +}; + +#define DP_VS_SYNC_CONN_SIZE (sizeof(struct dp_vs_sync_conn)) +#define DP_VS_SYNC_MESG_HEADER_LEN (sizeof(struct dp_vs_sync_mesg)) + +#define DP_VS_SYNC_CONN_INFO (0) +#define DP_VS_SYNC_NEGO_INFO (1) + +#define DP_VS_SYNC_INFO_PROBE_CODE (0) +#define DP_VS_SYNC_INFO_REPLY_CODE (1) +#define DP_VS_SYNC_INFO_FETCH_CODE (2) +#define DP_VS_SYNC_INFO_DONE_CODE (3) + +#define DP_VS_SYNC_DELAY_SECONDS (2) +#define DP_VS_SYNC_CONN_CNT_PER_TIME (128) + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +extern struct dp_vs_sync_core g_dp_vs_sync_fwd_core; +#define DP_VS_SYNC_FULL_IS_START(cid) \ + (g_dp_vs_sync_fwd_core.fwd_core[cid].start == true) + +#define DP_VS_SYNC_FULL_IS_END(cid) \ + (g_dp_vs_sync_fwd_core.fwd_core[cid].end == true) + +#define DP_VS_SYNC_FULL_SET_LAST_INDEX(cid, index) \ + (g_dp_vs_sync_fwd_core.fwd_core[cid].last_index = index) + +#define DP_VS_SYNC_FULL_GET_LAST_INDEX(cid) \ + (g_dp_vs_sync_fwd_core.fwd_core[cid].last_index) + +extern struct dp_vs_sync_conf g_dp_vs_sync_conf; +#define DP_VS_SYNC_FULL_CNT_PER_TIME \ + g_dp_vs_sync_conf.sync_per_time_cnt + +void dp_vs_sync_conn_enqueue(struct dp_vs_conn *cp, dp_vs_sync_type type); +int dp_vs_sync_conn_handler(struct dp_vs_conn *conn, int new_state); +int dp_vs_sync_lcore_process_rx_msg(lcoreid_t cid); +int dp_vs_sync_set_rx_core(lcoreid_t cid); +int dp_vs_sync_set_tx_core(lcoreid_t cid); +void dp_vs_sync_run_loop(lcoreid_t cid); +int dp_vs_sync_init(void); +int dp_vs_sync_term(void); + +int dp_vs_sync_recv_nego(const char * buf, int len, + struct sockaddr_in* remote_addr); +int dp_vs_sync_full_end(lcoreid_t cid); +int dp_vs_sync_full_start(lcoreid_t cid); +int dp_vs_sync_conn_start(void); +char* dp_vs_sync_laddr_ifname(void); +void install_session_sync_keywords(void); + +#endif /* __DPVS_SYNC_H__ */ diff --git a/include/ipvs/sync_msg.h b/include/ipvs/sync_msg.h new file mode 100644 index 000000000..63bcab611 --- /dev/null +++ b/include/ipvs/sync_msg.h @@ -0,0 +1,40 @@ +#ifndef __DPVS_SEND_MSG__ +#define __DPVS_SEND_MSG__ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "dpdk.h" + +#define RTE_LOGTYPE_MCAST RTE_LOGTYPE_USER1 +#define RTE_LOGTYPE_UNICAST RTE_LOGTYPE_USER1 + +int create_mcast_receive_sock(void); +int create_mcast_send_sock(void); +int add_mcast_group(int sockfd); +int drop_mcast_group(int sockfd); +int send_mcast_msg(int sockfd, char *buffer, int len); +int receive_mcast_msg(int sockfd, char *buffer, const size_t buflen, + struct sockaddr_in* remote_addr); + +int create_receive_unicast_sock(void); + +int create_send_unicast_sock(void); + +int send_unicast_msg(int sockfd, char *buffer, int len, + struct sockaddr_in* remote_addr); + +int receive_unicast_msg(int sockfd, char *buffer, const size_t buflen, + struct sockaddr_in* remote_addr); +int get_sock_mtu(void); +void install_session_sync_sock_keywords(void); + +#endif /* __DPVS_SEND_MSG__ */ diff --git a/include/netif.h b/include/netif.h index ca3ef1d82..fa880bf36 100644 --- a/include/netif.h +++ b/include/netif.h @@ -366,5 +366,6 @@ static inline char *eth_addr_dump(const struct ether_addr *ea, portid_t netif_port_count(void); void lcore_process_packets(struct netif_queue_conf *qconf, struct rte_mbuf **mbufs, lcoreid_t cid, uint16_t count, bool pkts_from_ring); +lcoreid_t get_lcoreid(queueid_t qid); #endif /* __DPVS_NETIF_H__ */ diff --git a/include/sa_pool.h b/include/sa_pool.h index 65387e191..e2968c209 100644 --- a/include/sa_pool.h +++ b/include/sa_pool.h @@ -77,6 +77,10 @@ int sa_release(const struct netif_port *dev, int sa_pool_stats(const struct inet_ifaddr *ifa, struct sa_pool_stats *stats); +int sa_bind_conn(int af, struct netif_port *dev, lcoreid_t cid, + const union inet_addr *dip, + __be16 dport, queueid_t queue); + /* config file */ void install_sa_pool_keywords(void); diff --git a/src/cfgfile.c b/src/cfgfile.c index 6dea928cd..361424018 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -33,6 +33,7 @@ #include "ipvs/proto_tcp.h" #include "ipvs/proto_udp.h" #include "ipvs/synproxy.h" +#include "ipvs/sync.h" typedef void (*sighandler_t)(int); @@ -86,7 +87,9 @@ static vector_t install_keywords(void) install_sublevel(); install_proto_udp_keywords(); install_sublevel_end(); - +#ifdef CONFIG_DPVS_SYNC + install_session_sync_keywords(); +#endif install_ipv6_keywords(); return g_keywords; diff --git a/src/config.mk b/src/config.mk index 119cdfec0..23d293a1f 100644 --- a/src/config.mk +++ b/src/config.mk @@ -23,6 +23,7 @@ CFLAGS += -D DPVS_MAX_SOCKET=2 CFLAGS += -D DPVS_MAX_LCORE=64 +#CFLAGS += -D CONFIG_DPVS_SYNC #CFLAGS += -D CONFIG_DPVS_NEIGH_DEBUG #CFLAGS += -D CONFIG_RECORD_BIG_LOOP @@ -39,6 +40,7 @@ CFLAGS += -D DPVS_MAX_LCORE=64 #CFLAGS += -D CONFIG_DPVS_IPSET_DEBUG #CFLAGS += -D CONFIG_NDISC_DEBUG #CFLAGS += -D CONFIG_MSG_DEBUG +#CFLAGS += -D CONFIG_DPVS_CONN_SYNC_DEBUG GCC_MAJOR = $(shell echo __GNUC__ | $(CC) -E -x c - | tail -n 1) GCC_MINOR = $(shell echo __GNUC_MINOR__ | $(CC) -E -x c - | tail -n 1) diff --git a/src/ipvs/ip_vs_conn.c b/src/ipvs/ip_vs_conn.c index d89e7dd65..98281bab3 100644 --- a/src/ipvs/ip_vs_conn.c +++ b/src/ipvs/ip_vs_conn.c @@ -31,6 +31,7 @@ #include "ipvs/proto_tcp.h" #include "ipvs/proto_udp.h" #include "ipvs/proto_icmp.h" +#include "ipvs/sync.h" #include "parser/parser.h" #include "ctrl.h" #include "conf/conn.h" @@ -491,10 +492,17 @@ static int conn_expire(void *priv) /* refcnt == 1 means we are the only referer. * no one is using the conn and it's timed out. */ if (rte_atomic32_read(&conn->refcnt) == 1) { - if (conn->flags & DPVS_CONN_F_TEMPLATE) + if (conn->flags & DPVS_CONN_F_TEMPLATE) { dpvs_timer_cancel(&conn->timer, true); - else +#ifdef CONFIG_DPVS_SYNC + dpvs_timer_cancel(&conn->conn_sync_timer, true); +#endif + } else { dpvs_timer_cancel(&conn->timer, false); +#ifdef CONFIG_DPVS_SYNC + dpvs_timer_cancel(&conn->conn_sync_timer, false); +#endif + } /* I was controlled by someone */ if (conn->control) @@ -537,21 +545,24 @@ static int conn_expire(void *priv) } conn_unbind_dest(conn); - dp_vs_laddr_unbind(conn); - - /* free stored ack packet */ - list_for_each_entry_safe(ack_mbuf, t_ack_mbuf, &conn->ack_mbuf, list) { - list_del_init(&ack_mbuf->list); - rte_pktmbuf_free(ack_mbuf->mbuf); - sp_dbg_stats32_dec(sp_ack_saved); - rte_mempool_put(this_ack_mbufpool, ack_mbuf); - } - conn->ack_num = 0; - /* free stored syn mbuf */ - if (conn->syn_mbuf) { - rte_pktmbuf_free(conn->syn_mbuf); - sp_dbg_stats32_dec(sp_syn_saved); + if (!(conn->flags & DPVS_CONN_F_SYNCED)) { + dp_vs_laddr_unbind(conn); + + /* free stored ack packet */ + list_for_each_entry_safe(ack_mbuf, t_ack_mbuf, &conn->ack_mbuf, list) { + list_del_init(&ack_mbuf->list); + rte_pktmbuf_free(ack_mbuf->mbuf); + sp_dbg_stats32_dec(sp_ack_saved); + rte_mempool_put(this_ack_mbufpool, ack_mbuf); + } + conn->ack_num = 0; + + /* free stored syn mbuf */ + if (conn->syn_mbuf) { + rte_pktmbuf_free(conn->syn_mbuf); + sp_dbg_stats32_dec(sp_syn_saved); + } } rte_atomic32_dec(&conn->refcnt); @@ -594,10 +605,17 @@ static void conn_flush(void) list_for_each_entry_safe(tuphash, next, &this_conn_tbl[i], list) { conn = tuplehash_to_conn(tuphash); - if (conn->flags & DPVS_CONN_F_TEMPLATE) + if (conn->flags & DPVS_CONN_F_TEMPLATE) { dpvs_timer_cancel(&conn->timer, true); - else +#ifdef CONFIG_DPVS_SYNC + dpvs_timer_cancel(&conn->conn_sync_timer, true); +#endif + } else { dpvs_timer_cancel(&conn->timer, false); +#ifdef CONFIG_DPVS_SYNC + dpvs_timer_cancel(&conn->conn_sync_timer, false); +#endif + } rte_atomic32_inc(&conn->refcnt); if (rte_atomic32_read(&conn->refcnt) != 2) { @@ -659,6 +677,155 @@ static void conn_flush(void) #endif } +struct dp_vs_conn * dp_vs_conn_copy_from_sync(void *param, struct dp_vs_dest *dest) +{ + struct dp_vs_conn *new; + struct conn_tuple_hash *t; + uint32_t err; + struct dp_vs_proto *pp; + struct netif_port *dev = NULL; + struct dp_vs_sync_conn *sync_conn = (struct dp_vs_sync_conn *)param; + + if (unlikely(rte_mempool_get(this_conn_cache, (void **)&new) != 0)) { + RTE_LOG(WARNING, IPVS, "%s: no memory\n", __func__); + return NULL; + } + memset(new, 0, sizeof(struct dp_vs_conn)); + new->connpool = this_conn_cache; + + /* init inbound conn tuple hash */ + t = &tuplehash_in(new); + t->direct = DPVS_CONN_DIR_INBOUND; + t->af = sync_conn->af; + t->proto = sync_conn->proto; + t->saddr = sync_conn->caddr; + t->sport = sync_conn->cport; + t->daddr = sync_conn->vaddr; + t->dport = sync_conn->vport; + INIT_LIST_HEAD(&t->list); + + /* init outbound conn tuple hash */ + t = &tuplehash_out(new); + t->direct = DPVS_CONN_DIR_OUTBOUND; + t->af = sync_conn->af; + t->proto = sync_conn->proto; + t->saddr = sync_conn->daddr; + t->sport = sync_conn->dport; + t->daddr = sync_conn->laddr; + t->dport = sync_conn->lport; + INIT_LIST_HEAD(&t->list); + + /* init connection */ + new->af = sync_conn->af; + new->proto = sync_conn->proto; + new->caddr = sync_conn->caddr; + new->cport = sync_conn->cport; + new->vaddr = sync_conn->vaddr; + new->vport = sync_conn->vport; + new->laddr = sync_conn->laddr; + new->lport = sync_conn->lport; + new->daddr = sync_conn->daddr; + new->dport = sync_conn->dport; + + /* L2 fast xmit */ + new->in_dev = NULL; + new->out_dev = NULL; + + /* Controll member */ + new->control = NULL; + rte_atomic32_clear(&new->n_control); + + /* caller will use it right after created, + * just like dp_vs_conn_get(). */ + rte_atomic32_set(&new->refcnt, 1); + + rte_memcpy(&new->fnat_seq, &sync_conn->fnat_seq, sizeof(struct dp_vs_seq)); + new->rs_end_seq = sync_conn->rs_end_seq; + new->rs_end_ack = sync_conn->rs_end_ack; + + new->flags = sync_conn->flags | DPVS_CONN_F_SYNCED; + new->state = sync_conn->state; + new->qid = sync_conn->qid; + new->lcore = rte_lcore_id(); + + dev = netif_port_get_by_name(dp_vs_sync_laddr_ifname()); + if (!dev) { + RTE_LOG(ERR, IPVS, "%s: dpdk device not found\n", __func__); + return EDPVS_OK; + } + + sa_bind_conn(new->af, dev, new->lcore, &new->laddr, 0, new->qid); + + /* bind destination and corresponding trasmitter */ + err = conn_bind_dest(new, dest); + if (err != EDPVS_OK) { + RTE_LOG(WARNING, IPVS, "%s: fail to bind dest: %s\n", __func__, + dpvs_strerror(err)); + goto errout; + } + + if ((err = dp_vs_conn_hash(new)) != EDPVS_OK) + goto errout; + + /* timer */ + pp = dp_vs_proto_lookup(sync_conn->proto); + if (pp && pp->timeout_table) + new->timeout.tv_sec = pp->timeout_table[sync_conn->state]; + else + new->timeout.tv_sec = 60; + + new->timeout.tv_usec = 0; + + this_conn_count++; + + /* schedule conn timer */ + dpvs_time_rand_delay(&new->timeout, 1000000); + if (new->flags & DPVS_CONN_F_TEMPLATE) { + dpvs_timer_sched(&new->timer, &new->timeout, conn_expire, new, true); + } else { + dpvs_timer_sched(&new->timer, &new->timeout, conn_expire, new, false); + } + +#ifdef CONFIG_DPVS_IPVS_DEBUG + conn_dump("sync conn: ", new); +#endif + return new; + +errout: + rte_mempool_put(this_conn_cache, new); + return NULL; +} + +int dp_vs_conn_lcore_tx(lcoreid_t cid) +{ + int i = 0; + int cnt = 0; + struct conn_tuple_hash *tuphash; + struct dp_vs_conn *conn; + + if (!DP_VS_SYNC_FULL_IS_START(cid)) { + return EDPVS_OK; + } + + i = DP_VS_SYNC_FULL_GET_LAST_INDEX(cid); + for (; i < DPVS_CONN_TBL_SIZE && cnt <= DP_VS_SYNC_FULL_CNT_PER_TIME; i++, cnt++) { + list_for_each_entry(tuphash, &this_conn_tbl[i], list) { + if (tuphash->direct != DPVS_CONN_DIR_INBOUND) + continue; + conn = tuplehash_to_conn(tuphash); + if (DPVS_TCP_S_ESTABLISHED == conn->state) + dp_vs_sync_conn_enqueue(conn, DP_VS_SYNC_UNICAST); + } + } + + DP_VS_SYNC_FULL_SET_LAST_INDEX(cid, i); + + if (i >= DPVS_CONN_TBL_SIZE) + dp_vs_sync_full_end(cid); + + return EDPVS_OK; +} + struct dp_vs_conn *dp_vs_conn_new(struct rte_mbuf *mbuf, const struct dp_vs_iphdr *iph, struct dp_vs_conn_param *param, @@ -762,6 +929,10 @@ struct dp_vs_conn *dp_vs_conn_new(struct rte_mbuf *mbuf, rte_atomic32_set(&new->refcnt, 1); new->flags = flags; new->state = 0; +#ifdef CONFIG_DPVS_SYNC + new->lcore = rte_lcore_id(); + new->qid = mbuf->hash.usr; +#endif #ifdef CONFIG_DPVS_IPVS_STATS_DEBUG new->ctime = rte_rdtsc(); #endif diff --git a/src/ipvs/ip_vs_core.c b/src/ipvs/ip_vs_core.c index 4b0547acd..7ba0eae2a 100644 --- a/src/ipvs/ip_vs_core.c +++ b/src/ipvs/ip_vs_core.c @@ -35,6 +35,7 @@ #include "ipvs/synproxy.h" #include "ipvs/blklst.h" #include "ipvs/proto_udp.h" +#include "ipvs/sync.h" #include "route6.h" #include "ipvs/redirect.h" @@ -1198,15 +1199,23 @@ int dp_vs_init(void) goto err_stats; } + err = dp_vs_sync_init(); + if (err != EDPVS_OK) { + RTE_LOG(ERR, IPVS, "fail to init stats: %s\n", dpvs_strerror(err)); + goto err_hooks; + } + err = inet_register_hooks(dp_vs_ops, NELEMS(dp_vs_ops)); if (err != EDPVS_OK) { RTE_LOG(ERR, IPVS, "fail to register hooks: %s\n", dpvs_strerror(err)); - goto err_hooks; + goto err_sync; } RTE_LOG(DEBUG, IPVS, "ipvs inialized.\n"); return EDPVS_OK; +err_sync: + dp_vs_sync_term(); err_hooks: dp_vs_stats_term(); err_stats: diff --git a/src/ipvs/ip_vs_dest.c b/src/ipvs/ip_vs_dest.c index 92697a7d9..afae4199f 100644 --- a/src/ipvs/ip_vs_dest.c +++ b/src/ipvs/ip_vs_dest.c @@ -47,6 +47,22 @@ struct dp_vs_dest *dp_vs_lookup_dest(int af, return NULL; } +struct dp_vs_dest *dp_vs_find_dest(int af, const union inet_addr *daddr, + uint16_t dport, const union inet_addr *vaddr, + uint16_t vport, uint16_t protocol) +{ + struct dp_vs_dest *dest; + struct dp_vs_service *svc; + svc = dp_vs_service_lookup(af, protocol, vaddr, vport, 0, NULL, NULL, NULL); + if(!svc) + return NULL; + dest = dp_vs_lookup_dest(af, svc, daddr, dport); + if(dest) + rte_atomic32_inc(&dest->refcnt); + dp_vs_service_put(svc); + return dest; +} + /* * Lookup dest by {svc,addr,port} in the destination trash. * The destination trash is used to hold the destinations that are removed diff --git a/src/ipvs/ip_vs_proto_tcp.c b/src/ipvs/ip_vs_proto_tcp.c index 27c04476c..fe333e18d 100644 --- a/src/ipvs/ip_vs_proto_tcp.c +++ b/src/ipvs/ip_vs_proto_tcp.c @@ -37,6 +37,7 @@ #include #include #include "ipvs/redirect.h" +#include "ipvs/sync.h" static int g_defence_tcp_drop = 0; @@ -923,6 +924,9 @@ static int tcp_state_trans(struct dp_vs_proto *proto, struct dp_vs_conn *conn, } } +#ifdef CONFIG_DPVS_SYNC + dp_vs_sync_conn_handler(conn, new_state); +#endif return EDPVS_OK; } diff --git a/src/ipvs/ip_vs_service.c b/src/ipvs/ip_vs_service.c index f30b48428..16b55089c 100644 --- a/src/ipvs/ip_vs_service.c +++ b/src/ipvs/ip_vs_service.c @@ -25,6 +25,7 @@ #include "ipvs/sched.h" #include "ipvs/laddr.h" #include "ipvs/blklst.h" +#include "ipvs/sync.h" #include "ctrl.h" #include "route.h" #include "route6.h" @@ -950,6 +951,11 @@ static int dp_vs_set_svc(sockoptid_t opt, const void *user, size_t len) if (opt == DPVS_SO_SET_FLUSH) return dp_vs_flush(); +#ifdef CONFIG_DPVS_SYNC + if (opt == DPVS_SO_SET_CONN_SYNC) + return dp_vs_sync_conn_start(); +#endif + memcpy(arg, user, len); usvc_compat = (struct dp_vs_service_user *)arg; udest_compat = (struct dp_vs_dest_user *)(usvc_compat + 1); diff --git a/src/ipvs/ip_vs_sync.c b/src/ipvs/ip_vs_sync.c new file mode 100644 index 000000000..6c8569ae6 --- /dev/null +++ b/src/ipvs/ip_vs_sync.c @@ -0,0 +1,1093 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2017 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include "netif.h" +#include "netif.h" +#include "ipvs/sync_msg.h" +#include "ipvs/sync.h" +#include "ipvs/proto.h" +#include "ipvs/proto_tcp.h" +#include "parser/parser.h" + +/* the sync_buff list head */ +struct list_head dp_vs_sync_queue[DP_VS_SYNC_MAX]; + +/* current sync_buff for accepting new conn entries */ +struct dp_vs_sync_buff *dp_vs_sync_curr_buff[DP_VS_SYNC_MAX]; + +struct dp_vs_sync_core g_dp_vs_sync_fwd_core; +struct dp_vs_sync_conf g_dp_vs_sync_conf; +static uint64_t cycles_per_sec = 0; + +struct rte_ring *g_dp_vs_sync_tx_ring[DP_VS_SYNC_MAX]; +struct rte_ring *g_dp_vs_sync_rx_ring[DPVS_MAX_LCORE]; +int g_dp_vs_sync_send_fd[DP_VS_SYNC_MAX]; + +uint64_t g_start_cycles; +uint8_t g_req_timeout; +struct dpvs_timer g_req_timer; + +struct dp_vs_sync_peer g_dp_vs_sync_fetch; +struct dp_vs_sync_peer g_dp_vs_sync_request; + +static inline void dp_vs_sync_msg_dump(const char* info, int issend, + struct dp_vs_sync_head* head) +{ + RTE_LOG(INFO, SYNC, "%s(syncid:%d %s %d) %s size %d\n", + info ? info : "", + g_dp_vs_sync_conf.syncid, issend ? "->" : "<-", head->syncid, + head->type == DP_VS_SYNC_NEGO_INFO ? "nego info" : "conn sync", + head->size); +} + +static inline void dp_vs_sync_conn_dump(const char *msg, struct dp_vs_sync_conn *conn) +{ + char cbuf[64], vbuf[64], lbuf[64], dbuf[64]; + const char *caddr, *vaddr, *laddr, *daddr; + + caddr = inet_ntop(conn->af, &conn->caddr, cbuf, sizeof(cbuf)) ? cbuf : "::"; + vaddr = inet_ntop(conn->af, &conn->vaddr, vbuf, sizeof(vbuf)) ? vbuf : "::"; + laddr = inet_ntop(conn->af, &conn->laddr, lbuf, sizeof(lbuf)) ? lbuf : "::"; + daddr = inet_ntop(conn->af, &conn->daddr, dbuf, sizeof(dbuf)) ? dbuf : "::"; + + RTE_LOG(INFO, SYNC, "%s [%d] %s %s:%u %s:%u %s:%u %s:%u from lcore: %u\n", + msg ? msg : "", rte_lcore_id(), inet_proto_name(conn->proto), + caddr, ntohs(conn->cport), vaddr, ntohs(conn->vport), + laddr, ntohs(conn->lport), daddr, ntohs(conn->dport), conn->lcore); +} + +/* + * Add an dp_vs_conn into the session sync tx ring. + * */ +void dp_vs_sync_conn_enqueue(struct dp_vs_conn *cp, dp_vs_sync_type type) +{ + struct dp_vs_sync_conn *s; + int ret; + + if (!(s=rte_zmalloc(NULL, + sizeof(struct dp_vs_sync_conn), + RTE_CACHE_LINE_SIZE))) { + RTE_LOG(WARNING, SYNC, "%s: no memory for a new dp_vs_sync_conn.\n", __func__); + return; + } + + /* copy members */ + s->af = cp->af; + s->proto = cp->proto; + s->cport = cp->cport; + s->vport = cp->vport; + s->lport = cp->lport; + s->dport = cp->dport; + + s->caddr = cp->caddr; + s->vaddr = cp->vaddr; + s->laddr = cp->laddr; + s->daddr = cp->daddr; + s->qid = cp->qid; + s->lcore = cp->lcore; + + rte_memcpy(&s->fnat_seq, &cp->fnat_seq, sizeof(struct dp_vs_seq)); + s->rs_end_seq = cp->rs_end_seq; + s->rs_end_ack = cp->rs_end_ack; + + s->flags = cp->flags & ~DPVS_CONN_F_HASHED; + s->state = cp->state; + + ret = rte_ring_enqueue(g_dp_vs_sync_tx_ring[type], s); + if (ret) { + if (-ENOBUFS == ret) + RTE_LOG(WARNING, SYNC, "%s: session %s sync tx ring quota exceeded.\n", + __func__, (DP_VS_SYNC_UNICAST== type) ? "unicat" : "mcast"); + else + RTE_LOG(WARNING, SYNC, "%s: session %s sync tx ring enqueue failed. ret = %d\n", + __func__, (DP_VS_SYNC_UNICAST == type) ? "unicast" : "mcast", ret); + rte_free(s); + } else +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(INFO, SYNC, "%s: session %s sync tx ring enqueue successed.\n", + __func__, (DP_VS_SYNC_UNICAST == type) ? "unicat" : "mcast"); +#endif + return; +} + +static void dp_vs_sync_buff_release(struct dp_vs_sync_buff *sb) +{ + rte_free(sb->mesg); + rte_free(sb); +} + +static inline struct dp_vs_sync_buff * dp_vs_sync_buff_create(void) +{ + struct dp_vs_sync_buff *sb; + + if (!(sb = rte_zmalloc(NULL, + sizeof(struct dp_vs_sync_buff), + RTE_CACHE_LINE_SIZE))) + return NULL; + + if (!(sb->mesg = rte_zmalloc(NULL, + g_dp_vs_sync_conf.send_mesg_maxlen, + RTE_CACHE_LINE_SIZE))) { + rte_free(sb); + return NULL; + } + + sb->mesg->head.syncid = g_dp_vs_sync_conf.syncid; + sb->mesg->head.type = DP_VS_SYNC_CONN_INFO; + sb->mesg->head.size = DP_VS_SYNC_MESG_HEADER_LEN; + sb->mesg->nr_conns = 0; + sb->head = (unsigned char *)sb->mesg + DP_VS_SYNC_MESG_HEADER_LEN; + sb->end = (unsigned char *)sb->mesg + g_dp_vs_sync_conf.send_mesg_maxlen; + sb->firstuse = rte_get_timer_cycles(); + + return sb; +} + +static inline void dp_vs_sync_buff_enqueue(struct dp_vs_sync_buff *sb, + dp_vs_sync_type type) +{ + list_add_tail(&sb->list, &dp_vs_sync_queue[type]); +} + +static struct dp_vs_sync_buff * dp_vs_sync_buff_dequeue(dp_vs_sync_type type) +{ + struct dp_vs_sync_buff *sb; + + if (list_empty(&dp_vs_sync_queue[type])) { + sb = NULL; + } else { + sb = list_entry(dp_vs_sync_queue[type].next, struct dp_vs_sync_buff, list); + list_del(&sb->list); + } + + return sb; +} + +static void dp_vs_sync_conn_append_to_buff(struct dp_vs_sync_conn *sync_conn, + dp_vs_sync_type type) +{ + struct dp_vs_sync_mesg *m; + int len = sizeof(struct dp_vs_sync_conn); + +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + dp_vs_sync_conn_dump("sync conn append to buff", sync_conn); +#endif + + if (!dp_vs_sync_curr_buff[type]) { + if (!(dp_vs_sync_curr_buff[type] = dp_vs_sync_buff_create())) { + RTE_LOG(INFO, SYNC, "%s: create sync buffer failed.\n", __func__); + return; + } + } + + m = dp_vs_sync_curr_buff[type]->mesg; + rte_memcpy(dp_vs_sync_curr_buff[type]->head, sync_conn, len); + m->nr_conns++; + m->head.size += len; + dp_vs_sync_curr_buff[type]->head += len; + + /** + * if curr_sync_buff has no space for next one + * then append it to dp_vs_sync_queue + */ + if (dp_vs_sync_curr_buff[type]->head + len > dp_vs_sync_curr_buff[type]->end) { + dp_vs_sync_buff_enqueue(dp_vs_sync_curr_buff[type], type); + dp_vs_sync_curr_buff[type] = NULL; + } + + return; +} + +/** + * get curr_sync_buff if it has been created for more + * than g_sync_buff_delay seconds + */ +static struct dp_vs_sync_buff * +dp_vs_sync_expired_curr_sync_buff(dp_vs_sync_type type) +{ + struct dp_vs_sync_buff *sb = NULL; + uint64_t time_diff = 0; + + if (NULL == dp_vs_sync_curr_buff[type]) + return NULL; + + time_diff = rte_get_timer_cycles() - dp_vs_sync_curr_buff[type]->firstuse; + if(time_diff >= g_dp_vs_sync_conf.sync_buff_delay * cycles_per_sec) { + sb = dp_vs_sync_curr_buff[type]; + dp_vs_sync_curr_buff[type] = NULL; + } + + return sb; +} + +static int dp_vs_sync_process_conn(const char *buffer, const int buflen) +{ + struct dp_vs_sync_mesg *m = (struct dp_vs_sync_mesg *)buffer; + struct dp_vs_sync_conn *s = NULL; + char *p = NULL; + int i, res; + + p = (char *)(buffer + sizeof(struct dp_vs_sync_mesg)); + + for (i = 0; i < m->nr_conns; i++) { + if (!(s = rte_zmalloc(NULL, sizeof(struct dp_vs_sync_conn), RTE_CACHE_LINE_SIZE))) { + RTE_LOG(ERR, SYNC, "%s: alloc sync conn node failed\n", __func__); + return EDPVS_NOMEM; + } + + rte_memcpy(s, p, sizeof(struct dp_vs_sync_conn)); + res = rte_ring_enqueue(g_dp_vs_sync_rx_ring[get_lcoreid(s->qid)], s); + if (res) { + if (unlikely(-EDQUOT == res)) { + RTE_LOG(WARNING, SYNC, "%s: session sync rx ring of lcore %d quota exceeded\n", + __func__, get_lcoreid(s->qid)); + } else if (res < 0) { + RTE_LOG(WARNING, SYNC, "%s: session sync rx ring of lcore %d enqueue failed\n", + __func__, get_lcoreid(s->qid)); + } + rte_free(s); + } + + p += sizeof(struct dp_vs_sync_conn); +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(DEBUG, SYNC, "%s: current conn %d(total conn %d) from core %d queue %d enqueue rx rings[%d] %s\n", + __func__, i + 1, m->nr_conns, s->lcore, s->qid, get_lcoreid(s->qid), + res ? "failed" : "succeed"); +#endif + } + + return EDPVS_OK; +} + +static int dp_vs_sync_send_msg(int type, char* msg, int len, + struct sockaddr_in* addr) +{ + if (DP_VS_SYNC_UNICAST== type) { + return send_unicast_msg(g_dp_vs_sync_send_fd[type], + msg, len, addr); + } else { + return send_mcast_msg(g_dp_vs_sync_send_fd[type], msg, len); + } +} + +/** + * Process received multicast message and create the corresponding + * dp_vs_conn entries. + */ +static int dp_vs_sync_process_rx_msg(const char *buffer, const int buflen, + struct sockaddr_in* remote_addr) +{ + struct dp_vs_sync_head *head = (struct dp_vs_sync_head *)buffer; + +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + dp_vs_sync_msg_dump("recv", 0, head); +#endif + + if (buflen != head->size) { + RTE_LOG(ERR, SYNC, "%s: recv conn sync message, buflen = %u, m->size = %d\n", + __func__, buflen, head->size); + return EDPVS_INVPKT; + } + + /* syncid sanity check, ignore message sent from itself */ + if (head->syncid == g_dp_vs_sync_conf.syncid) { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(DEBUG, SYNC, "%s: ignoring incoming msg with syncid = %d\n", + __func__, head->syncid); +#endif + return EDPVS_OK; + } + + if (DP_VS_SYNC_NEGO_INFO == head->type) { + dp_vs_sync_recv_nego(buffer, buflen, remote_addr); + } else { + dp_vs_sync_process_conn(buffer, buflen); + } + + return EDPVS_OK; +} + +static int dp_vs_sync_tx_loop(dp_vs_sync_type type) +{ + uint16_t nb_rb = 0; + uint16_t index = 0; + struct dp_vs_sync_conn *conns[NETIF_MAX_PKT_BURST]; + struct dp_vs_sync_conn *conn; + struct dp_vs_sync_buff *sb; + + nb_rb = rte_ring_dequeue_burst(g_dp_vs_sync_tx_ring[type], (void **)conns, + NETIF_MAX_PKT_BURST, NULL); + for (index = 0; index < nb_rb; index++) { + conn = conns[index]; + dp_vs_sync_conn_append_to_buff(conn, type); + rte_free(conn); + } + + while ((sb = dp_vs_sync_buff_dequeue(type))) { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + dp_vs_sync_msg_dump("send sync dequeue conn", 1, &(sb->mesg->head)); +#endif + dp_vs_sync_send_msg(type, (char *)sb->mesg, sb->mesg->head.size, + &g_dp_vs_sync_request.addr); + dp_vs_sync_buff_release(sb); + } + + if ((sb = dp_vs_sync_expired_curr_sync_buff(type))) { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + dp_vs_sync_msg_dump("send curr sync buff conn", 1, &(sb->mesg->head)); +#endif + dp_vs_sync_send_msg(type, (char *)sb->mesg, sb->mesg->head.size, + &g_dp_vs_sync_request.addr); + dp_vs_sync_buff_release(sb); + } + + return EDPVS_OK; +} + +static int dp_vs_session_sync_tx_loop(void) +{ + int index = 0; + if (!g_dp_vs_sync_conf.sync_enable) + return EDPVS_OK; + + if (!cycles_per_sec) + cycles_per_sec = rte_get_timer_hz(); + + g_dp_vs_sync_send_fd[DP_VS_SYNC_MCAST] = create_mcast_send_sock(); + if (g_dp_vs_sync_send_fd[DP_VS_SYNC_MCAST] == -1) { + RTE_LOG(ERR, SYNC, "%s: failed to create mcast send sock.\n", __func__); + return -1; + } + + g_dp_vs_sync_send_fd[DP_VS_SYNC_UNICAST] = create_send_unicast_sock(); + if (g_dp_vs_sync_send_fd[DP_VS_SYNC_UNICAST] == -1) { + RTE_LOG(ERR, SYNC, "%s: failed to create unicast send sock.\n", __func__); + return -1; + } + + for(;;) { + for (index = 0; index < DP_VS_SYNC_MAX; index++) { + dp_vs_sync_tx_loop(index); + } + } + + return 0; +} + +static int dp_vs_sync_unicast_rx_loop(int sockfd, char *buffer, const size_t buflen) +{ + int len = 0; + struct sockaddr_in remote_addr; + memset(buffer, 0, buflen); + + len = receive_unicast_msg(sockfd, buffer, buflen, &remote_addr); + if (len <= 0) { + return EDPVS_OK; + } + dp_vs_sync_process_rx_msg(buffer, len, &remote_addr); + + return EDPVS_OK; +} + +static int dp_vs_sync_mcast_rx_loop(int sockfd, char *buffer, const size_t buflen) +{ + int len = 0; + struct sockaddr_in remote_addr; + memset(buffer, 0, buflen); + + len = receive_mcast_msg(sockfd, buffer, buflen, &remote_addr); + if (len <= 0) { + return EDPVS_OK; + } + dp_vs_sync_process_rx_msg(buffer, len, &remote_addr); + + return EDPVS_OK; +} + +static int dp_vs_session_sync_rx_loop(void) +{ + int res = 0; + int unicast_fd = 0; + int mcast_fd = 0; + char* buff = NULL; + fd_set fdsr; + int maxsock; + struct timeval tv; + int ret = 0; + + if (!g_dp_vs_sync_conf.sync_enable) + return EDPVS_OK; + + mcast_fd = create_mcast_receive_sock(); + if (mcast_fd == -1) { + RTE_LOG(ERR, SYNC, "%s: failed to create receive sock.\n", __func__); + return -1; + } + + res = add_mcast_group(mcast_fd); + if (res < 0) { + RTE_LOG(ERR, SYNC, "%s: failed to add multicast group.\n", __func__); + return -1; + } + + unicast_fd = create_receive_unicast_sock(); + if (!(buff = rte_zmalloc(NULL, + g_dp_vs_sync_conf.recv_mesg_maxlen, + RTE_CACHE_LINE_SIZE))) { + RTE_LOG(ERR, SYNC, "%s: alloc sync recv buffer failed\n", __func__); + return EDPVS_NOMEM; + } + + maxsock = MAX(unicast_fd, mcast_fd); + for (;;) { + FD_ZERO(&fdsr); + FD_SET(mcast_fd,&fdsr); + FD_SET(unicast_fd, &fdsr); + tv.tv_sec = 0; + tv.tv_usec = 0; + ret = select(maxsock +1, &fdsr, NULL, NULL, &tv); + if (ret < 0) { + RTE_LOG(ERR, SYNC, "%s: select error ret %d\n", __func__, ret); + } else if (ret == 0) { + continue; + } + + if (FD_ISSET(mcast_fd, & fdsr)) { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(INFO, SYNC, "%s: mcastfd ready\n", __func__); +#endif + dp_vs_sync_mcast_rx_loop(mcast_fd, buff, g_dp_vs_sync_conf.recv_mesg_maxlen); + } + + if (FD_ISSET(unicast_fd, &fdsr)) { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(INFO, SYNC, "%s: unicastfd ready\n", __func__); +#endif + dp_vs_sync_unicast_rx_loop(unicast_fd, buff, g_dp_vs_sync_conf.recv_mesg_maxlen); + } + } + + rte_free(buff); + return 0; +} + +int dp_vs_sync_lcore_process_rx_msg(lcoreid_t cid) +{ + uint16_t idx = 0; + uint16_t nb_rb = 0; + struct dp_vs_sync_conn *conns[CONN_SYNC_MAX_PKT_BURST]; + struct dp_vs_sync_conn *conn = NULL; + struct dp_vs_proto *pp = NULL; + struct dp_vs_conn *cp = NULL; + struct dp_vs_dest *dest = NULL; + +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + char sbuf[64], dbuf[64]; +#endif + + if (!g_dp_vs_sync_conf.sync_enable) + return EDPVS_OK; + + nb_rb = rte_ring_dequeue_burst(g_dp_vs_sync_rx_ring[cid], (void **)conns, + CONN_SYNC_MAX_PKT_BURST, NULL); + for (idx = 0; idx < nb_rb; idx++) { + conn = conns[idx]; + cp = dp_vs_conn_get(conn->af, + conn->proto, + &conn->caddr, + &conn->vaddr, + conn->cport, + conn->vport, + NULL /*direct*/, + false); +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(DEBUG, SYNC, "conn lookup: [%d] %s %s/%d -> %s/%d %s\n", + rte_lcore_id(), inet_proto_name(conn->proto), + inet_ntop(conn->af, &(conn->caddr), sbuf, sizeof(sbuf)) ? sbuf : "::", ntohs(conn->cport), + inet_ntop(conn->af, &(conn->vaddr), dbuf, sizeof(dbuf)) ? dbuf : "::", ntohs(conn->vport), + cp ? "already exits" : "does not exits"); +#endif + if (!cp) { + dest = dp_vs_find_dest(conn->af, + &conn->daddr, + conn->dport, + &conn->vaddr, + conn->vport, + conn->proto); + if (!dest) { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(DEBUG, SYNC, "dest lookup: [%d] %s %s/%d -> %s/%d %s\n", + rte_lcore_id(), inet_proto_name(conn->proto), + inet_ntop(conn->af, &(conn->vaddr), dbuf, sizeof(dbuf)) ? dbuf : "::", ntohs(conn->vport), + inet_ntop(conn->af, &(conn->daddr), sbuf, sizeof(sbuf)) ? sbuf : "::", ntohs(conn->dport), + "does not exits"); +#endif + continue; + } + cp = dp_vs_conn_copy_from_sync(conn, dest); + } else { /* connection is already exists. change the state */ +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(DEBUG, SYNC, "conn lookup: [%d] %s %s/%d -> %s/%d state %d->%d\n", + rte_lcore_id(), inet_proto_name(conn->proto), + inet_ntop(conn->af, &(conn->caddr), sbuf, sizeof(sbuf)) ? sbuf : "::", ntohs(conn->cport), + inet_ntop(conn->af, &(conn->vaddr), dbuf, sizeof(dbuf)) ? dbuf : "::", ntohs(conn->vport), + cp->state, conn->state); +#endif + cp->state = conn->state; + pp = dp_vs_proto_lookup(cp->proto); + if (pp && pp->timeout_table) + cp->timeout.tv_sec = pp->timeout_table[cp->state]; + else + cp->timeout.tv_sec = 60; + + cp->timeout.tv_usec = 0; + if (cp->flags & DPVS_CONN_F_TEMPLATE) + dpvs_timer_update(&cp->timer, &cp->timeout, true); + else + dpvs_timer_update(&cp->timer, &cp->timeout, false); + } + + dp_vs_conn_put_no_reset(cp); + rte_free(conn); + } + + return EDPVS_OK; +} + +static int dp_vs_sync_conn_expire(void *priv) +{ + struct dp_vs_conn *conn = priv; + assert(conn); + +#ifdef CONFIG_DPVS_SYNC_DEBUG + char sbuf[64], dbuf[64]; + dp_vs_sync_conn_dump("sync conn expire", conn); +#endif + + if (conn->flags & DPVS_CONN_F_TEMPLATE) { + dpvs_timer_cancel(&conn->conn_sync_timer, true); + } else { + dpvs_timer_cancel(&conn->conn_sync_timer, false); + } + + dp_vs_sync_conn_enqueue(conn, DP_VS_SYNC_MCAST); + return DTIMER_STOP; +} + +int dp_vs_sync_conn_handler(struct dp_vs_conn *conn, int new_state) +{ + bool global = false; + if (!g_dp_vs_sync_conf.sync_enable) + return EDPVS_OK; + + if (conn->flags & DPVS_CONN_F_TEMPLATE) { + global = true; + } + + dpvs_timer_cancel(&conn->conn_sync_timer, global); + + if (new_state == DPVS_TCP_S_ESTABLISHED) { + conn->conn_sync_timeout.tv_sec = g_dp_vs_sync_conf.sync_conn_elapse; + conn->conn_sync_timeout.tv_usec = 0; + dpvs_time_rand_delay(&conn->conn_sync_timeout, 1000000); + if (conn->flags & DPVS_CONN_F_TEMPLATE) { + dpvs_timer_sched(&conn->conn_sync_timer, &conn->conn_sync_timeout, + dp_vs_sync_conn_expire, conn, true); + } else { + dpvs_timer_sched(&conn->conn_sync_timer, &conn->conn_sync_timeout, + dp_vs_sync_conn_expire, conn, false); + } + } else if ( new_state == DPVS_TCP_S_FIN_WAIT || + new_state == DPVS_TCP_S_TIME_WAIT || + new_state == DPVS_TCP_S_CLOSE || + new_state == DPVS_TCP_S_CLOSE_WAIT || + new_state == DPVS_TCP_S_LAST_ACK) { + dp_vs_sync_conn_enqueue(conn, DP_VS_SYNC_MCAST); + } + + return EDPVS_OK; +} + +int dp_vs_sync_set_rx_core(lcoreid_t cid) +{ + g_dp_vs_sync_conf.sync_rx_lcore = cid; + RTE_LOG(INFO, SYNC, "%s: conn sync receive core id %d.\n", + __func__, g_dp_vs_sync_conf.sync_rx_lcore); + return EDPVS_OK; +} + +int dp_vs_sync_set_tx_core(lcoreid_t cid) +{ + g_dp_vs_sync_conf.sync_tx_lcore = cid; + RTE_LOG(INFO, SYNC, "%s: conn sync send core id %d.\n", + __func__, g_dp_vs_sync_conf.sync_tx_lcore); + return EDPVS_OK; +} + +static int dp_vs_sync_head_init(struct dp_vs_sync_nego* info) +{ + int len = 0; + len = sizeof(struct dp_vs_sync_nego); + memset(info, 0, len); + + info->head.syncid = g_dp_vs_sync_conf.syncid; + info->head.type = DP_VS_SYNC_NEGO_INFO; + info->head.size = len; + + return EDPVS_OK; +} + +static char* dp_vs_sync_code2str(int code) +{ + switch (code) { + case DP_VS_SYNC_INFO_REPLY_CODE: + return "reply"; + case DP_VS_SYNC_INFO_PROBE_CODE: + return "probe"; + case DP_VS_SYNC_INFO_FETCH_CODE: + return "fetch"; + case DP_VS_SYNC_INFO_DONE_CODE: + return "done"; + default: + return "unknown"; + } +} + +static int dp_vs_sync_send_nego(uint8_t peer_syncid, int code, + int type, struct sockaddr_in* remote_addr) +{ + struct dp_vs_sync_nego full_req; + int len = 0; + + len = sizeof(struct dp_vs_sync_nego); + dp_vs_sync_head_init(&full_req); + full_req.code = code; + full_req.peer_syncid = peer_syncid; + full_req.uptime = rte_get_timer_cycles() - g_start_cycles; + + RTE_LOG(INFO, SYNC, "(syncid:%d -> %d)send sync %s" + " uptime %ld remote addr %s\n", + full_req.head.syncid, full_req.peer_syncid, + dp_vs_sync_code2str(full_req.code), + full_req.uptime, + remote_addr ? inet_ntoa(remote_addr->sin_addr) : "null"); + + dp_vs_sync_send_msg(type, (char *)&full_req, len, remote_addr); + + return EDPVS_OK; +} + +static int dp_vs_sync_send_fetch_code(void* arg) +{ + dpvs_timer_cancel(&g_req_timer, true); + g_req_timeout = 1; + dp_vs_sync_send_nego(g_dp_vs_sync_fetch.syncid, + DP_VS_SYNC_INFO_FETCH_CODE, DP_VS_SYNC_UNICAST, + &g_dp_vs_sync_fetch.addr); + + return DTIMER_STOP; +} + +static void dp_vs_sync_full_is_all_end(void) +{ + int cid = 0; + + for (cid = 0; cid < DPVS_MAX_LCORE; cid++) { + if ((cid == rte_get_master_lcore()) || !is_lcore_id_valid(cid)) + continue; + + if (!DP_VS_SYNC_FULL_IS_END(cid)) + break; + } + + if (cid >= DPVS_MAX_LCORE) { +#ifdef CONFIG_DPVS_SYNC_DEBUG + RTE_LOG(INFO, SYNC, "(syncid:%d -> %d) full sync is complete.\n", + g_dp_vs_sync_conf.syncid, g_dp_vs_sync_request.syncid); +#endif + dp_vs_sync_send_nego(g_dp_vs_sync_request.syncid, + DP_VS_SYNC_INFO_DONE_CODE, DP_VS_SYNC_UNICAST, + &g_dp_vs_sync_request.addr); + } + + return; +} + +int dp_vs_sync_full_start(lcoreid_t cid) +{ + g_dp_vs_sync_fwd_core.fwd_core[cid].start = true; + g_dp_vs_sync_fwd_core.fwd_core[cid].end = false; + g_dp_vs_sync_fwd_core.fwd_core[cid].last_index = 0; + return EDPVS_OK; +} + +int dp_vs_sync_full_end(lcoreid_t cid) +{ + g_dp_vs_sync_fwd_core.fwd_core[cid].end = true; + g_dp_vs_sync_fwd_core.fwd_core[cid].start = false; + + dp_vs_sync_full_is_all_end(); + return EDPVS_OK; +} + +static int dp_vs_sync_recv_probe_code(struct dp_vs_sync_nego* req, + struct sockaddr_in* remote_addr) +{ + return dp_vs_sync_send_nego(req->head.syncid, DP_VS_SYNC_INFO_REPLY_CODE, + DP_VS_SYNC_UNICAST, remote_addr); +} + +static int dp_vs_sync_recv_reply_code(struct dp_vs_sync_nego* req, + struct sockaddr_in* remote_addr) +{ + if (req->peer_syncid != g_dp_vs_sync_conf.syncid || g_req_timeout) + return EDPVS_OK; + + if (req->uptime > g_dp_vs_sync_fetch.uptime) { + g_dp_vs_sync_fetch.uptime = req->uptime; + g_dp_vs_sync_fetch.syncid = req->head.syncid; + memcpy(&g_dp_vs_sync_fetch.addr, remote_addr, + sizeof(g_dp_vs_sync_fetch.addr)); + } + + return EDPVS_OK; +} + +static int dp_vs_sync_recv_fetch_code(struct dp_vs_sync_nego* req, + struct sockaddr_in* remote_addr) +{ + int cid = 0; + + if (req->peer_syncid != g_dp_vs_sync_conf.syncid) + return EDPVS_OK; + + for (cid = 0; cid < DPVS_MAX_LCORE; cid++) { + if ((cid == rte_get_master_lcore()) || !is_lcore_id_valid(cid)) + continue; + dp_vs_sync_full_start(cid); + } + + g_dp_vs_sync_request.syncid = req->head.syncid; + memcpy(&g_dp_vs_sync_request.addr.sin_addr, + &remote_addr->sin_addr, sizeof(g_dp_vs_sync_request.addr.sin_addr)); + + return EDPVS_OK; +} + +static int dp_vs_sync_recv_done_code(struct dp_vs_sync_nego* req, + struct sockaddr_in* remote_addr) +{ + if (req->peer_syncid != g_dp_vs_sync_conf.syncid) + return EDPVS_OK; + + RTE_LOG(INFO, SYNC, "%s:(syncid %d <- %d) full sync is complete.\n", + __func__, g_dp_vs_sync_conf.syncid, req->head.syncid); + + return EDPVS_OK; +} + + +int dp_vs_sync_recv_nego(const char * buf, int len, + struct sockaddr_in* remote_addr) +{ + struct dp_vs_sync_nego* req = (struct dp_vs_sync_nego*)buf; + + if (len != sizeof(struct dp_vs_sync_nego)) { + RTE_LOG(ERR, SYNC, "%s: recv request sync message len error" + "(actual length = %d, expected length = %d)\n", + __func__, len, sizeof(struct dp_vs_sync_nego)); + return EDPVS_INVPKT; + } + + RTE_LOG(INFO, SYNC, "(syncid:%d <- %d)recv sync %s" + " uptime %ld remote addr %s\n", + g_dp_vs_sync_conf.syncid, req->head.syncid, + dp_vs_sync_code2str(req->code), req->uptime, + inet_ntoa(remote_addr->sin_addr)); + + switch (req->code) { + case DP_VS_SYNC_INFO_REPLY_CODE: + dp_vs_sync_recv_reply_code(req, remote_addr); + break; + case DP_VS_SYNC_INFO_PROBE_CODE: + dp_vs_sync_recv_probe_code(req, remote_addr); + break; + case DP_VS_SYNC_INFO_FETCH_CODE: + dp_vs_sync_recv_fetch_code(req, remote_addr); + break; + case DP_VS_SYNC_INFO_DONE_CODE: + dp_vs_sync_recv_done_code(req, remote_addr); + break; + default: + RTE_LOG(ERR, SYNC, "(syncid:%d <- %d)recv sync code %d" + " uptime %ld remote addr %s\n", + g_dp_vs_sync_conf.syncid, req->head.syncid, + req->code, req->uptime, + inet_ntoa(remote_addr->sin_addr)); + return EDPVS_INVPKT; + } + + return EDPVS_OK; +} + +int dp_vs_sync_conn_start(void) +{ + struct timeval tv; + static int start_full_sync = 0; + + if (start_full_sync) { + RTE_LOG(DEBUG, SYNC, "%s:(syncid:%d) already start conn sync.\n", + __func__, g_dp_vs_sync_conf.syncid); + return 0; + } + + start_full_sync = 1; + dp_vs_sync_send_nego(0, DP_VS_SYNC_INFO_PROBE_CODE, + DP_VS_SYNC_MCAST, NULL); + tv.tv_sec = 2; + tv.tv_usec = 0; + dpvs_timer_sched(&g_req_timer, &tv, + dp_vs_sync_send_fetch_code, NULL, true); + +#ifdef CONFIG_DPVS_SYNC_DEBUG + RTE_LOG(INFO, SYNC, "%s:(syncid:%d) start conn sync.\n", + __func__, g_dp_vs_sync_conf.syncid); +#endif + return EDPVS_OK; +} + +void dp_vs_sync_run_loop(lcoreid_t cid) +{ + if (cid == g_dp_vs_sync_conf.sync_rx_lcore) { + dp_vs_session_sync_rx_loop(); + } else if (cid == g_dp_vs_sync_conf.sync_tx_lcore) { + dp_vs_session_sync_tx_loop(); + } +} + +char* dp_vs_sync_laddr_ifname(void) +{ + return g_dp_vs_sync_conf.laddr_ifname; +} + +static int dp_vs_set_sync_mesg_maxlen(void) +{ + int num = 0; + int mtu = 0; + + mtu = get_sock_mtu(); + + num = (mtu - sizeof(struct ipv4_hdr) - sizeof(struct udp_hdr) - + DP_VS_SYNC_MESG_HEADER_LEN - 20) / DP_VS_SYNC_CONN_SIZE; + + g_dp_vs_sync_conf.send_mesg_maxlen = + DP_VS_SYNC_MESG_HEADER_LEN + DP_VS_SYNC_CONN_SIZE * num; + RTE_LOG(INFO, SYNC, "%s: send_mesg_maxlen is %d.\n", __func__, + g_dp_vs_sync_conf.send_mesg_maxlen); + + g_dp_vs_sync_conf.recv_mesg_maxlen = mtu - sizeof(struct ipv4_hdr) - sizeof(struct udp_hdr); + RTE_LOG(INFO, SYNC, "%s: recv_mesg_maxlen is %d.\n", __func__, + g_dp_vs_sync_conf.recv_mesg_maxlen); + + return 0; +} + +static int dp_vs_sync_conf_init(void) +{ + if (!g_dp_vs_sync_conf.sync_buff_delay) { + g_dp_vs_sync_conf.sync_buff_delay = DP_VS_SYNC_DELAY_SECONDS; + + RTE_LOG(INFO, SYNC, "%s: sync curr buffer delay time is %d.\n", __func__, + g_dp_vs_sync_conf.sync_buff_delay); + } + + if (!g_dp_vs_sync_conf.sync_conn_elapse) { + g_dp_vs_sync_conf.sync_conn_elapse = DP_VS_SYNC_DELAY_SECONDS; + + RTE_LOG(INFO, SYNC, "%s: sync conn delay time is %d.\n", __func__, + g_dp_vs_sync_conf.sync_conn_elapse); + } + + dp_vs_set_sync_mesg_maxlen(); + return EDPVS_OK; +} + +int dp_vs_sync_init(void) +{ + char ring_name[128]; + uint8_t cid; + int index = 0; + + for (cid = 0; cid < DPVS_MAX_LCORE; cid++) { + snprintf(ring_name, sizeof(ring_name), "session_sync_rx_ring_%d", cid); + g_dp_vs_sync_rx_ring[cid] = rte_ring_create(ring_name, CONN_SYNC_RING_SIZE, + rte_socket_id(), RING_F_SP_ENQ); + if (unlikely(!g_dp_vs_sync_rx_ring[cid])) { + RTE_LOG(ERR, SYNC, "%s: Failed to create rx ring on lcore %d.\n", + __func__, cid); + return EDPVS_DPDKAPIFAIL; + } + } + + for (index = 0; index < DP_VS_SYNC_MAX; index++) { + INIT_LIST_HEAD(&dp_vs_sync_queue[index]); + dp_vs_sync_curr_buff[index] = NULL; + + snprintf(ring_name, sizeof(ring_name), "session_sync_tx_ring_%d", index); + g_dp_vs_sync_tx_ring[index] = rte_ring_create(ring_name, CONN_SYNC_RING_SIZE, + rte_socket_id(), RING_F_SC_DEQ); + if (unlikely(!g_dp_vs_sync_tx_ring[index])) { + RTE_LOG(ERR, SYNC, "[%s] Failed to create tx ring.\n", __func__); + return EDPVS_DPDKAPIFAIL; + } + } + + g_start_cycles = rte_get_timer_cycles(); + + dp_vs_sync_conf_init(); + + return EDPVS_OK; +} + +int dp_vs_sync_term(void) +{ + lcoreid_t cid = 0; + int index = 0; + + for (cid = 0; cid < DPVS_MAX_LCORE; cid++) { + rte_ring_free(g_dp_vs_sync_rx_ring[cid]); + } + + for (index = 0; index < DP_VS_SYNC_MAX; index++) { + rte_ring_free(g_dp_vs_sync_tx_ring[index]); + } + + return EDPVS_OK; +} + +static void dp_vs_sync_enable_handler(vector_t tokens) +{ + g_dp_vs_sync_conf.sync_enable = 1; + RTE_LOG(INFO, SYNC, "%s: g_sync_sesion_enable ON.\n", __func__); +} + +static void dp_vs_sync_elapse_handler(vector_t tokens) +{ + char *str = set_value(tokens); + int delay = 0; + assert(str); + + delay = atoi(str); + if (delay > 30 || delay < 0) { + RTE_LOG(WARNING, SYNC, "invalid sync_conn_elapse %s, using default %d\n", + str, DP_VS_SYNC_DELAY_SECONDS); + delay = DP_VS_SYNC_DELAY_SECONDS; + } else { + RTE_LOG(INFO, SYNC, "sync_conn_elapse = %d\n", delay); + } + + g_dp_vs_sync_conf.sync_conn_elapse = delay; + + FREE_PTR(str); +} + +static void dp_vs_sync_buff_delay_handler(vector_t tokens) +{ + char *str = set_value(tokens); + int delay = 0; + assert(str); + + delay = atoi(str); + if (delay > 30 || delay < 0) { + RTE_LOG(WARNING, SYNC, "invalid sync_buff_delay %s, using default %d\n", + str, DP_VS_SYNC_DELAY_SECONDS); + delay = DP_VS_SYNC_DELAY_SECONDS; + } else { + RTE_LOG(INFO, SYNC, "sync_buff_delay = %d\n", delay); + } + + g_dp_vs_sync_conf.sync_buff_delay = delay; + + FREE_PTR(str); +} + +static void dp_vs_sync_conn_count_handler(vector_t tokens) +{ + char *str = set_value(tokens); + int count = 0; + assert(str); + + count = atoi(str); + if (count < 0) { + RTE_LOG(WARNING, SYNC, "invalid count of sync per time %s, using default %d\n", + str, DP_VS_SYNC_CONN_CNT_PER_TIME); + count = DP_VS_SYNC_CONN_CNT_PER_TIME; + } else { + RTE_LOG(INFO, SYNC, "sync_per_time_cnt = %d\n", count); + } + + g_dp_vs_sync_conf.sync_per_time_cnt = count; + + FREE_PTR(str); +} + +static void dp_vs_sync_laddr_ifname_handler(vector_t tokens) +{ + char *str = set_value(tokens); + assert(str); + + rte_memcpy(g_dp_vs_sync_conf.laddr_ifname, str, strlen(str)); + RTE_LOG(INFO, SYNC, "%s: laddr_ifname is %s\n", + __func__, g_dp_vs_sync_conf.laddr_ifname); + + FREE_PTR(str); +} + +static void dp_vs_sync_syncid_handler(vector_t tokens) +{ + char *str = set_value(tokens); + int id = 0; + assert(str); + + id = atoi(str); + if (id > 65534 || id < 1) { + RTE_LOG(WARNING, SYNC, "invalid dp_vs_syncid %s, using default %d\n", + str, 1); + g_dp_vs_sync_conf.syncid = 1; + } else { + RTE_LOG(INFO, SYNC, "dp_vs_syncid = %d\n", id); + g_dp_vs_sync_conf.syncid = id; + } + + FREE_PTR(str); +} + +void install_session_sync_keywords(void) +{ + install_keyword_root("session_sync", NULL); + install_keyword("sync_session_enable", dp_vs_sync_enable_handler, KW_TYPE_INIT); + install_keyword("sync_session_elapse", dp_vs_sync_elapse_handler, KW_TYPE_INIT); + install_keyword("sync_buff_delay", dp_vs_sync_buff_delay_handler, KW_TYPE_INIT); + install_keyword("sync_conn_count", dp_vs_sync_conn_count_handler, KW_TYPE_INIT); + install_keyword("laddr_ifname", dp_vs_sync_laddr_ifname_handler, KW_TYPE_INIT); + install_keyword("sync_id", dp_vs_sync_syncid_handler, KW_TYPE_INIT); + install_keyword("socket", NULL, KW_TYPE_NORMAL); + install_sublevel(); + install_session_sync_sock_keywords(); + install_sublevel_end(); +} diff --git a/src/ipvs/ip_vs_sync_msg.c b/src/ipvs/ip_vs_sync_msg.c new file mode 100644 index 000000000..1b835b7f7 --- /dev/null +++ b/src/ipvs/ip_vs_sync_msg.c @@ -0,0 +1,337 @@ +#include "ipvs/sync_msg.h" +#include "parser/parser.h" + +#define DEFAULT_MCAST_ADDR "224.0.1.100" +#define DEFAULT_MCAST_PORT 8088 +#define DEFAULT_MCAST_TTL 20 +#define DEFAULT_SOCK_MTU 1500 + +static uint32_t sock_mtu = DEFAULT_SOCK_MTU; +static uint32_t mcast_ttl = DEFAULT_MCAST_TTL; +static uint16_t mcast_port = DEFAULT_MCAST_PORT; +char mcast_addr_str[16]; +static struct sockaddr_in mcast_addr; + +#define DEFAULT_UNICAST_PORT 8089 +static uint16_t unicast_port = DEFAULT_UNICAST_PORT; + +/* Set up sending multicast socket over UDP */ +int create_mcast_send_sock(void) +{ + /* Create a socket */ + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + RTE_LOG(ERR, MCAST, "%s: failed to create socket.\n", __func__); + return fd; + } + /* Add mcast info */ + memset(&mcast_addr, 0, sizeof(mcast_addr)); + mcast_addr.sin_family = AF_INET; + mcast_addr.sin_addr.s_addr = inet_addr(mcast_addr_str); + mcast_addr.sin_port = htons(mcast_port); + + /* Set multicast ttl */ + u_char ttl = mcast_ttl; + setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof(ttl)); + + return fd; +} + +int create_mcast_receive_sock(void) +{ + struct sockaddr_in local_addr; + + /* Create a socket */ + int yes = 1; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + RTE_LOG(ERR, MCAST, "%s: failed to create socket.\n", __func__); + return fd; + } + + /* Set reused*/ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + RTE_LOG(ERR, MCAST, "%s: failed to set SO_REUSEADDR.\n", __func__); + goto error; + } + + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + local_addr.sin_port = htons(DEFAULT_MCAST_PORT); + + /* Set loop */ + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + RTE_LOG(ERR, MCAST, "%s: failed to set IP_MULTICAST_LOOP.\n", __func__); + goto error; + } + + /* Bind local addr */ + if (bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) { + RTE_LOG(ERR, MCAST, "%s: failed to bind local_addr.\n", __func__); + goto error; + } + + return fd; + +error: + close(fd); + return -1; +} + +static void fill_in_mreq(struct ip_mreq *mreq) +{ + //memset(mreq, 0, sizeof(struct ip_mreq)); + mreq->imr_multiaddr.s_addr = inet_addr(mcast_addr_str); + mreq->imr_interface.s_addr = htonl(INADDR_ANY); +} + +int add_mcast_group(int sockfd) +{ + struct ip_mreq mreq; + int err; + fill_in_mreq(&mreq); + err = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err < 0) { + RTE_LOG(ERR, MCAST, "%s: failed to add multicast group.\n", __func__); + close(sockfd); + } + return err; +} + +int drop_mcast_group(int sockfd) +{ + struct ip_mreq mreq; + int err; + fill_in_mreq(&mreq); + err = setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err < 0) { + RTE_LOG(ERR, MCAST, "%s: failed to drop mcast group.\n", __func__); + } + close(sockfd); + return err; +} + +int send_mcast_msg(int sockfd, char *buffer, int len) +{ + int res = sendto(sockfd, buffer, len, 0, + (struct sockaddr*)&mcast_addr, + sizeof(mcast_addr)); + if (res < 0 ) + RTE_LOG(ERR, MCAST, "%s: mcast send to %s failed.\n", + __func__, inet_ntoa(mcast_addr.sin_addr)); +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + else + RTE_LOG(DEBUG, MCAST, "%s: send %d bytes msg to %s success.\n", + __func__, res, inet_ntoa(mcast_addr.sin_addr)); +#endif + return res; +} + +int receive_mcast_msg(int sockfd, char *buffer, const size_t buflen, + struct sockaddr_in* remote_addr) +{ + /* receive a multicast packet */ + struct sockaddr_in addr; + socklen_t l = sizeof(addr); + int len = recvfrom(sockfd, buffer, buflen, 0, (struct sockaddr *)&addr, &l); + if (len == -1) { + RTE_LOG(ERR, MCAST, "%s: failed to recv msg.\n", __func__); + } else { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(DEBUG, MCAST, "%s: recv %d msg from %s .\n", __func__, + len, inet_ntoa(addr.sin_addr)); +#endif + if (remote_addr != NULL) + memcpy(remote_addr, &addr, l); + } + return len; +} + +static void mcast_addr_str_handler(vector_t tokens) +{ + char *str = set_value(tokens); + assert(str); + + rte_memcpy(mcast_addr_str, str, strlen(str)); + RTE_LOG(INFO, MCAST, "%s: mcast_addr = %s\n", __func__, + mcast_addr_str); + + FREE_PTR(str); +} + +static void mcast_port_handler(vector_t tokens) +{ + char *str = set_value(tokens); + uint16_t port; + + assert(str); + port = atoi(str); + + if (port > 65535 || port < 1024) { + RTE_LOG(WARNING, MCAST, "invalid mcast_port %s, using default %d\n", + str, DEFAULT_MCAST_PORT); + } else { + RTE_LOG(INFO, MCAST, "%s: mcast_port = %d\n", __func__, port); + mcast_port = port; + } + + FREE_PTR(str); +} + +static void mcast_ttl_handler(vector_t tokens) +{ + char *str = set_value(tokens); + uint32_t ttl; + + assert(str); + ttl = atoi(str); + + if (ttl > 255 || ttl < 0) { + RTE_LOG(WARNING, MCAST, "invalid mcast_ttl %s, using default %d\n", + str, DEFAULT_MCAST_TTL); + mcast_ttl = DEFAULT_MCAST_TTL; + } else { + RTE_LOG(INFO, MCAST, "%s: mcast_ttl = %d\n", __func__, ttl); + mcast_ttl = ttl; + } + + FREE_PTR(str); +} + +static void install_session_sync_mcast_keywords(void) +{ + install_keyword("mcast_addr", mcast_addr_str_handler, KW_TYPE_NORMAL); + install_keyword("mcast_port", mcast_port_handler, KW_TYPE_NORMAL); + install_keyword("mcast_ttl", mcast_ttl_handler, KW_TYPE_NORMAL); + //install_keyword("mcast_mtu", mcast_mtu_handler, KW_TYPE_NORMAL); +} + +int create_receive_unicast_sock(void) +{ + struct sockaddr_in sin; + int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd == -1) { + return -1; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(unicast_port); + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) + goto eexit; + return fd; + +eexit: + close(fd); + return -1; +} + +int create_send_unicast_sock(void) +{ + int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd == -1) { + return -1; + } + + return fd; +} + +int send_unicast_msg(int sockfd, char *buffer, int len, + struct sockaddr_in* remote_addr) +{ + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons(unicast_port); + memcpy(&addr.sin_addr, &remote_addr->sin_addr, sizeof(addr.sin_addr)); + + int res = sendto(sockfd, buffer, len, 0, + (struct sockaddr*)&addr, + sizeof(addr)); + if (res < 0 ) + RTE_LOG(ERR, UNICAST, "%s: unicast send to %s failed.\n", + __func__, inet_ntoa(remote_addr->sin_addr)); +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + else + RTE_LOG(DEBUG, UNICAST, "%s: unicast send %d bytes msg to %s success.\n", + __func__, res, inet_ntoa(remote_addr->sin_addr)); +#endif + return res; +} + +int receive_unicast_msg(int sockfd, char *buffer, const size_t buflen, + struct sockaddr_in* remote_addr) +{ + struct sockaddr_in addr; + socklen_t l = sizeof(addr); + int len = recvfrom(sockfd, buffer, buflen, 0, (struct sockaddr *)&addr, &l); + if (len == -1) { + RTE_LOG(ERR, UNICAST, "%s: unicast failed to recv msg.\n", __func__); + } else { +#ifdef CONFIG_DPVS_CONN_SYNC_DEBUG + RTE_LOG(INFO, UNICAST, "%s: unicast recv %d msg from %s .\n", __func__, + len, inet_ntoa(addr.sin_addr)); +#endif + if (remote_addr != NULL) + memcpy(remote_addr, &addr, l); + } + return len; +} + +static void unicast_port_handler(vector_t tokens) +{ + char *str = set_value(tokens); + uint16_t port; + + assert(str); + port = atoi(str); + + if (port > 65535 || port < 1024) { + RTE_LOG(WARNING, UNICAST, "invalid mcast_port %s, using default %d\n", + str, DEFAULT_UNICAST_PORT); + } else { + RTE_LOG(INFO, UNICAST, "%s: mcast_port = %d\n", __func__, port); + unicast_port = port; + } + + FREE_PTR(str); +} + +static void install_session_sync_unicast_keywords(void) +{ + install_keyword("unicast_port", unicast_port_handler, KW_TYPE_NORMAL); +} + +int get_sock_mtu(void) +{ + return sock_mtu; +} + +static void sock_mtu_handler(vector_t tokens) +{ + char *str = set_value(tokens); + uint32_t mtu; + + assert(str); + mtu = atoi(str); + + if (mtu > 0 && mtu < 65536) { + RTE_LOG(INFO, MCAST, "sock_mtu = %d\n", mtu); + sock_mtu = mtu; + } else { + RTE_LOG(WARNING, MCAST, "invalid sock_mtu %s, using default %d\n", + str, DEFAULT_SOCK_MTU); + sock_mtu = DEFAULT_SOCK_MTU; + } + + FREE_PTR(str); +} + +void install_session_sync_sock_keywords(void) +{ + install_keyword("mtu", sock_mtu_handler, KW_TYPE_NORMAL); + install_session_sync_mcast_keywords(); + install_session_sync_unicast_keywords(); +} diff --git a/src/netif.c b/src/netif.c index 2af2fe1e4..56da4bca5 100644 --- a/src/netif.c +++ b/src/netif.c @@ -43,6 +43,7 @@ #include #include #include +#include "ipvs/sync.h" #define NETIF_PKTPOOL_NB_MBUF_DEF 65535 #define NETIF_PKTPOOL_NB_MBUF_MIN 1023 @@ -653,7 +654,8 @@ static void worker_type_handler(vector_t tokens) struct worker_conf_stream, worker_list_node); assert(str); - if (!strcmp(str, "master") || !strcmp(str, "slave")) { + if (!strcmp(str, "master") || !strcmp(str, "slave") + || !strcmp(str, "sync-rx") || !strcmp(str, "sync-tx")) { RTE_LOG(INFO, NETIF, "%s:type = %s\n", current_worker->name, str); strncpy(current_worker->type, str, sizeof(current_worker->type)); } else { @@ -681,6 +683,12 @@ static void cpu_id_handler(vector_t tokens) cpu_id = atoi(str); RTE_LOG(INFO, NETIF, "%s:cpu_id = %d\n", current_worker->name, cpu_id); current_worker->cpu_id = cpu_id; + + if (!strcmp(current_worker->type, "sync-rx")) + dp_vs_sync_set_rx_core(cpu_id); + + if (!strcmp(current_worker->type, "sync-tx")) + dp_vs_sync_set_tx_core(cpu_id); } FREE_PTR(str); @@ -1152,20 +1160,28 @@ static void isol_rxq_del(struct rx_partner *isol_rxq, bool force); static void config_lcores(struct list_head *worker_list) { int ii, tk; - int cpu_id_min, cpu_left; + int cpu_id_min, cpu_left, cpu_cnt; lcoreid_t id = 0; portid_t pid; struct netif_port *port; struct queue_conf_stream *queue; - struct worker_conf_stream *worker, *worker_min; - - cpu_left = list_elems(worker_list); - list_for_each_entry(worker, worker_list, worker_list_node) { + struct worker_conf_stream *worker, *worker_next, *worker_min; + + /** + * move non-slave workers to tail of worker_list. + * cpu_cnt: number of wokers + * cpu_left: number of slave workers + * */ + cpu_cnt = cpu_left = list_elems(worker_list); + list_for_each_entry_safe(worker, worker_next, worker_list, worker_list_node) { if (strcmp(worker->type, "slave")) { list_move_tail(&worker->worker_list_node, worker_list); cpu_left--; } + if (--cpu_cnt <= 0) + break; } + while (cpu_left > 0) { cpu_id_min = DPVS_MAX_LCORE; worker_min = NULL; @@ -1637,6 +1653,21 @@ portid_t netif_max_pid; queueid_t netif_max_qid; struct port_queue_lcore_map pql_map[NETIF_MAX_PORTS]; +lcoreid_t get_lcoreid(queueid_t qid) +{ + struct netif_port *dev = NULL; + char* ifname = NULL; + + ifname = dp_vs_sync_laddr_ifname(); + dev = netif_port_get_by_name(ifname); + if (!dev) { + RTE_LOG(WARNING, IPVS, "%s: dpdk device not found\n", __func__); + return 16; + } + + return pql_map[dev->id].rx_qid[qid]; +} + static int build_port_queue_lcore_map(void) { int i, j, k; @@ -2313,6 +2344,9 @@ void lcore_process_packets(struct netif_queue_conf *qconf, struct rte_mbuf **mbu /* L2 filter */ for (i = 0; i < count; i++) { struct rte_mbuf *mbuf = mbufs[i]; +#ifdef CONFIG_DPVS_SYNC + mbuf->hash.usr = qconf->id; /* use in session synchronization*/ +#endif struct netif_port *dev = netif_port_get(mbuf->port); if (unlikely(!dev)) { @@ -2417,6 +2451,11 @@ static void lcore_job_recv_fwd(void *arg) cid = rte_lcore_id(); assert(LCORE_ID_ANY != cid); +#ifdef CONFIG_DPVS_SYNC + dp_vs_sync_lcore_process_rx_msg(cid); + dp_vs_conn_lcore_tx(cid); +#endif + for (i = 0; i < lcore_conf[lcore2index[cid]].nports; i++) { pid = lcore_conf[lcore2index[cid]].pqs[i].id; assert(pid <= bond_pid_end); @@ -4014,6 +4053,9 @@ static int netif_loop(void *dummy) assert(LCORE_ID_ANY != cid); +#ifdef CONFIG_DPVS_SYNC + dp_vs_sync_run_loop(cid); +#endif try_isol_rxq_lcore_loop(); if (0 == lcore_conf[lcore2index[cid]].nports) { RTE_LOG(INFO, NETIF, "[%s] Lcore %d has nothing to do.\n", __func__, cid); diff --git a/src/sa_pool.c b/src/sa_pool.c index efdef0b1b..ab40300f7 100644 --- a/src/sa_pool.c +++ b/src/sa_pool.c @@ -71,6 +71,7 @@ #define LPORT_LCORE_MAPPING_POOL_MODE_NAME "lport_lcore_mapping" #define LADDR_LCORE_MAPPING_POOL_MODE_NAME "laddr_lcore_mapping" +#define INVALID_QUEUE_ID (0xFFFF) enum { SA_F_USED = 0x01, @@ -146,8 +147,8 @@ uint8_t sa_pool_mode = LPORT_LCORE_MAPPING_POOL_MODE; extern uint32_t lcore_ids[RTE_MAX_LCORE]; static int __add_del_filter_addr_mode(int af, struct netif_port *dev, lcoreid_t cid, - const union inet_addr *dip, - uint32_t filter_id[MAX_FDIR_PROTO], bool add) + const union inet_addr *dip, uint32_t filter_id[MAX_FDIR_PROTO], + bool add, queueid_t queue) { struct rte_eth_fdir_filter filt = { .action.behavior = RTE_ETH_FDIR_ACCEPT, @@ -165,7 +166,6 @@ static int __add_del_filter_addr_mode(int af, struct netif_port *dev, lcoreid_t return EDPVS_NOTSUPP; } - queueid_t queue; int err; enum rte_filter_op op; #ifdef CONFIG_DPVS_SAPOOL_DEBUG @@ -186,12 +186,18 @@ static int __add_del_filter_addr_mode(int af, struct netif_port *dev, lcoreid_t return EDPVS_INVAL; } - err = netif_get_queue(dev, cid, &queue); - if (err != EDPVS_OK) - return err; + if (queue == INVALID_QUEUE_ID) { + err = netif_get_queue(dev, cid, &queue); + if (err != EDPVS_OK) + return err; + } filt.action.rx_queue = queue; - op = add ? RTE_ETH_FILTER_ADD : RTE_ETH_FILTER_DELETE; + + /**add change to update, purpose is to resolve the problem + of returning collision errors when send duplicate configurations + **/ + op = add ? RTE_ETH_FILTER_UPDATE : RTE_ETH_FILTER_DELETE; err = netif_fdir_filter_set(dev, op, &filt); if (err != EDPVS_OK) @@ -270,7 +276,11 @@ static int __add_del_filter_port_mode(int af, struct netif_port *dev, lcoreid_t return err; filt[0].action.rx_queue = filt[1].action.rx_queue = queue; - op = add ? RTE_ETH_FILTER_ADD : RTE_ETH_FILTER_DELETE; + + /**add change to update, purpose is to resolve the problem + of returning collision errors when send duplicate configurations + **/ + op = add ? RTE_ETH_FILTER_UPDATE : RTE_ETH_FILTER_DELETE; netif_mask_fdir_filter(af, dev, &filt[0]); netif_mask_fdir_filter(af, dev, &filt[1]); @@ -301,26 +311,26 @@ static int __add_del_filter_port_mode(int af, struct netif_port *dev, lcoreid_t static int __add_del_filter(int af, struct netif_port *dev, lcoreid_t cid, const union inet_addr *dip, __be16 dport, - uint32_t filter_id[MAX_FDIR_PROTO], bool add) + uint32_t filter_id[MAX_FDIR_PROTO], bool add, queueid_t queue) { if (SA_POOL_MODE == LPORT_LCORE_MAPPING_POOL_MODE) return __add_del_filter_port_mode(af, dev, cid, dip, dport, filter_id, add); else - return __add_del_filter_addr_mode(af, dev, cid, dip, filter_id, add); + return __add_del_filter_addr_mode(af, dev, cid, dip, filter_id, add, queue); } static inline int sa_add_filter(int af, struct netif_port *dev, lcoreid_t cid, const union inet_addr *dip, __be16 dport, - uint32_t filter_id[MAX_FDIR_PROTO]) + uint32_t filter_id[MAX_FDIR_PROTO], queueid_t queue) { - return __add_del_filter(af, dev, cid, dip, dport, filter_id, true); + return __add_del_filter(af, dev, cid, dip, dport, filter_id, true, queue); } static inline int sa_del_filter(int af, struct netif_port *dev, lcoreid_t cid, const union inet_addr *dip, __be16 dport, - uint32_t filter_id[MAX_FDIR_PROTO]) + uint32_t filter_id[MAX_FDIR_PROTO], queueid_t queue) { - return __add_del_filter(af, dev, cid, dip, dport, filter_id, false); + return __add_del_filter(af, dev, cid, dip, dport, filter_id, false, queue); } static int sa_pool_alloc_hash(struct sa_pool *ap, uint8_t hash_sz, @@ -398,8 +408,8 @@ static int __sa_pool_create(struct inet_ifaddr *ifa, lcoreid_t cid, /* if add filter failed, waste some soft-id is acceptable. */ filtids[0] = fdir->soft_id++; filtids[1] = fdir->soft_id++; - err = sa_add_filter(ifa->af, ifa->idev->dev, cid, &ifa->addr, - fdir->port_base, filtids); + err = sa_add_filter(ifa->af, ifa->idev->dev, cid, &ifa->addr, + fdir->port_base, filtids, INVALID_QUEUE_ID); if (err != EDPVS_OK) { sa_pool_free_hash(ap); rte_free(ap); @@ -482,7 +492,7 @@ int sa_pool_destroy(struct inet_ifaddr *ifa) } sa_del_filter(ifa->af, ifa->idev->dev, cid, &ifa->addr, - fdir->port_base, ap->filter_id); + fdir->port_base, ap->filter_id, INVALID_QUEUE_ID); sa_pool_free_hash(ap); rte_free(ap); ifa->sa_pools[cid] = NULL; @@ -866,6 +876,36 @@ int sa_release(const struct netif_port *dev, return err; } +int sa_bind_conn(int af, struct netif_port *dev, lcoreid_t cid, + const union inet_addr *dip, + __be16 dport, queueid_t queue) +{ + uint32_t filtids[MAX_FDIR_PROTO]; + int err; +#ifdef CONFIG_DPVS_SAPOOL_DEBUG + char ipaddr[64]; +#endif + + filtids[0] = 1; + filtids[1] = 2; + + err = sa_add_filter(af, dev, cid, dip, dport, filtids, queue); + if (err != EDPVS_OK) { + RTE_LOG(WARNING, SAPOOL, "%s: add fdir filter failed.\n", __func__); + return err; + } + +#ifdef CONFIG_DPVS_SAPOOL_DEBUG + RTE_LOG(DEBUG, SAPOOL, "FDIR: %s %s %s " + "ip %s queue %d lcore %2d\n", + "bind", dev->name, + af == AF_INET ? "IPv4" : "IPv6", + inet_ntop(af, dip, ipaddr, sizeof(ipaddr)) ? : "::", + queue, cid); +#endif + return EDPVS_OK; +} + int sa_pool_stats(const struct inet_ifaddr *ifa, struct sa_pool_stats *stats) { struct dpvs_msg *req, *reply; diff --git a/tools/ipvsadm/ipvsadm.c b/tools/ipvsadm/ipvsadm.c index 274d01e0a..7c94daa7f 100644 --- a/tools/ipvsadm/ipvsadm.c +++ b/tools/ipvsadm/ipvsadm.c @@ -144,7 +144,8 @@ #define CMD_ADDBLKLST (CMD_NONE+18) #define CMD_DELBLKLST (CMD_NONE+19) #define CMD_GETBLKLST (CMD_NONE+20) -#define CMD_MAX CMD_GETBLKLST +#define CMD_CONN_SYNC (CMD_NONE+21) +#define CMD_MAX CMD_CONN_SYNC #define NUMBER_OF_CMD (CMD_MAX - CMD_NONE) static const char* cmdnames[] = { @@ -284,6 +285,7 @@ enum { TAG_SORT, TAG_NO_SORT, TAG_PERSISTENCE_ENGINE, + TAG_CONN_SYNC, TAG_SOCKPAIR, }; @@ -392,6 +394,8 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, TAG_START_DAEMON, NULL, NULL }, { "stop-daemon", '\0', POPT_ARG_STRING, &optarg, TAG_STOP_DAEMON, NULL, NULL }, + { "conn-sync", '\0', POPT_ARG_NONE, &optarg, + TAG_CONN_SYNC, NULL, NULL }, { "add-laddr", 'P', POPT_ARG_NONE, NULL, 'P', NULL, NULL }, { "del-laddr", 'Q', POPT_ARG_NONE, NULL, 'Q', NULL, NULL }, { "get-laddr", 'G', POPT_ARG_NONE, NULL, 'G', NULL, NULL }, @@ -485,6 +489,9 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, case 'C': set_command(&ce->cmd, CMD_FLUSH); break; + case TAG_CONN_SYNC: + set_command(&ce->cmd, CMD_CONN_SYNC); + break; case 'L': case 'l': set_command(&ce->cmd, CMD_LIST); @@ -947,6 +954,10 @@ static int process_options(int argc, char **argv, int reading_stdin) result = ipvs_flush(); break; + case CMD_CONN_SYNC: + result = ipvs_conn_sync(); + break; + case CMD_ADD: result = ipvs_add_service(&ce.svc); break; diff --git a/tools/keepalived/keepalived/libipvs-2.6/dp_vs.h b/tools/keepalived/keepalived/libipvs-2.6/dp_vs.h index a222ca3f4..8c2446856 100644 --- a/tools/keepalived/keepalived/libipvs-2.6/dp_vs.h +++ b/tools/keepalived/keepalived/libipvs-2.6/dp_vs.h @@ -22,6 +22,7 @@ enum{ DPVS_SO_SET_EDITDEST, DPVS_SO_SET_DELDEST, DPVS_SO_SET_GRATARP, + DPVS_SO_SET_CONN_SYNC, }; enum{ diff --git a/tools/keepalived/keepalived/libipvs-2.6/libipvs.c b/tools/keepalived/keepalived/libipvs-2.6/libipvs.c index 8e532d937..3d45121c3 100644 --- a/tools/keepalived/keepalived/libipvs-2.6/libipvs.c +++ b/tools/keepalived/keepalived/libipvs-2.6/libipvs.c @@ -159,6 +159,10 @@ int ipvs_flush(void) return dpvs_setsockopt(DPVS_SO_SET_FLUSH, NULL, 0); } +int ipvs_conn_sync(void) +{ + return dpvs_setsockopt(DPVS_SO_SET_CONN_SYNC, NULL, 0); +} int ipvs_add_service(ipvs_service_t *svc) { diff --git a/tools/keepalived/keepalived/libipvs-2.6/libipvs.h b/tools/keepalived/keepalived/libipvs-2.6/libipvs.h index 4c46fa2f0..3c21bf4fe 100644 --- a/tools/keepalived/keepalived/libipvs-2.6/libipvs.h +++ b/tools/keepalived/keepalived/libipvs-2.6/libipvs.h @@ -92,6 +92,7 @@ extern unsigned int ipvs_version(void); /* flush all the rules */ extern int ipvs_flush(void); +extern int ipvs_conn_sync(void); /* add a virtual service */ extern int ipvs_add_service(ipvs_service_t *svc); From f73ea54aef5df1079ba19e83483dd0ed655cfca0 Mon Sep 17 00:00:00 2001 From: weiyanhua Date: Fri, 23 Aug 2019 16:22:36 +0800 Subject: [PATCH 3/4] Add identifiers for Synchronized sessions. --- include/conf/conn.h | 1 + include/ipvs/conn.h | 1 + include/ipvs/sync.h | 1 + src/ipvs/ip_vs_conn.c | 4 ++++ src/ipvs/ip_vs_sync.c | 1 + tools/ipvsadm/ipvsadm.c | 12 +++++++++--- 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/conf/conn.h b/include/conf/conn.h index 86da70b66..0e5fa59dc 100644 --- a/include/conf/conn.h +++ b/include/conf/conn.h @@ -62,6 +62,7 @@ struct ip_vs_conn_entry { uint16_t in_af; uint16_t out_af; uint16_t proto; + uint16_t syncid; union inet_addr caddr; union inet_addr vaddr; union inet_addr laddr; diff --git a/include/ipvs/conn.h b/include/ipvs/conn.h index 9270f8d78..e44108a84 100644 --- a/include/ipvs/conn.h +++ b/include/ipvs/conn.h @@ -99,6 +99,7 @@ struct dp_vs_conn { struct dpvs_timer conn_sync_timer; struct timeval conn_sync_timeout; + uint16_t syncid; queueid_t qid; /* used in session synchronization*/ lcoreid_t lcore; diff --git a/include/ipvs/sync.h b/include/ipvs/sync.h index 104ebdb2c..81177d946 100644 --- a/include/ipvs/sync.h +++ b/include/ipvs/sync.h @@ -56,6 +56,7 @@ struct dp_vs_sync_conn { struct dp_vs_seq fnat_seq; uint32_t rs_end_seq; uint32_t rs_end_ack; + uint16_t syncid; }; struct dp_vs_sync_head { diff --git a/src/ipvs/ip_vs_conn.c b/src/ipvs/ip_vs_conn.c index 98281bab3..151290e33 100644 --- a/src/ipvs/ip_vs_conn.c +++ b/src/ipvs/ip_vs_conn.c @@ -743,6 +743,7 @@ struct dp_vs_conn * dp_vs_conn_copy_from_sync(void *param, struct dp_vs_dest *de new->rs_end_seq = sync_conn->rs_end_seq; new->rs_end_ack = sync_conn->rs_end_ack; + new->syncid = sync_conn->syncid; new->flags = sync_conn->flags | DPVS_CONN_F_SYNCED; new->state = sync_conn->state; new->qid = sync_conn->qid; @@ -1353,6 +1354,9 @@ static inline void sockopt_fill_conn_entry(const struct dp_vs_conn *conn, entry->lport = conn->lport; entry->dport = conn->dport; entry->timeout = conn->timeout.tv_sec; + if (conn->flags & DPVS_CONN_F_SYNCED) { + entry->syncid = conn->syncid; + } } static int sockopt_conn_get_specified(const struct ip_vs_conn_req *conn_req, diff --git a/src/ipvs/ip_vs_sync.c b/src/ipvs/ip_vs_sync.c index 6c8569ae6..b5a06084f 100644 --- a/src/ipvs/ip_vs_sync.c +++ b/src/ipvs/ip_vs_sync.c @@ -100,6 +100,7 @@ void dp_vs_sync_conn_enqueue(struct dp_vs_conn *cp, dp_vs_sync_type type) s->daddr = cp->daddr; s->qid = cp->qid; s->lcore = cp->lcore; + s->syncid = g_dp_vs_sync_conf.syncid; rte_memcpy(&s->fnat_seq, &cp->fnat_seq, sizeof(struct dp_vs_seq)); s->rs_end_seq = cp->rs_end_seq; diff --git a/tools/ipvsadm/ipvsadm.c b/tools/ipvsadm/ipvsadm.c index 7c94daa7f..c1aff2681 100644 --- a/tools/ipvsadm/ipvsadm.c +++ b/tools/ipvsadm/ipvsadm.c @@ -1573,9 +1573,15 @@ static void print_conn_entry(const ipvs_conn_entry_t *conn_entry, ntohs(conn_entry->dport), conn_entry->proto, format))) goto exit; - printf("[%d]%-3s %-6s %-11s %-18s %-18s %-18s %s\n", - conn_entry->lcoreid, proto_str, time_str, conn_entry->state, - cname, vname, lname, dname); + if (conn_entry->syncid) { + printf("[%d]%-3s %-6s %-11s %-18s %-18s %-18s %-18s syncid:%d\n", + conn_entry->lcoreid, proto_str, time_str, conn_entry->state, + cname, vname, lname, dname, conn_entry->syncid); + } else { + printf("[%d]%-3s %-6s %-11s %-18s %-18s %-18s %s\n", + conn_entry->lcoreid, proto_str, time_str, conn_entry->state, + cname, vname, lname, dname); + } exit: if (cname) free(cname); From a3130ede1d89dbf546a2084aab9e2e93d4742755 Mon Sep 17 00:00:00 2001 From: weiyanhua Date: Fri, 6 Sep 2019 14:38:18 +0800 Subject: [PATCH 4/4] add readme for session sync. --- doc/pics/synchronization-principle.png | Bin 0 -> 185279 bytes doc/tutorial.md | 67 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 doc/pics/synchronization-principle.png diff --git a/doc/pics/synchronization-principle.png b/doc/pics/synchronization-principle.png new file mode 100644 index 0000000000000000000000000000000000000000..7e5f4fad00223d1dbe543e9b6204f7271a9d0009 GIT binary patch literal 185279 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 aM`vOJN#5hHDEQ*Re~R+= To compile and launch DPVS, pls check *README.md* for this project. @@ -1120,3 +1121,69 @@ worker_defs { } ``` + + +# Session synchronization + +Session synchronization to decrease the connection break due to the DPVS node failure in the cluster. +There are two synchronization modes: full and incremental +* incremental synchronization is used for the new sessions +* full synchronization is used for the existing sessions + +The basic synchronization principle,looks like below +![synchronization-principle.png](pics/synchronization-principle.png) + +Adding new DPVS nodes in the cluster requires the following commands to select synchronous nodes and synchronize existing sessions. + +```bash +$ ipvsadm --conn-sync +``` +If you would like to use session synchronization, add the following lines into the device configs of `dpvs.conf`: + +``` +! worker config (lcores) +worker_defs { + worker cpu0 { + type master + cpu_id 0 + } + + worker cpu1 { + type slave + cpu_id 1 + port dpdk0 { + rx_queue_ids 0 + tx_queue_ids 0 + ! isol_rx_cpu_ids 9 + ! isol_rxq_ring_sz 1048576 + } + } + ....... + + worker cpu10 { + type sync-tx + cpu_id 10 + } + + worker cpu11 { + type sync-rx + cpu_id 11 + } +} + +session_sync { + sync_session_enable + sync_session_elapse 2 !secondes elapsed since the connection is established + sync_buff_delay 2 + laddr_ifname dpdk0 + sync_id 8 + + socket { + mcast_addr 224.0.1.100 + mcast_port 8088 + mcast_ttl 20 + mtu 1500 + unicast_port 8089 + } +} +```