|
| 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