257 lines
5.4 KiB
C
257 lines
5.4 KiB
C
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
|
/* Copyright (c) 2020 Facebook */
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/resource.h>
|
|
#include <bpf/libbpf.h>
|
|
#include "replica.skel.h"
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "replica.h"
|
|
|
|
typedef enum {
|
|
OFFLINE = 0,
|
|
ONLINE = 1,
|
|
}replica_state_e ;
|
|
|
|
struct cmd_node {
|
|
__u64 seq;
|
|
__u32 len;
|
|
uint8_t *cmd;
|
|
struct cmd_node *next;
|
|
};
|
|
|
|
struct pending_queue {
|
|
struct cmd_node *head;
|
|
struct cmd_node *tail;
|
|
int count;
|
|
};
|
|
|
|
/* ================= 全局状态 ================= */
|
|
|
|
static replica_state_e state = OFFLINE;
|
|
static int sockfd = -1;
|
|
|
|
static char peer_ip[MAX_IP_LEN];
|
|
static int peer_port;
|
|
static __u64 peer_seq;
|
|
|
|
static struct pending_queue pending = {
|
|
.head = NULL,
|
|
.tail = NULL,
|
|
.count = 0,
|
|
};
|
|
|
|
/* ================= pending 队列操作 ================= */
|
|
static void pending_free()
|
|
{
|
|
struct pending_queue *q = &pending;
|
|
struct cmd_node *cur = q->head;
|
|
while (cur) {
|
|
struct cmd_node *tmp = cur;
|
|
cur = cur->next;
|
|
free(tmp->cmd);
|
|
free(tmp);
|
|
}
|
|
q->head = q->tail = NULL;
|
|
q->count = 0;
|
|
}
|
|
|
|
static void pending_push(__u64 seq, __u32 len, const uint8_t *cmd)
|
|
{
|
|
struct cmd_node *node = malloc(sizeof(*node));
|
|
if (!node)
|
|
return;
|
|
|
|
node->cmd = malloc(len);
|
|
if (!node->cmd) {
|
|
free(node);
|
|
return;
|
|
}
|
|
|
|
memcpy(node->cmd, cmd, len);
|
|
node->seq = seq;
|
|
node->len = len;
|
|
node->next = NULL;
|
|
|
|
if (!pending.tail) {
|
|
pending.head = pending.tail = node;
|
|
} else {
|
|
pending.tail->next = node;
|
|
pending.tail = node;
|
|
}
|
|
|
|
pending.count++;
|
|
}
|
|
|
|
static void pending_gc(__u64 min_seq)
|
|
{
|
|
struct cmd_node *cur = pending.head;
|
|
|
|
int n = pending.count;
|
|
while (cur && cur->seq < min_seq) {
|
|
struct cmd_node *tmp = cur;
|
|
cur = cur->next;
|
|
|
|
free(tmp->cmd);
|
|
free(tmp);
|
|
pending.count--;
|
|
}
|
|
|
|
printf("gc:%d\n", n-pending.count);
|
|
|
|
pending.head = cur;
|
|
if (!cur)
|
|
pending.tail = NULL;
|
|
}
|
|
|
|
static void pending_send_one(struct cmd_node *node){
|
|
int rt = send(sockfd, node->cmd, node->len, 0);
|
|
printf("send seq:%lld, rt=%d\n", node->seq, rt);
|
|
}
|
|
|
|
static void pending_send_all(void)
|
|
{
|
|
struct cmd_node *cur = pending.head;
|
|
|
|
while (cur) {
|
|
pending_send_one(cur);
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
/* ================= 网络逻辑 ================= */
|
|
static void try_connect(void)
|
|
{
|
|
if(sockfd > 0){
|
|
close(sockfd);
|
|
sockfd = -1;
|
|
}
|
|
|
|
struct sockaddr_in addr = {};
|
|
int i = 0;
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(peer_port);
|
|
inet_pton(AF_INET, peer_ip, &addr.sin_addr);
|
|
|
|
for(i = 0;i < 10; ++ i){
|
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sockfd < 0) {
|
|
perror("socket");
|
|
return;
|
|
}
|
|
|
|
printf("connect try %d...\n", i + 1);
|
|
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
|
printf("connect success: %s:%d\n", peer_ip, peer_port);
|
|
state = ONLINE;
|
|
pending_send_all();
|
|
return;
|
|
}
|
|
|
|
perror("connect");
|
|
close(sockfd);
|
|
sockfd = -1;
|
|
|
|
sleep(1);
|
|
}
|
|
|
|
printf("connect failed after 10 retries\n");
|
|
}
|
|
|
|
|
|
/* ================= perf buffer 回调 ================= */
|
|
static void handle_event(void *ctx, int cpu, void *data, __u32 size)
|
|
{
|
|
struct replica_event *evt = data;
|
|
|
|
switch (evt->type) {
|
|
|
|
case EVENT_SSYNC:
|
|
strncpy(peer_ip, evt->sync.ip, sizeof(peer_ip));
|
|
peer_port = evt->sync.port;
|
|
peer_seq = evt->sync.seq;
|
|
printf("SSYNC [seq:%lld], [%s:%d]\n", peer_seq, peer_ip, peer_port);
|
|
|
|
state = OFFLINE;
|
|
pending_gc(peer_seq);
|
|
break;
|
|
|
|
case EVENT_COMPLETED_CMD:
|
|
// printf("CMD [seq:%lld], cmd:\n[\n%s]\n", evt->complete.seq, evt->complete.cmd);
|
|
pending_push(evt->complete.seq,
|
|
evt->complete.len,
|
|
evt->complete.cmd);
|
|
|
|
if (state == ONLINE && pending.tail) {
|
|
struct cmd_node *n = pending.tail;
|
|
pending_send_one(n);
|
|
}
|
|
break;
|
|
|
|
case EVENT_SREADY:
|
|
printf("SREADY \n");
|
|
if (state == OFFLINE)
|
|
try_connect();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ================= main ================= */
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct replica_bpf *skel;
|
|
struct perf_buffer *pb = NULL;
|
|
int err;
|
|
|
|
/* Open BPF application */
|
|
skel = replica_bpf__open();
|
|
if (!skel) {
|
|
fprintf(stderr, "Failed to open BPF skeleton\n");
|
|
return 1;
|
|
}
|
|
|
|
/* Load & verify BPF programs */
|
|
err = replica_bpf__load(skel);
|
|
if (err) {
|
|
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Attach tracepoint handler */
|
|
err = replica_bpf__attach(skel);
|
|
if (err) {
|
|
fprintf(stderr, "Failed to attach BPF skeleton\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
|
|
"to see output of the BPF programs.\n");
|
|
|
|
|
|
pb = perf_buffer__new(bpf_map__fd(skel->maps.events), 8,
|
|
handle_event, NULL, NULL, NULL);
|
|
|
|
if(!pb){
|
|
goto cleanup;
|
|
}
|
|
|
|
while (1) {
|
|
perf_buffer__poll(pb, 1000); // 处理事件
|
|
}
|
|
|
|
perf_buffer__free(pb);
|
|
|
|
cleanup:
|
|
pending_free();
|
|
if (sockfd >= 0) close(sockfd);
|
|
replica_bpf__destroy(skel);
|
|
return -err;
|
|
}
|