Skip to content

Commit a75a9ba

Browse files
committed
📝 doc: improve docs, change arch
1 parent 90feab8 commit a75a9ba

File tree

3 files changed

+288
-122
lines changed

3 files changed

+288
-122
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
module github.com/breayhing/reader_writer
1+
module github.com/breayhing/rdmahandler
22

33
go 1.21.3

handler.go

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// Package rdmahandler 提供了用于RDMA(Remote Direct Memory Access)通信的处理程序和接口。
2+
// 它封装了与RDMA相关的操作,允许用户方便地初始化服务器或客户端,进行数据读写,以及销毁资源。
3+
package rdmahandler
4+
5+
/*
6+
#cgo LDFLAGS: -libverbs
7+
#include "rdma_operations.h"
8+
*/
9+
import "C"
10+
import (
11+
"fmt"
12+
"unsafe"
13+
)
14+
15+
type RDMACommunicator interface {
16+
InitServer(port int) (*RDMAResources, error)
17+
InitClient(ip string, port int) (*RDMAResources, error)
18+
Write(res *RDMAResources, contents string, character string) error
19+
Read(res *RDMAResources, character string) (string, error)
20+
Destroy(res *RDMAResources) error
21+
}
22+
23+
// RDMAHandler 实现了 RDMACommunicator 接口,提供了具体的 RDMA 通信功能。
24+
// 它包含了为 RDMA 通信所需的所有操作,包括服务器和客户端的初始化、
25+
// 数据读写,以及资源的释放。
26+
//
27+
// 作为 RDMACommunicator 的具体实现,RDMAHandler 提供了这些方法的具体逻辑,
28+
// 使得可以在 RDMA 网络环境中高效地进行数据传输和管理。
29+
//
30+
// Example of usage:
31+
//
32+
// handler := rdmahandler.RDMAHandler{}
33+
// res, err := handler.InitServer(8080)
34+
// if err != nil {
35+
// log.Fatalf("Server initialization failed: %v", err)
36+
// }
37+
// // Use handler to perform RDMA operations
38+
// ...
39+
type RDMAHandler struct{}
40+
41+
// InitServer initializes an RDMA server on the specified port. It sets up
42+
// the necessary RDMA resources and returns a pointer to these resources along with
43+
// any error encountered during the setup.
44+
//
45+
// `port` is the port number on which the RDMA server will listen. It should be a valid
46+
// port number where the server has permissions to bind.
47+
//
48+
// On success, it returns a pointer to the initialized RDMAResources and nil error.
49+
// On failure, it returns nil and the error encountered.
50+
//
51+
// Example:
52+
//
53+
// res, err := h.InitServer(8080)
54+
// if err != nil {
55+
// log.Fatalf("Failed to initialize RDMA server: %v", err)
56+
// }
57+
// // Use res (RDMAResources) as needed
58+
// ...
59+
func (h *RDMAHandler) InitServer(port int) (*RDMAResources, error) {
60+
return initRDMAConnection("", port)
61+
}
62+
63+
// InitClient establishes a connection to an RDMA server at the specified IP address and port.
64+
// It initializes the client-side RDMA resources and returns a pointer to these resources, along with
65+
// any error encountered during the connection and initialization process.
66+
//
67+
// `ip` is the IP address of the RDMA server to connect to. It should be a valid IPv4 or IPv6 address.
68+
// `port` is the port number on which the RDMA server is listening. It should be a valid port number
69+
// where the server is expecting connections.
70+
//
71+
// On success, it returns a pointer to the initialized RDMAResources and nil error. On failure, it
72+
// returns nil and the error encountered.
73+
//
74+
// Example:
75+
//
76+
// clientRes, err := h.InitClient("192.168.1.10", 8080)
77+
// if err != nil {
78+
// log.Fatalf("Failed to initialize RDMA client: %v", err)
79+
// }
80+
// // Use clientRes (RDMAResources) for client-side operations
81+
// ...
82+
func (h *RDMAHandler) InitClient(ip string, port int) (*RDMAResources, error) {
83+
return initRDMAConnection(ip, port)
84+
}
85+
86+
// Write sends the given contents to a remote RDMA peer using the specified RDMAResources.
87+
//
88+
// It performs an RDMA write operation and ensures the data synchronization.
89+
//
90+
// `res` is a pointer to RDMAResources which should be previously initialized and represent
91+
// an established RDMA connection.
92+
//
93+
// `contents` is the string data to be sent to the remote peer.
94+
//
95+
// `character` is used in error messages to identify the operation or the role of the peer
96+
// (e.g., "client" or "server").
97+
//
98+
// This function first synchronizes the data, then performs the RDMA write operation, and
99+
// finally checks for completion. Any error encountered during these steps is returned.
100+
//
101+
// On success, it returns nil. On failure, it returns an error detailing the issue encountered.
102+
//
103+
// Example:
104+
//
105+
// err := h.Write(clientRes, "Hello RDMA", "client")
106+
// if err != nil {
107+
// log.Fatalf("RDMA write failed: %v", err)
108+
// }
109+
func (h *RDMAHandler) Write(res *RDMAResources, contents string, character string) error {
110+
if err := syncData(res); err != nil {
111+
return err
112+
}
113+
cContents := C.CString(contents)
114+
defer C.free(unsafe.Pointer(cContents))
115+
116+
C.strcpy(res.res.buf, cContents)
117+
118+
if C.post_send(&res.res, C.IBV_WR_RDMA_WRITE) != 0 {
119+
return fmt.Errorf("%s: failed to post SR", character)
120+
}
121+
if C.poll_completion(&res.res) != 0 {
122+
return fmt.Errorf("%s: poll completion failed", character)
123+
}
124+
if err := syncData(res); err != nil {
125+
return err
126+
}
127+
return nil
128+
}
129+
130+
// Read performs an RDMA read operation using the given RDMAResources and retrieves data from a remote RDMA peer.
131+
//
132+
// `res` is a pointer to RDMAResources that must be previously initialized and represent
133+
// an established RDMA connection.
134+
//
135+
// `character` is a string used to identify the operation or the role of the peer in error messages
136+
// (e.g., "client" or "server").
137+
//
138+
// This function synchronizes the data before and after the RDMA read operation. If any error
139+
// occurs during these steps, the function returns an empty string along with the error.
140+
//
141+
// On successful completion of the read operation, it returns the read data as a string and nil error.
142+
// On failure, it returns an empty string and the error encountered.
143+
//
144+
// Example:
145+
//
146+
// data, err := h.Read(serverRes, "server")
147+
// if err != nil {
148+
// log.Fatalf("RDMA read failed: %v", err)
149+
// }
150+
// fmt.Println("Received data:", data)
151+
func (h *RDMAHandler) Read(res *RDMAResources, character string) (string, error) {
152+
if err := syncData(res); err != nil {
153+
return "", err
154+
}
155+
if C.post_send(&res.res, C.IBV_WR_RDMA_READ) != 0 {
156+
return "", fmt.Errorf("%s: failed to post SR", character)
157+
}
158+
if C.poll_completion(&res.res) != 0 {
159+
return "", fmt.Errorf("%s: poll completion after post_send failed", character)
160+
}
161+
if err := syncData(res); err != nil {
162+
return "", err
163+
}
164+
return C.GoString(res.res.buf), nil
165+
}
166+
167+
// Destroy releases the resources allocated for an RDMA connection.
168+
//
169+
// `res` is a pointer to RDMAResources that should be previously allocated and used
170+
// for an RDMA connection. This function is responsible for properly releasing these
171+
// resources to avoid resource leaks.
172+
//
173+
// It attempts to destroy the RDMA resources by calling the appropriate C function.
174+
// If the resources cannot be successfully destroyed, the function returns an error
175+
// detailing the failure.
176+
//
177+
// On success, it returns nil, indicating the resources were successfully released.
178+
// On failure, it returns an error.
179+
//
180+
// Example:
181+
//
182+
// err := h.Destroy(clientRes)
183+
// if err != nil {
184+
// log.Fatalf("Failed to destroy RDMA resources: %v", err)
185+
// }
186+
func (h *RDMAHandler) Destroy(res *RDMAResources) error {
187+
if C.resources_destroy(&res.res) != 0 {
188+
189+
return fmt.Errorf("failed to destroy resources")
190+
}
191+
return nil
192+
}
193+
194+
// RDMAResources encapsulates the resources required for establishing and managing
195+
// an RDMA (Remote Direct Memory Access) connection. It serves as a wrapper around
196+
// the C-level struct_resources, providing a Go-friendly interface for RDMA operations.
197+
//
198+
// The `res` field is an instance of C.struct_resources, which holds the necessary
199+
// RDMA resources and configurations such as the protection domain, memory regions,
200+
// queue pairs, and other essential components for establishing RDMA connections.
201+
//
202+
// This struct is used throughout the RDMA handling code to maintain the state and
203+
// resources of an RDMA connection, either as a client or a server.
204+
//
205+
// Example of usage:
206+
//
207+
// var resources RDMAResources
208+
// // Initialize RDMA resources for a client or server
209+
// // Use resources in RDMA operations such as Read, Write, etc.
210+
// ...
211+
type RDMAResources struct {
212+
res C.struct_resources
213+
}
214+
215+
// initRDMAConnection initializes the RDMA resources and establishes a connection
216+
// either as a client or a server based on the provided IP address.
217+
//
218+
// `ip` is the IP address of the RDMA server to connect to. If `ip` is an empty string,
219+
// the function sets up as a server, otherwise it sets up as a client.
220+
//
221+
// `port` is the port number used for the RDMA connection.
222+
//
223+
// This function configures the RDMA connection parameters, creates the necessary
224+
// resources, and connects the queue pairs (QPs). If any step in this process fails,
225+
// it cleans up any partially created resources and returns an error.
226+
//
227+
// On success, it returns a pointer to the initialized RDMAResources and nil error.
228+
// On failure, it returns nil and an error explaining the failure.
229+
//
230+
// Example:
231+
//
232+
// res, err := initRDMAConnection("192.168.1.10", 8080)
233+
// if err != nil {
234+
// log.Fatalf("RDMA connection initialization failed: %v", err)
235+
// }
236+
func initRDMAConnection(ip string, port int) (*RDMAResources, error) {
237+
var resources RDMAResources
238+
239+
serverAddr := C.CString(ip)
240+
defer C.free(unsafe.Pointer(serverAddr))
241+
242+
if ip != "" {
243+
fmt.Println("client now setting up")
244+
C.config.server_name = serverAddr
245+
} else {
246+
fmt.Println("server now setting up")
247+
C.config.server_name = nil
248+
}
249+
C.config.tcp_port = C.uint32_t(port)
250+
251+
if C.resources_create(&resources.res) != 0 {
252+
return nil, fmt.Errorf("failed to create resources")
253+
}
254+
if C.connect_qp(&resources.res) != 0 {
255+
C.resources_destroy(&resources.res)
256+
return nil, fmt.Errorf("failed to connect QPs")
257+
}
258+
return &resources, nil
259+
}
260+
261+
// syncData synchronizes data over the socket associated with the provided RDMA resources.
262+
//
263+
// `res` is a pointer to RDMAResources which should be previously initialized and represent
264+
// an established RDMA connection.
265+
//
266+
// This function attempts to synchronize data across the connection by sending a single
267+
// character ('R') and expecting to receive a character back. This ensures both sides of
268+
// the RDMA connection are ready to proceed with further operations.
269+
//
270+
// If the synchronization fails, the function returns an error detailing the issue.
271+
//
272+
// On success, it returns nil, indicating successful synchronization.
273+
// On failure, it returns an error.
274+
//
275+
// Example:
276+
//
277+
// err := syncData(serverRes)
278+
// if err != nil {
279+
// log.Fatalf("Data synchronization failed: %v", err)
280+
// }
281+
func syncData(res *RDMAResources) error {
282+
var tempChar C.char
283+
if C.sock_sync_data(res.res.sock, 1, C.CString("R"), &tempChar) != 0 {
284+
return fmt.Errorf("sync error")
285+
}
286+
return nil
287+
}

0 commit comments

Comments
 (0)