feat(rdma): Add rdma bench mark

Signed-off-by: Wu Huocheng <wuhuocheng@oppo.com>
This commit is contained in:
aaronwu2010 2024-12-04 15:07:09 +08:00
parent 49233e0a35
commit 65726f617b
42 changed files with 6186 additions and 0 deletions

View File

@ -0,0 +1,67 @@
分到拷贝到两台机器上之后分别进到client和server执行go build之后就是如下命令
[service@server-machine server]$ ./server -h
Usage of ./server:
-deep int
io deep (default 1)
-ip string
127.0.0.1 (default "127.0.0.1")
-port string
9000 (default "9000")
-protocol string
rdma/tcp (default "rdma")
-size int
io size (default 4096)
[service@client-machine client]$ ./client -h
Usage of ./client:
-deep int
io deep (default 1)
-ip string
127.0.0.1 (default "127.0.0.1")
-port string
9000 (default "9000")
-protocol string
rdma/tcp (default "rdma")
-size int
io size (default 4096)
-----------------------------------------------------------------------------------------------------------------------
[service@client-machine client]$ ./client -ip 192.168.12.100 -protocol tcp -port 17370 -ip 192.168.12.100
param {1 4096 tcp 192.168.12.100 17370}
IOPS=[10792.00], TOTAL_BPS=[84.31M], AVG_TM=[92.55us]
IOPS=[10316.00], TOTAL_BPS=[80.59M], AVG_TM=[96.83us]
IOPS=[9843.33], TOTAL_BPS=[76.90M], AVG_TM=[101.49us]
IOPS=[9784.50], TOTAL_BPS=[76.44M], AVG_TM=[102.10us]
IOPS=[9676.60], TOTAL_BPS=[75.60M], AVG_TM=[103.24us]
^C
[service@client-machine client]$ ./client -ip 192.168.12.100 -protocol rdma -port 17370 -ip 192.168.12.100
param {1 4096 rdma 192.168.12.100 17370}
IOPS=[54937.00], TOTAL_BPS=[429.20M], AVG_TM=[16.69us]
IOPS=[59708.50], TOTAL_BPS=[466.48M], AVG_TM=[16.03us]
IOPS=[61561.00], TOTAL_BPS=[480.95M], AVG_TM=[15.76us]
-----------------------------------------------------------------------------------------------------------------------
[service@server-machine server]$ ./server -protocol tcp -port 17370 -ip 192.168.12.100
param {1 4096 tcp 192.168.12.100 17370}
IOPS=[0.00], TOTAL_BPS=[0.00M], AVG_TM=[NaNus]
IOPS=[0.00], TOTAL_BPS=[0.00M], AVG_TM=[NaNus]
IOPS=[0.00], TOTAL_BPS=[0.00M], AVG_TM=[NaNus]
IOPS=[10017.00], TOTAL_BPS=[78.26M], AVG_TM=[92.40us]
IOPS=[10007.00], TOTAL_BPS=[78.18M], AVG_TM=[96.18us]
IOPS=[9610.33], TOTAL_BPS=[75.08M], AVG_TM=[101.43us]
IOPS=[9617.75], TOTAL_BPS=[75.14M], AVG_TM=[102.00us]
IOPS=[9543.20], TOTAL_BPS=[74.56M], AVG_TM=[103.18us]
^C
[service@server-machine server]$
[service@server-machine server]$ ./server -protocol rdma -port 17370 -ip 192.168.12.100
param {1 4096 rdma 192.168.12.100 17370}
IOPS=[0.00], TOTAL_BPS=[0.00M], AVG_TM=[NaNus]
IOPS=[0.00], TOTAL_BPS=[0.00M], AVG_TM=[NaNus]
IOPS=[59072.00], TOTAL_BPS=[461.50M], AVG_TM=[16.69us]
IOPS=[62272.50], TOTAL_BPS=[486.50M], AVG_TM=[15.91us]
IOPS=[62930.33], TOTAL_BPS=[491.64M], AVG_TM=[15.78us]
-----------------------------------------------------------------------------------------------------------------------

Binary file not shown.

View File

@ -0,0 +1,126 @@
package main
import (
"net"
"rdma_test/common"
"rdma_test/rdma"
"time"
)
var exit = false
var Config = &rdma.RdmaEnvConfig{}
func ReadBytes(conn net.Conn, buf []byte) error {
offset := 0
for offset < len(buf) {
n, err := conn.Read(buf[offset:])
if n == -1 || (err != nil) {
println("ReadBytes failed, err: ", err)
return err
}
offset += n
}
return nil
}
func testRdma() {
if err := rdma.InitPool(Config); err != nil {
println("init rdma pool failed")
return
}
for i := 0; i < common.GParam.IoDeep; i++ {
go func() {
conn := &rdma.Connection{}
if err := conn.Dial(common.GParam.Ip,common.GParam.Port); err != nil {
println("client rdma conn dial failed")
return
}
defer conn.Close()
for !exit {
beginTm := time.Now()
p := common.NewWritePacket(common.NormalExtentType, conn)
p.Size = uint32(common.GParam.IoSize)
if err := p.WriteToRDMAConn(conn); err != nil {
println(err.Error())
break
}
reply := common.NewReply(p.ReqID)
if err := reply.RecvRespFromRDMAConn(conn, 5); err != nil {
println(err.Error())
break
}
err := conn.ReleaseConnRxDataBuffer(reply.RdmaBuffer)
if err != nil {
println(err.Error())
break
}
err = rdma.ReleaseDataBuffer(conn, p.RdmaBuffer, uint32(105 + common.GParam.IoSize))
if err != nil {
println(err.Error())
break
}
common.Stat().AddSumTime(common.GParam.IoSize, time.Now().UnixNano() / 1000 - beginTm.UnixNano() / 1000)
}
}()
}
}
func testTcp() {
common.InitBufferPool(0)
for i := 0; i < common.GParam.IoDeep; i++ {
go func() {
c, err := net.Dial("tcp", common.GParam.Ip + ":" + common.GParam.Port)
if err != nil {
println(err.Error())
return
}
conn, _ := c.(*net.TCPConn)
conn.SetKeepAlive(true)
conn.SetNoDelay(true)
defer conn.Close()
for !exit {
beginTm := time.Now()
p := common.NewWritePacket(common.NormalExtentType, conn)
p.Size = uint32(common.GParam.IoSize)
if err := p.WriteToConn(conn); err != nil {
println(err.Error())
break
}
reply := common.NewReply(p.ReqID)
if err := reply.ReadFromConn(conn, 5); err != nil {
println(err.Error())
break
}
common.Stat().AddSumTime(common.GParam.IoSize, time.Now().UnixNano() / 1000 - beginTm.UnixNano() / 1000)
}
}()
}
}
func main() {
common.ParseParam()
if common.GParam.Protocol == "rdma" {
go testRdma()
} else {
go testTcp()
}
for !exit {
time.Sleep(time.Millisecond * 1000)
common.Stat().Print()
}
}

View File

@ -0,0 +1,178 @@
package common
import (
"fmt"
"rdma_test/common/context"
"rdma_test/common/rate"
"sync"
"sync/atomic"
)
const (
HeaderBufferPoolSize = 8192
InvalidLimit = 0
)
var ReadBufPool = sync.Pool{
New: func() interface{} {
b := make([]byte, 32*1024)
return b
},
}
var tinyBuffersTotalLimit int64 = 4096
var NormalBuffersTotalLimit int64
var HeadBuffersTotalLimit int64
var tinyBuffersCount int64
var normalBuffersCount int64
var headBuffersCount int64
var normalBufAllocId uint64
var headBufAllocId uint64
var normalBufFreecId uint64
var headBufFreeId uint64
var buffersRateLimit = rate.NewLimiter(rate.Limit(16), 16)
var normalBuffersRateLimit = rate.NewLimiter(rate.Limit(16), 16)
var headBuffersRateLimit = rate.NewLimiter(rate.Limit(16), 16)
func NewTinyBufferPool() *sync.Pool {
return &sync.Pool{
New: func() interface{} {
if atomic.LoadInt64(&tinyBuffersCount) >= tinyBuffersTotalLimit {
ctx := context.Background()
buffersRateLimit.Wait(ctx)
}
return make([]byte, DefaultTinySizeLimit)
},
}
}
func NewHeadBufferPool() *sync.Pool {
return &sync.Pool{
New: func() interface{} {
if HeadBuffersTotalLimit != InvalidLimit && atomic.LoadInt64(&headBuffersCount) >= HeadBuffersTotalLimit {
ctx := context.Background()
headBuffersRateLimit.Wait(ctx)
}
return make([]byte, PacketHeaderSize)
},
}
}
func NewNormalBufferPool() *sync.Pool {
return &sync.Pool{
New: func() interface{} {
if NormalBuffersTotalLimit != InvalidLimit && atomic.LoadInt64(&normalBuffersCount) >= NormalBuffersTotalLimit {
ctx := context.Background()
normalBuffersRateLimit.Wait(ctx)
}
return make([]byte, BlockSize)
},
}
}
// BufferPool defines the struct of a buffered pool with 4 objects.
type BufferPool struct {
headPools []chan []byte
normalPools []chan []byte
tinyPool *sync.Pool
headPool *sync.Pool
normalPool *sync.Pool
}
var (
slotCnt = uint64(16)
)
// NewBufferPool returns a new buffered pool.
func NewBufferPool() (bufferP *BufferPool) {
bufferP = &BufferPool{}
bufferP.headPools = make([]chan []byte, slotCnt)
bufferP.normalPools = make([]chan []byte, slotCnt)
for i := 0; i < int(slotCnt); i++ {
bufferP.headPools[i] = make(chan []byte, HeaderBufferPoolSize/slotCnt)
bufferP.normalPools[i] = make(chan []byte, HeaderBufferPoolSize/slotCnt)
}
bufferP.tinyPool = NewTinyBufferPool()
bufferP.headPool = NewHeadBufferPool()
bufferP.normalPool = NewNormalBufferPool()
return bufferP
}
func (bufferP *BufferPool) getHead(id uint64) (data []byte) {
select {
case data = <-bufferP.headPools[id%slotCnt]:
return
default:
return bufferP.headPool.Get().([]byte)
}
}
func (bufferP *BufferPool) getNoraml(id uint64) (data []byte) {
select {
case data = <-bufferP.normalPools[id%slotCnt]:
return
default:
return bufferP.normalPool.Get().([]byte)
}
}
// Get returns the data based on the given size. Different size corresponds to different object in the pool.
func (bufferP *BufferPool) Get(size int) (data []byte, err error) {
if size == PacketHeaderSize {
atomic.AddInt64(&headBuffersCount, 1)
id := atomic.AddUint64(&headBufAllocId, 1)
return bufferP.getHead(id), nil
} else if size == BlockSize {
atomic.AddInt64(&normalBuffersCount, 1)
id := atomic.AddUint64(&normalBufAllocId, 1)
return bufferP.getNoraml(id), nil
} else if size == DefaultTinySizeLimit {
atomic.AddInt64(&tinyBuffersCount, 1)
return bufferP.tinyPool.Get().([]byte), nil
}
return nil, fmt.Errorf("can only support 45 or 65536 bytes")
}
func (bufferP *BufferPool) putHead(index int, data []byte) {
select {
case bufferP.headPools[index] <- data:
return
default:
bufferP.headPool.Put(data)
}
}
func (bufferP *BufferPool) putNormal(index int, data []byte) {
select {
case bufferP.normalPools[index] <- data:
return
default:
bufferP.normalPool.Put(data)
}
}
// Put puts the given data into the buffer pool.
func (bufferP *BufferPool) Put(data []byte) {
if data == nil {
return
}
size := len(data)
if size == PacketHeaderSize {
atomic.AddInt64(&headBuffersCount, -1)
id := atomic.AddUint64(&headBufFreeId, 1)
bufferP.putHead(int(id%slotCnt), data)
} else if size == BlockSize {
atomic.AddInt64(&normalBuffersCount, -1)
id := atomic.AddUint64(&normalBufFreecId, 1)
bufferP.putNormal(int(id%slotCnt), data)
} else if size == DefaultTinySizeLimit {
bufferP.tinyPool.Put(data)
atomic.AddInt64(&tinyBuffersCount, -1)
}
return
}

View File

@ -0,0 +1,56 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
// As of Go 1.7 this package is available in the standard library under the
// name context. https://golang.org/pkg/context.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
// func DoSomething(ctx context.Context, arg Arg) error {
// // ... use ctx ...
// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See http://blog.golang.org/context for example code for a server that uses
// Contexts.
package context // import "golang.org/x/net/context"
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return todo
}

View File

@ -0,0 +1,73 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.7
// +build go1.7
package context
import (
"context" // standard library's context, as of Go 1.7
"time"
)
var (
todo = context.TODO()
background = context.Background()
)
// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = context.Canceled
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = context.DeadlineExceeded
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
ctx, f := context.WithCancel(parent)
return ctx, CancelFunc(f)
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
ctx, f := context.WithDeadline(parent, deadline)
return ctx, CancelFunc(f)
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
return context.WithValue(parent, key, val)
}

View File

@ -0,0 +1,21 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.9
// +build go1.9
package context
import "context" // standard library's context, as of Go 1.7
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context = context.Context
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc = context.CancelFunc

View File

@ -0,0 +1,301 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.7
// +build !go1.7
package context
import (
"errors"
"fmt"
"sync"
"time"
)
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = errors.New("context canceled")
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = errors.New("context deadline exceeded")
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, c)
return c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) *cancelCtx {
return &cancelCtx{
Context: parent,
done: make(chan struct{}),
}
}
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
if parent.Done() == nil {
return // parent is never canceled
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]bool)
}
p.children[child] = true
}
p.mu.Unlock()
} else {
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx. This function understands how each of the concrete types in this
// package represents its parent.
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
for {
switch c := parent.(type) {
case *cancelCtx:
return c, true
case *timerCtx:
return c.cancelCtx, true
case *valueCtx:
parent = c.Context
default:
return nil, false
}
}
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
done chan struct{} // closed by the first cancel call.
mu sync.Mutex
children map[canceler]bool // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
func (c *cancelCtx) Done() <-chan struct{} {
return c.done
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.err
}
func (c *cancelCtx) String() string {
return fmt.Sprintf("%v.WithCancel", c.Context)
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
close(c.done)
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: deadline,
}
propagateCancel(parent, c)
d := deadline.Sub(time.Now())
if d <= 0 {
c.cancel(true, DeadlineExceeded) // deadline has already passed
return c, func() { c.cancel(true, Canceled) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(d, func() {
c.cancel(true, DeadlineExceeded)
})
}
return c, func() { c.cancel(true, Canceled) }
}
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
*cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
return c.deadline, true
}
func (c *timerCtx) String() string {
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
}
func (c *timerCtx) cancel(removeFromParent bool, err error) {
c.cancelCtx.cancel(false, err)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
return &valueCtx{parent, key, val}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
Context
key, val interface{}
}
func (c *valueCtx) String() string {
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}

View File

@ -0,0 +1,110 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.9
// +build !go1.9
package context
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

View File

@ -0,0 +1,379 @@
package common
import (
"encoding/binary"
"errors"
"io"
"net"
"rdma_test/rdma"
"strconv"
"sync/atomic"
"syscall"
"time"
)
const (
_ = iota
KB = 1 << (10 * iota)
MB
GB
TB
PB
OpWrite uint8 = 0x03
OpOk uint8 = 0xF0
ProtoMagic uint8 = 0xFF
TinyExtentType = 0
NormalExtentType = 1
BlockCount = 1024
BlockSize = 65536 * 2
ReadBlockSize = BlockSize
PerBlockCrcSize = 4
ExtentSize = BlockCount * BlockSize
PacketHeaderSize = 57
BlockHeaderSize = 4096
SyscallTryMaxTimes = 3
DefaultTinySizeLimit = 1 * MB
)
var(
GRequestID = int64(1)
Buffers *BufferPool
)
func InitBufferPool(bufLimit int64) {
NormalBuffersTotalLimit = bufLimit
HeadBuffersTotalLimit = bufLimit
Buffers = NewBufferPool()
}
func GenerateRequestID() int64 {
return atomic.AddInt64(&GRequestID, 1)
}
type Packet struct {
Magic uint8
ExtentType uint8
Opcode uint8
ResultCode uint8
RemainingFollowers uint8
CRC uint32
Size uint32
ArgLen uint32
KernelOffset uint64
PartitionID uint64
ExtentID uint64
ExtentOffset int64
ReqID int64
Arg []byte // for create or append ops, the data contains the address
Data []byte
RdmaBuffer []byte
StartT int64
mesg string
HasPrepare bool
}
func NewWritePacket(storeMode int, c net.Conn) *Packet {
p := new(Packet)
p.ReqID = GenerateRequestID()
p.Magic = ProtoMagic
p.Opcode = OpWrite
if storeMode == TinyExtentType {
if GParam.Protocol == "rdma" {
dataBuffer,_ := rdma.GetDataBuffer(DefaultTinySizeLimit + 105)
p.Arg = dataBuffer[65:105]
p.Data = dataBuffer[105:]
p.RdmaBuffer = dataBuffer
} else {
p.Arg = make([]byte, 40)
p.Data, _ = Buffers.Get(DefaultTinySizeLimit)
}
} else {
if GParam.Protocol == "rdma" {
dataBuffer,_ := rdma.GetDataBuffer(BlockSize + 105)
p.Arg = dataBuffer[65:105]
p.Data = dataBuffer[105:]
p.RdmaBuffer = dataBuffer
} else {
p.Arg = make([]byte, 40)
p.Data, _ = Buffers.Get(BlockSize)
}
}
return p
}
func NewReply(reqID int64) *Packet {
p := new(Packet)
p.ReqID = reqID
p.Magic = ProtoMagic
p.ExtentType = NormalExtentType
return p
}
func NewPacket() (p *Packet) {
p = new(Packet)
p.Magic = ProtoMagic
p.StartT = time.Now().UnixNano()
return
}
func (p *Packet) PacketOkReply() {
p.ResultCode = OpOk
p.Size = 0
p.Data = nil
p.ArgLen = 0
}
func (p *Packet) MarshalHeader(out []byte) {
out[0] = p.Magic
out[1] = p.ExtentType
out[2] = p.Opcode
out[3] = p.ResultCode
out[4] = p.RemainingFollowers
binary.BigEndian.PutUint32(out[5:9], p.CRC)
binary.BigEndian.PutUint32(out[9:13], p.Size)
binary.BigEndian.PutUint32(out[13:17], p.ArgLen)
binary.BigEndian.PutUint64(out[17:25], p.PartitionID)
binary.BigEndian.PutUint64(out[25:33], p.ExtentID)
binary.BigEndian.PutUint64(out[33:41], uint64(p.ExtentOffset))
binary.BigEndian.PutUint64(out[41:49], uint64(p.ReqID))
binary.BigEndian.PutUint64(out[49:PacketHeaderSize], p.KernelOffset)
return
}
func (p *Packet) UnmarshalHeader(in []byte) error {
p.Magic = in[0]
if p.Magic != ProtoMagic {
return errors.New("Bad Magic " + strconv.Itoa(int(p.Magic)))
}
p.ExtentType = in[1]
p.Opcode = in[2]
p.ResultCode = in[3]
p.RemainingFollowers = in[4]
p.CRC = binary.BigEndian.Uint32(in[5:9])
p.Size = binary.BigEndian.Uint32(in[9:13])
p.ArgLen = binary.BigEndian.Uint32(in[13:17])
p.PartitionID = binary.BigEndian.Uint64(in[17:25])
p.ExtentID = binary.BigEndian.Uint64(in[25:33])
p.ExtentOffset = int64(binary.BigEndian.Uint64(in[33:41]))
p.ReqID = int64(binary.BigEndian.Uint64(in[41:49]))
p.KernelOffset = binary.BigEndian.Uint64(in[49:PacketHeaderSize])
return nil
}
func (p *Packet) WriteToRDMAConn(conn *rdma.Connection) (err error) {
offset := 0
p.MarshalHeader(p.RdmaBuffer[0:PacketHeaderSize])
offset += PacketHeaderSize + 8
offset += 40
if _, err = conn.WriteExternalBuffer(p.RdmaBuffer, int(105 + p.Size)); err != nil {
return
}
return
}
func (p *Packet) WriteToConn(c net.Conn) (err error) {
header, err := Buffers.Get(PacketHeaderSize)
if err != nil {
header = make([]byte, PacketHeaderSize)
}
defer Buffers.Put(header)
c.SetWriteDeadline(time.Now().Add(5 * time.Second))
p.MarshalHeader(header)
if _, err = c.Write(header); err == nil {
if _, err = c.Write(p.Arg[:int(p.ArgLen)]); err == nil {
if p.Data != nil && p.Size != 0 {
_, err = c.Write(p.Data[:p.Size])
}
}
}
return
}
func (p *Packet) RecvRespFromRDMAConn(c *rdma.Connection, timeoutSec int) (err error) {
var dataBuffer []byte
var offset uint32 = 0
if dataBuffer, err = c.GetRecvMsgBuffer(); err != nil {
return
}
p.RdmaBuffer = dataBuffer
if err = p.UnmarshalHeader(dataBuffer[0:PacketHeaderSize]); err != nil {
c.ReleaseConnRxDataBuffer(dataBuffer)
return
}
offset += PacketHeaderSize + 8
if p.ArgLen > 0 {
p.Arg = dataBuffer[65:65+p.ArgLen]
}
offset += 40
if p.Size < 0 {
c.ReleaseConnRxDataBuffer(dataBuffer)
return syscall.EBADMSG
}
size := p.Size
p.Data = dataBuffer[105:105+int(size)]
return
}
func (p *Packet) ReadFromConn(c net.Conn, timeoutSec int) (err error) {
if timeoutSec != -1 {
c.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeoutSec)))
} else {
c.SetReadDeadline(time.Time{})
}
header, err := Buffers.Get(PacketHeaderSize)
if err != nil {
header = make([]byte, PacketHeaderSize)
}
defer Buffers.Put(header)
var n int
if n, err = io.ReadFull(c, header); err != nil {
return
}
if n != PacketHeaderSize {
return syscall.EBADMSG
}
if err = p.UnmarshalHeader(header); err != nil {
return
}
if p.ArgLen > 0 {
p.Arg = make([]byte, int(p.ArgLen))
if _, err = io.ReadFull(c, p.Arg[:int(p.ArgLen)]); err != nil {
return err
}
}
if p.Size < 0 {
return syscall.EBADMSG
}
size := p.Size
p.Data = make([]byte, size)
if n, err = io.ReadFull(c, p.Data[:size]); err != nil {
return err
}
if n != int(size) {
return syscall.EBADMSG
}
return nil
}
func (p *Packet) ReadFromRDMAConnFromCli(conn *rdma.Connection, deadlineTime time.Duration) (err error) {
var dataBuffer []byte
var offset int
if dataBuffer, err = conn.GetRecvMsgBuffer(); err != nil {
return
}
p.RdmaBuffer = dataBuffer
if err = p.UnmarshalHeader(dataBuffer[0:PacketHeaderSize]); err != nil {
conn.ReleaseConnRxDataBuffer(dataBuffer)
return
}
offset += PacketHeaderSize + 8 //rdma
if p.ArgLen > 0 {
p.Arg = dataBuffer[65:65+p.ArgLen]
}
offset += 40
if p.Size < 0 {
conn.ReleaseConnRxDataBuffer(dataBuffer)
return
}
size := p.Size
p.Data = dataBuffer[105:105+int(size)]
return
}
func ReadFull(c net.Conn, buf *[]byte, readSize int) (err error) {
*buf = make([]byte, readSize)
_, err = io.ReadFull(c, (*buf)[:readSize])
return
}
func (p *Packet) ReadFull(c net.Conn, opcode uint8, readSize int) (err error) {
if readSize == BlockSize {
p.Data, _ = Buffers.Get(readSize)
} else {
p.Data = make([]byte, readSize)
}
_, err = io.ReadFull(c, p.Data[:readSize])
return
}
func (p *Packet) ReadFromTCPConnFromCli(c net.Conn, deadlineTime time.Duration) (err error) {
var header []byte
header, err = Buffers.Get(PacketHeaderSize)
if err != nil {
header = make([]byte, PacketHeaderSize)
}
defer Buffers.Put(header)
if _, err = io.ReadFull(c, header); err != nil {
return
}
if err = p.UnmarshalHeader(header); err != nil {
return
}
if p.ArgLen > 0 {
if err = ReadFull(c, &p.Arg, int(p.ArgLen)); err != nil {
return
}
}
if p.Size < 0 {
return
}
size := p.Size
return p.ReadFull(c, p.Opcode, int(size))
}
func (p *Packet) SendRespToRDMAConn(conn *rdma.Connection) (err error) {
var dataBuffer []byte
var offset uint32 = 0
if dataBuffer, err = conn.GetConnTxDataBuffer(145); err != nil {
return
}
defer func() {
if dataBuffer != nil {
if err = conn.ReleaseConnTxDataBuffer(dataBuffer); err != nil {
}
}
}()
p.MarshalHeader(dataBuffer[0:PacketHeaderSize])
offset += PacketHeaderSize + 8
if p.ArgLen != 0 {
copy(dataBuffer[65:105], p.Arg)
}
offset += 40
if p.Data != nil && p.Size != 0 {
copy(dataBuffer[105:105+int(p.Size)], p.Data)
}
if _, err = conn.WriteBuffer(dataBuffer, int(105+p.Size)); err != nil {
return
}
return
}

View File

@ -0,0 +1,30 @@
package common
import "C"
import (
"flag"
"fmt"
)
type Param struct {
IoDeep int
IoSize int
Protocol string
Ip string
Port string
}
var GParam = Param{}
func init() {
flag.IntVar(&GParam.IoDeep, "deep", 1, "io deep")
flag.IntVar(&GParam.IoSize, "size", 4096, "io size")
flag.StringVar(&GParam.Protocol, "protocol", "rdma", "rdma/tcp")
flag.StringVar(&GParam.Ip, "ip", "127.0.0.1", "127.0.0.1")
flag.StringVar(&GParam.Port, "port", "9000", "9000")
}
func ParseParam() {
flag.Parse()
fmt.Printf("param %v\n", GParam)
}

View File

@ -0,0 +1,40 @@
package common
import (
"fmt"
"time"
)
type NetPerfStat struct {
num int64
size int64
start int64
tmCost int64
}
var stat = NetPerfStat{}
func Stat() *NetPerfStat {
return &stat
}
func (s *NetPerfStat)AddSum(size int) {
s.num++
s.size += 2*int64(size) //send and recv
if s.start == 0 {
s.start = time.Now().Unix()
}
}
func (s *NetPerfStat)AddSumTime(size int, tm int64) {
s.AddSum(size)
s.tmCost += tm
}
func (s *NetPerfStat)Print() {
tmCost := time.Now().Unix() - s.start
//io ps band w
fmt.Printf("IOPS=[%.2f], TOTAL_BPS=[%.2fM], AVG_TM=[%.2fus]\n", float64(s.num)/float64(tmCost),
float64(s.size)/(1024*1024)/float64(tmCost), float64(s.tmCost)/float64(s.num))
}

View File

@ -0,0 +1,405 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package rate provides a rate limiter.
package rate
import (
"context"
"fmt"
"math"
"sync"
"time"
)
// Limit defines the maximum frequency of some events.
// Limit is represented as number of events per second.
// A zero Limit allows no events.
type Limit float64
// Inf is the infinite rate limit; it allows all events (even if burst is zero).
const Inf = Limit(math.MaxFloat64)
// Every converts a minimum time interval between events to a Limit.
func Every(interval time.Duration) Limit {
if interval <= 0 {
return Inf
}
return 1 / Limit(interval.Seconds())
}
// A Limiter controls how frequently events are allowed to happen.
// It implements a "token bucket" of size b, initially full and refilled
// at rate r tokens per second.
// Informally, in any large enough time interval, the Limiter limits the
// rate to r tokens per second, with a maximum burst size of b events.
// As a special case, if r == Inf (the infinite rate), b is ignored.
// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
//
// The zero value is a valid Limiter, but it will reject all events.
// Use NewLimiter to create non-zero Limiters.
//
// Limiter has three main methods, Allow, Reserve, and Wait.
// Most callers should use Wait.
//
// Each of the three methods consumes a single token.
// They differ in their behavior when no token is available.
// If no token is available, Allow returns false.
// If no token is available, Reserve returns a reservation for a future token
// and the amount of time the caller must wait before using it.
// If no token is available, Wait blocks until one can be obtained
// or its associated context.Context is canceled.
//
// The methods AllowN, ReserveN, and WaitN consume n tokens.
type Limiter struct {
mu sync.Mutex
limit Limit
burst int
tokens float64
// last is the last time the limiter's tokens field was updated
last time.Time
// lastEvent is the latest time of a rate-limited event (past or future)
lastEvent time.Time
}
// Limit returns the maximum overall event rate.
func (lim *Limiter) Limit() Limit {
lim.mu.Lock()
defer lim.mu.Unlock()
return lim.limit
}
// Burst returns the maximum burst size. Burst is the maximum number of tokens
// that can be consumed in a single call to Allow, Reserve, or Wait, so higher
// Burst values allow more events to happen at once.
// A zero Burst allows no events, unless limit == Inf.
func (lim *Limiter) Burst() int {
lim.mu.Lock()
defer lim.mu.Unlock()
return lim.burst
}
// NewLimiter returns a new Limiter that allows events up to rate r and permits
// bursts of at most b tokens.
func NewLimiter(r Limit, b int) *Limiter {
return &Limiter{
limit: r,
burst: b,
}
}
// Allow is shorthand for AllowN(time.Now(), 1).
func (lim *Limiter) Allow() bool {
return lim.AllowN(time.Now(), 1)
}
// AllowN reports whether n events may happen at time now.
// Use this method if you intend to drop / skip events that exceed the rate limit.
// Otherwise use Reserve or Wait.
func (lim *Limiter) AllowN(now time.Time, n int) bool {
return lim.reserveN(now, n, 0).ok
}
// A Reservation holds information about events that are permitted by a Limiter to happen after a delay.
// A Reservation may be canceled, which may enable the Limiter to permit additional events.
type Reservation struct {
ok bool
lim *Limiter
tokens int
timeToAct time.Time
// This is the Limit at reservation time, it can change later.
limit Limit
}
// OK returns whether the limiter can provide the requested number of tokens
// within the maximum wait time. If OK is false, Delay returns InfDuration, and
// Cancel does nothing.
func (r *Reservation) OK() bool {
return r.ok
}
// Delay is shorthand for DelayFrom(time.Now()).
func (r *Reservation) Delay() time.Duration {
return r.DelayFrom(time.Now())
}
// InfDuration is the duration returned by Delay when a Reservation is not OK.
const InfDuration = time.Duration(1<<63 - 1)
// DelayFrom returns the duration for which the reservation holder must wait
// before taking the reserved action. Zero duration means act immediately.
// InfDuration means the limiter cannot grant the tokens requested in this
// Reservation within the maximum wait time.
func (r *Reservation) DelayFrom(now time.Time) time.Duration {
if !r.ok {
return InfDuration
}
delay := r.timeToAct.Sub(now)
if delay < 0 {
return 0
}
return delay
}
// Cancel is shorthand for CancelAt(time.Now()).
func (r *Reservation) Cancel() {
r.CancelAt(time.Now())
}
// CancelAt indicates that the reservation holder will not perform the reserved action
// and reverses the effects of this Reservation on the rate limit as much as possible,
// considering that other reservations may have already been made.
func (r *Reservation) CancelAt(now time.Time) {
if !r.ok {
return
}
r.lim.mu.Lock()
defer r.lim.mu.Unlock()
if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) {
return
}
// calculate tokens to restore
// The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved
// after r was obtained. These tokens should not be restored.
restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct))
if restoreTokens <= 0 {
return
}
// advance time to now
now, _, tokens := r.lim.advance(now)
// calculate new number of tokens
tokens += restoreTokens
if burst := float64(r.lim.burst); tokens > burst {
tokens = burst
}
// update state
r.lim.last = now
r.lim.tokens = tokens
if r.timeToAct == r.lim.lastEvent {
prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens)))
if !prevEvent.Before(now) {
r.lim.lastEvent = prevEvent
}
}
}
// Reserve is shorthand for ReserveN(time.Now(), 1).
func (lim *Limiter) Reserve() *Reservation {
return lim.ReserveN(time.Now(), 1)
}
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
// The Limiter takes this Reservation into account when allowing future events.
// The returned Reservations OK() method returns false if n exceeds the Limiter's burst size.
// Usage example:
// r := lim.ReserveN(time.Now(), 1)
// if !r.OK() {
// // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
// return
// }
// time.Sleep(r.Delay())
// Act()
// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
// If you need to respect a deadline or cancel the delay, use Wait instead.
// To drop or skip events exceeding rate limit, use Allow instead.
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation {
r := lim.reserveN(now, n, InfDuration)
return &r
}
// Wait is shorthand for WaitN(ctx, 1).
func (lim *Limiter) Wait(ctx context.Context) (err error) {
return lim.WaitN(ctx, 1)
}
// WaitN blocks until lim permits n events to happen.
// It returns an error if n exceeds the Limiter's burst size, the Context is
// canceled, or the expected wait time exceeds the Context's Deadline.
// The burst limit is ignored if the rate limit is Inf.
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
lim.mu.Lock()
burst := lim.burst
limit := lim.limit
lim.mu.Unlock()
if n > burst && limit != Inf {
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst)
}
// Check if ctx is already cancelled
select {
case <-ctx.Done():
return ctx.Err()
default:
}
// Determine wait limit
now := time.Now()
waitLimit := InfDuration
if deadline, ok := ctx.Deadline(); ok {
waitLimit = deadline.Sub(now)
}
// Reserve
r := lim.reserveN(now, n, waitLimit)
if !r.ok {
return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n)
}
// Wait if necessary
delay := r.DelayFrom(now)
if delay == 0 {
return nil
}
t := time.NewTimer(delay)
defer t.Stop()
select {
case <-t.C:
// We can proceed.
return nil
case <-ctx.Done():
// Context was canceled before we could proceed. Cancel the
// reservation, which may permit other events to proceed sooner.
r.Cancel()
return ctx.Err()
}
}
// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit).
func (lim *Limiter) SetLimit(newLimit Limit) {
lim.SetLimitAt(time.Now(), newLimit)
}
// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated
// or underutilized by those which reserved (using Reserve or Wait) but did not yet act
// before SetLimitAt was called.
func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) {
lim.mu.Lock()
defer lim.mu.Unlock()
now, _, tokens := lim.advance(now)
lim.last = now
lim.tokens = tokens
lim.limit = newLimit
}
// SetBurst is shorthand for SetBurstAt(time.Now(), newBurst).
func (lim *Limiter) SetBurst(newBurst int) {
lim.SetBurstAt(time.Now(), newBurst)
}
// SetBurstAt sets a new burst size for the limiter.
func (lim *Limiter) SetBurstAt(now time.Time, newBurst int) {
lim.mu.Lock()
defer lim.mu.Unlock()
now, _, tokens := lim.advance(now)
lim.last = now
lim.tokens = tokens
lim.burst = newBurst
}
// reserveN is a helper method for AllowN, ReserveN, and WaitN.
// maxFutureReserve specifies the maximum reservation wait duration allowed.
// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {
lim.mu.Lock()
defer lim.mu.Unlock()
if lim.limit == Inf {
return Reservation{
ok: true,
lim: lim,
tokens: n,
timeToAct: now,
}
} else if lim.limit == 0 {
var ok bool
if lim.burst >= n {
ok = true
lim.burst -= n
}
return Reservation{
ok: ok,
lim: lim,
tokens: lim.burst,
timeToAct: now,
}
}
now, last, tokens := lim.advance(now)
// Calculate the remaining number of tokens resulting from the request.
tokens -= float64(n)
// Calculate the wait duration
var waitDuration time.Duration
if tokens < 0 {
waitDuration = lim.limit.durationFromTokens(-tokens)
}
// Decide result
ok := n <= lim.burst && waitDuration <= maxFutureReserve
// Prepare reservation
r := Reservation{
ok: ok,
lim: lim,
limit: lim.limit,
}
if ok {
r.tokens = n
r.timeToAct = now.Add(waitDuration)
}
// Update state
if ok {
lim.last = now
lim.tokens = tokens
lim.lastEvent = r.timeToAct
} else {
lim.last = last
}
return r
}
// advance calculates and returns an updated state for lim resulting from the passage of time.
// lim is not changed.
// advance requires that lim.mu is held.
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
last := lim.last
if now.Before(last) {
last = now
}
// Calculate the new number of tokens, due to time that passed.
elapsed := now.Sub(last)
delta := lim.limit.tokensFromDuration(elapsed)
tokens := lim.tokens + delta
if burst := float64(lim.burst); tokens > burst {
tokens = burst
}
return now, last, tokens
}
// durationFromTokens is a unit conversion function from the number of tokens to the duration
// of time it takes to accumulate them at a rate of limit tokens per second.
func (limit Limit) durationFromTokens(tokens float64) time.Duration {
if limit <= 0 {
return InfDuration
}
seconds := tokens / float64(limit)
return time.Duration(float64(time.Second) * seconds)
}
// tokensFromDuration is a unit conversion function from a time duration to the number of tokens
// which could be accumulated during that duration at a rate of limit tokens per second.
func (limit Limit) tokensFromDuration(d time.Duration) float64 {
if limit <= 0 {
return 0
}
return d.Seconds() * float64(limit)
}

View File

@ -0,0 +1,3 @@
module "rdma_test"
go 1.17

View File

@ -0,0 +1,247 @@
#include "buddy.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <pthread.h>
#define NODE_UNUSED 0
#define NODE_USED 1
#define NODE_SPLIT 2
#define NODE_FULL 3
struct buddy {
pthread_mutex_t mutex;
int level;
uint8_t tree[1];
};
struct buddy *
buddy_new(int level) {
int size = 1 << level;
struct buddy * self = malloc(sizeof(struct buddy) + sizeof(uint8_t) * (size * 2 - 2));
self->level = level;
memset(self->tree , NODE_UNUSED , size*2-1);
pthread_mutex_init(&self->mutex, NULL);
return self;
}
void
buddy_delete(struct buddy * self) {
free(self);
}
static inline int
is_pow_of_2(uint32_t x) {
return !(x & (x-1));
}
static inline uint32_t
next_pow_of_2(uint32_t x) {
if ( is_pow_of_2(x) )
return x;
x |= x>>1;
x |= x>>2;
x |= x>>4;
x |= x>>8;
x |= x>>16;
return x+1;
}
static inline int
_index_offset(int index, int level, int max_level) {
return ((index + 1) - (1 << level)) << (max_level - level);
}
static void
_mark_parent(struct buddy * self, int index) {
for (;;) {
int buddy = index - 1 + (index & 1) * 2;
if (buddy > 0 && (self->tree[buddy] == NODE_USED || self->tree[buddy] == NODE_FULL)) {
index = (index + 1) / 2 - 1;
self->tree[index] = NODE_FULL;
} else {
return;
}
}
}
int
buddy_alloc(struct buddy * self , int s) {
pthread_mutex_lock(&self->mutex);
int size;
if (s==0) {
size = 1;
} else {
size = (int)next_pow_of_2(s);
}
int length = 1 << self->level;
if (size > length) {
pthread_mutex_unlock(&self->mutex);
return -1;
}
int index = 0;
int level = 0;
while (index >= 0) {
if (size == length) {
if (self->tree[index] == NODE_UNUSED) {
self->tree[index] = NODE_USED;
_mark_parent(self, index);
pthread_mutex_unlock(&self->mutex);
return _index_offset(index, level, self->level);
}
} else {
// size < length
switch (self->tree[index]) {
case NODE_USED:
case NODE_FULL:
break;
case NODE_UNUSED:
// split first
self->tree[index] = NODE_SPLIT;
self->tree[index*2+1] = NODE_UNUSED;
self->tree[index*2+2] = NODE_UNUSED;
default:
index = index * 2 + 1;
length /= 2;
level++;
continue;
}
}
if (index & 1) {
++index;
continue;
}
for (;;) {
level--;
length *= 2;
index = (index+1)/2 -1;
if (index < 0) {
pthread_mutex_unlock(&self->mutex);
return -1;
}
if (index & 1) {
++index;
break;
}
}
}
pthread_mutex_unlock(&self->mutex);
return -1;
}
static void
_combine(struct buddy * self, int index) {
for (;;) {
int buddy = index - 1 + (index & 1) * 2;
if (buddy < 0 || self->tree[buddy] != NODE_UNUSED) {
self->tree[index] = NODE_UNUSED;
while (((index = (index + 1) / 2 - 1) >= 0) && self->tree[index] == NODE_FULL){
self->tree[index] = NODE_SPLIT;
}
return;
}
index = (index + 1) / 2 - 1;
}
}
void
buddy_free(struct buddy * self, int offset) {
pthread_mutex_lock(&self->mutex);
assert( offset < (1<< self->level));
int left = 0;
int length = 1 << self->level;
int index = 0;
for (;;) {
switch (self->tree[index]) {
case NODE_USED:
assert(offset == left);
_combine(self, index);
pthread_mutex_unlock(&self->mutex);
return;
case NODE_UNUSED:
assert(0);
pthread_mutex_unlock(&self->mutex);
return;
default:
length /= 2;
if (offset < left + length) {
index = index * 2 + 1;
} else {
left += length;
index = index * 2 + 2;
}
break;
}
}
pthread_mutex_unlock(&self->mutex);
}
int
buddy_size(struct buddy * self, int offset) {
pthread_mutex_lock(&self->mutex);
assert( offset < (1<< self->level));
int left = 0;
int length = 1 << self->level;
int index = 0;
for (;;) {
switch (self->tree[index]) {
case NODE_USED:
assert(offset == left);
pthread_mutex_unlock(&self->mutex);
return length;
case NODE_UNUSED:
assert(0);
pthread_mutex_unlock(&self->mutex);
return length;
default:
length /= 2;
if (offset < left + length) {
index = index * 2 + 1;
} else {
left += length;
index = index * 2 + 2;
}
break;
}
}
pthread_mutex_unlock(&self->mutex);
}
static void
_dump(struct buddy * self, int index , int level) {
switch (self->tree[index]) {
case NODE_UNUSED:
printf("(%d:%d)", _index_offset(index, level, self->level) , 1 << (self->level - level));
break;
case NODE_USED:
printf("[%d:%d]", _index_offset(index, level, self->level) , 1 << (self->level - level));
break;
case NODE_FULL:
printf("{");
_dump(self, index * 2 + 1 , level+1);
_dump(self, index * 2 + 2 , level+1);
printf("}");
break;
default:
printf("(");
_dump(self, index * 2 + 1 , level+1);
_dump(self, index * 2 + 2 , level+1);
printf(")");
break;
}
}
void
buddy_dump(struct buddy * self) {
_dump(self, 0 , 0);
printf("\n");
}

View File

@ -0,0 +1,14 @@
#ifndef BUDDY_MEMORY_ALLOCATION_H
#define BUDDY_MEMORY_ALLOCATION_H
struct buddy;
struct buddy * buddy_new(int level);
void buddy_delete(struct buddy *);
int buddy_alloc(struct buddy *, int size);
void buddy_free(struct buddy *, int offset);
int buddy_size(struct buddy *, int offset);
void buddy_dump(struct buddy *);
#endif

View File

@ -0,0 +1,44 @@
#include <client.h>
struct connection* rdma_connect_by_addr(const char* ip, const char* port) {
struct addrinfo *addr;
struct rdma_conn_param cm_params;
uint64_t nd;
nd = allocate_nd(CONN_ACTIVE_BIT);
connection* conn = init_connection(nd, CONN_TYPE_CLIENT);
if (conn == NULL) {
log_error("init_connection return null");
return NULL;
}
add_conn_to_worker(conn, conn->worker, conn->worker->nd_map);
getaddrinfo(ip, port, NULL, &addr);
int ret = rdma_create_id(g_net_env->event_channel, &conn->cm_id, (void*)(conn->nd), RDMA_PS_TCP);
if (ret != 0) {
log_error("conn(%ld-%p) create cmid failed, err:%d", conn->nd, conn, errno);
goto err_free;
}
log_debug("conn(%lu-%p) create cmid:%p", conn->nd, conn, conn->cm_id);
ret = rdma_resolve_addr(conn->cm_id, NULL, addr->ai_addr, TIMEOUT_IN_MS);
if (ret != 0) {
log_error("conn(%lu-%p) solve addr failed, err:%d", conn->nd, conn, errno);
goto err_destroy_id;
}
freeaddrinfo(addr);
wait_event(conn->connect_fd);
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) {//state is already equal to CONN_STATE_DISCONNECTED
//conn_disconnect(conn);
return NULL;
}
return conn;
err_destroy_id:
rdma_destroy_id(conn->cm_id);
err_free:
del_conn_from_worker(conn->nd,conn->worker,conn->worker->nd_map);
destroy_connection(conn);
return NULL;
}

View File

@ -0,0 +1,15 @@
#ifndef CLIENT_H
#define CLIENT_H
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <infiniband/verbs.h>
#include "rdma.h"
//#include "rdma_proto.h"
struct connection* rdma_connect_by_addr(const char* ip, const char* port);
#endif

View File

@ -0,0 +1,852 @@
#include "connection.h"
int64_t get_time_ns() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return ts.tv_sec * 1000000000 + ts.tv_nsec;
}
void build_qp_attr(struct ibv_cq *cq, struct ibv_qp_init_attr *qp_attr) {
memset(qp_attr, 0, sizeof(*qp_attr));
qp_attr->cap.max_send_wr = WQ_DEPTH;
qp_attr->cap.max_recv_wr = WQ_DEPTH;
qp_attr->cap.max_send_sge = WQ_SG_DEPTH;
qp_attr->cap.max_recv_sge = WQ_SG_DEPTH;
qp_attr->qp_type = IBV_QPT_RC;
qp_attr->send_cq = cq;
qp_attr->recv_cq = cq;
}
int conn_rdma_post_recv(connection *conn, rdma_ctl_cmd *cmd) {
struct ibv_sge sge;
struct ibv_recv_wr recv_wr, *bad_wr;
cmd_entry *entry;
int ret;
entry = (cmd_entry*)malloc(sizeof(cmd_entry));
if (entry == NULL) {
log_error("conn(%lu-%p) rdma post recv: malloc entry failed", conn->nd, conn);
return C_ERR;
}
sge.addr = (uint64_t)cmd;
sge.length = sizeof(rdma_ctl_cmd);
sge.lkey = conn->ctl_buf_mr->lkey;
entry->cmd = cmd;
entry->nd = conn->nd;
recv_wr.wr_id = (uint64_t)entry;
recv_wr.sg_list = &sge;
recv_wr.num_sge = 1;
recv_wr.next = NULL;
ret = ibv_post_recv(conn->qp, &recv_wr, &bad_wr);
if (ret != 0) {
log_error("conn(%lu-%p) ibv post recv failed: %d", conn->nd, conn, ret);
//int state = get_conn_state(conn);
//if (state == CONN_STATE_CONNECTED) {
// set_conn_state(conn, CONN_STATE_ERROR); //TODO
//}
free(entry);
return C_ERR;
}
return C_OK;
}
int conn_rdma_post_send(connection *conn, rdma_ctl_cmd *cmd) {
struct ibv_send_wr send_wr, *bad_wr;
struct ibv_sge sge;
cmd_entry *entry;
int ret;
entry = (cmd_entry*)malloc(sizeof(cmd_entry));
if (entry == NULL) {
log_error("conn(%lu-%p) rdma post recv: malloc entry failed", conn->nd, conn);
return C_ERR;
}
sge.addr = (uint64_t)cmd;
sge.length = sizeof(rdma_ctl_cmd);
sge.lkey = conn->ctl_buf_mr->lkey;
entry->cmd = cmd;
entry->nd = conn->nd;
send_wr.sg_list = &sge;
send_wr.num_sge = 1;
send_wr.wr_id = (uint64_t)entry;
send_wr.opcode = IBV_WR_SEND;
send_wr.send_flags = IBV_SEND_SIGNALED;
send_wr.next = NULL;
ret = ibv_post_send(conn->qp, &send_wr, &bad_wr);
if (ret != 0) {
log_error("conn(%lu-%p) ibv post send failed: %d", conn->nd,conn, ret);
//int state = get_conn_state(conn);
//if (state == CONN_STATE_CONNECTED) {
// set_conn_state(conn, CONN_STATE_ERROR); //TODO
//}
free(entry);
return C_ERR;
}
return C_OK;
}
void rdma_destroy_ioBuf(connection *conn) {
if (conn->ctl_buf_mr) {
ibv_dereg_mr(conn->ctl_buf_mr);
conn->ctl_buf_mr = NULL;
}
if (conn->ctl_buf) {
free(conn->ctl_buf);
conn->ctl_buf = NULL;
}
if (conn->rx) {
conn->rx->mr = NULL;
if (conn->rx->addr) {
int index = (int)((conn->rx->addr - (rdma_pool->memory_pool->original_mem)) / (rdma_env_config->mem_block_size));
buddy_free(rdma_pool->memory_pool->allocation, index);
//buddy_dump(rdmaPool->memoryPool->allocation);
conn->rx->addr = NULL;
}
}
if (conn->tx) {
conn->tx->mr = NULL;
if (conn->tx->addr) {
int index = (int)((conn->tx->addr - (rdma_pool->memory_pool->original_mem)) / (rdma_env_config->mem_block_size));
buddy_free(rdma_pool->memory_pool->allocation, index);
//buddy_dump(rdmaPool->memoryPool->allocation);
conn->tx->addr = NULL;
}
}
}
int rdma_setup_ioBuf(connection *conn) {
int access = IBV_ACCESS_LOCAL_WRITE;
size_t ctl_buf_length = sizeof(rdma_ctl_cmd) * WQ_DEPTH * 2;
rdma_ctl_cmd *cmd;
int i;
conn->ctl_buf = page_aligned_zalloc(ctl_buf_length);
if (conn->ctl_buf == NULL) {
log_error("conn(%lu-%p) ctl buf alloc failed", conn->nd, conn);
goto destroy_iobuf;
}
conn->ctl_buf_mr = ibv_reg_mr(conn->worker->pd, conn->ctl_buf, ctl_buf_length, access);
if (conn->ctl_buf_mr == NULL) {
log_error("conn(%lu-%p) ctl buf register failed", conn->nd, conn);
goto destroy_iobuf;
}
for (i = 0; i < WQ_DEPTH; i++) {
cmd = conn->ctl_buf + i;
if (conn_rdma_post_recv(conn, cmd) == C_ERR) {
log_error("conn(%lu-%p) ctl buf post recv failed", conn->nd, conn);
goto destroy_iobuf;
}
}
for (i = WQ_DEPTH; i < WQ_DEPTH * 2; i++) {
cmd = conn->ctl_buf + i;
if(EnQueue(conn->free_list, cmd) == NULL) {
log_error("conn(%lu-%p) freeList has no more memory can be malloced", conn->nd, conn);
goto destroy_iobuf;
}
}
access = IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_WRITE;
int quotient = CONN_DATA_SIZE / rdma_env_config->mem_block_size;
int remainder = CONN_DATA_SIZE % rdma_env_config->mem_block_size;
if(remainder > 0) {
quotient++;
}
int index = buddy_alloc(rdma_pool->memory_pool->allocation, quotient);
//buddy_dump(rdma_pool->memory_pool->allocation);
int s = buddy_size(rdma_pool->memory_pool->allocation, index);//when index == -1,assert is not pass
if(index == -1) {
log_error("memory pool(%p): there is no space to alloc", rdma_pool->memory_pool);
goto destroy_iobuf;
}
s = buddy_size(rdma_pool->memory_pool->allocation, index);
assert(s * rdma_env_config->mem_block_size >= CONN_DATA_SIZE);
uint32_t data_buf_length = (uint32_t) s * (uint32_t) rdma_env_config->mem_block_size;
conn->rx->addr = rdma_pool->memory_pool->original_mem + index * rdma_env_config->mem_block_size;
conn->rx->length = data_buf_length;
conn->rx->mr = rdma_pool->memory_pool->mr;
return C_OK;
destroy_iobuf:
rdma_destroy_ioBuf(conn);
return C_ERR;
}
int rdma_adjust_txBuf(connection *conn, uint32_t length) {
if (length == conn->remote_rx_length) {
return C_OK;
}
if (conn->remote_rx_length) {
conn->tx->mr = NULL;
int index = (int)((conn->tx->addr - (rdma_pool->memory_pool->original_mem)) / (rdma_env_config->mem_block_size));
buddy_free(rdma_pool->memory_pool->allocation, index);
//buddy_dump(rdmaPool->memoryPool->allocation);
conn->tx->addr = NULL;
conn->remote_rx_length = 0;
}
int quotient = length / rdma_env_config->mem_block_size;
int remainder = length % rdma_env_config->mem_block_size;
if(remainder > 0) {
quotient++;
}
int index = buddy_alloc(rdma_pool->memory_pool->allocation, quotient);
//buddy_dump(rdma_pool->memory_pool->allocation);
int s = buddy_size(rdma_pool->memory_pool->allocation, index);//when index == -1,assert is not pass
if(index == -1) {
log_error("memory pool(%p): there is no space to alloc", rdma_pool->memory_pool);
return C_ERR;
}
s = buddy_size(rdma_pool->memory_pool->allocation, index);
assert(s * rdma_env_config->mem_block_size >= length);
//uint32_t data_buf_length = (uint32_t) s * (uint32_t) rdma_env_config->mem_block_size;
conn->tx->addr = rdma_pool->memory_pool->original_mem + index * rdma_env_config->mem_block_size;
conn->remote_rx_length = length;
conn->tx->mr = rdma_pool->memory_pool->mr;
return C_OK;
}
void destroy_connection(connection *conn) {
if (conn->free_list) {
DestroyQueue(conn->free_list);
}
if (conn->msg_list) {
DestroyQueue(conn->msg_list);
}
conn->conn_context = NULL;
conn->context = NULL;
conn->remote_rx_addr = NULL;
if (conn->connect_fd >0) {
notify_event(conn->connect_fd,1);
conn->connect_fd = -1;
}
if (conn->msg_fd > 0) {
notify_event(conn->msg_fd,1);
conn->msg_fd = -1;
}
if (conn->close_fd > 0) {
notify_event(conn->close_fd,1);
conn->close_fd = -1;
}
pthread_spin_destroy(&conn->spin_lock);
pthread_spin_destroy(&conn->free_list_lock);
pthread_spin_destroy(&conn->msg_list_lock);
if (conn->tx) {
free(conn->tx);
}
if (conn->rx) {
free(conn->rx);
}
memset(conn, 0, sizeof(connection));
free(conn);
}
connection* init_connection(uint64_t nd, int conn_type) {
int ret = 0;
connection *conn = (connection*)malloc(sizeof(connection));
if (conn == NULL) {
log_error("create conn mem obj failed");
return NULL;
}
memset(conn, 0, sizeof(connection));
log_debug("malloc connect:%p", conn);
conn->nd = nd;
log_debug("conn nd:%lu",nd);
conn->worker = get_worker_by_nd(conn->nd);
log_debug("conn worker:%p",conn->worker);
conn->free_list = InitQueue();
if (conn->free_list == NULL) {
log_error("conn(%lu-%p) init free list failed", conn->nd, conn);
goto err_free;
}
conn->msg_list = InitQueue();
if (conn->msg_list == NULL) {
log_error("conn(%lu-%p) init msg list failed", conn->nd, conn);
goto err_destroy_freelist;
}
conn->conn_type = conn_type;
conn->conn_context = NULL;
conn->context = NULL;
conn->send_timeout_ns = 0;
conn->recv_timeout_ns = 0;
conn->remote_rx_addr = NULL;
conn->remote_rx_key = 0;
conn->remote_rx_length = 0;
conn->remote_rx_offset = 0;
conn->tx_full_offset = 0;
conn->rx_full_offset = 0;
conn->connect_fd = -1;
conn->msg_fd = -1;
conn->close_fd = -1;
conn->connect_fd = open_event_fd();
if (conn->connect_fd < 0) {
log_error("conn(%lu-%p) open connect fd failed", conn->nd, conn);
goto err_destroy_msglist;
}
conn->msg_fd = open_event_fd();
if (conn->msg_fd < 0) {
log_error("conn(%lu-%p) open msg fd failed", conn->nd, conn);
goto err_destroy_connectfd;
}
conn->close_fd = open_event_fd();
if (conn->close_fd < 0) {
log_error("conn(%lu-%p) open close fd failed", conn->nd, conn);
goto err_destroy_msgfd;
}
ret = pthread_spin_init(&(conn->spin_lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("conn(%lu-%p) init spin lock failed, err:%d", conn->nd, conn, ret);
goto err_destroy_closefd;
}
ret = pthread_spin_init(&(conn->free_list_lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("init conn(%p) free list lock failed, err:%d", conn, ret);
goto err_destroy_spin_lock;
}
ret = pthread_spin_init(&(conn->msg_list_lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("init conn(%p) msg list lock failed, err:%d", conn, ret);
goto err_destroy_free_list_lock;
}
conn->tx = (data_buf*)malloc(sizeof(data_buf));
if (conn->tx == NULL) {
log_error("conn(%lu-%p) malloc tx failed", conn->nd, conn);
goto err_destroy_msg_list_lock;
}
memset(conn->tx,0,sizeof(data_buf));
conn->rx = (data_buf*)malloc(sizeof(data_buf));
if (conn->rx == NULL) {
log_error("conn(%lu-%p) malloc rx failed", conn->nd, conn);
goto err_free_tx;
}
memset(conn->rx,0,sizeof(data_buf));
set_conn_state(conn, CONN_STATE_CONNECTING);
return conn;
err_free_tx:
free(conn->tx);
err_destroy_msg_list_lock:
pthread_spin_destroy(&conn->msg_list_lock);
err_destroy_free_list_lock:
pthread_spin_destroy(&conn->free_list_lock);
err_destroy_spin_lock:
pthread_spin_destroy(&conn->spin_lock);
err_destroy_closefd:
notify_event(conn->close_fd,1);
conn->close_fd = -1;
err_destroy_msgfd:
notify_event(conn->msg_fd,1);
conn->msg_fd = -1;
err_destroy_connectfd:
notify_event(conn->connect_fd,1);
conn->connect_fd = -1;
err_destroy_msglist:
DestroyQueue(conn->msg_list);
err_destroy_freelist:
DestroyQueue(conn->free_list);
err_free:
free(conn);
return NULL;
}
void destroy_conn_qp(connection *conn) {
if (conn->qp != NULL && conn->cm_id != NULL) {
rdma_destroy_qp(conn->cm_id);
log_debug("conn(%lu-%p) destroy qp, cmid:%p", conn->nd, conn, conn->cm_id);
}
conn->qp = NULL;
}
int create_conn_qp(connection *conn, struct rdma_cm_id* id) {
struct ibv_qp_init_attr qp_attr;
build_qp_attr(conn->worker->cq, &qp_attr);
int ret = rdma_create_qp(id, conn->worker->pd, &qp_attr);
if (ret != 0) {
log_error("conn(%lu-%p) create qp failed, errno:%d", conn->nd, conn, errno);
return C_ERR;
}
conn->qp = id->qp;
log_debug("conn(%lu-%p) rdma_create_qp:%p", conn->nd, conn, conn->qp);
return C_OK;
}
int add_conn_to_server(connection *conn, struct rdma_listener *server) {
int ret = 0;
conn->context = server;
pthread_spin_lock(&server->conn_lock);
ret = hashmap_put(server->conn_map, conn->nd, (uint64_t)conn);
pthread_spin_unlock(&server->conn_lock);
log_debug("add conn(%lu-%p) to server(%p) conn_map(%p)", conn->nd, conn, server, server->conn_map);
return ret >= 0;
}
int del_conn_from_server(connection *conn, struct rdma_listener *server) {
int ret = 0;
pthread_spin_lock(&server->conn_lock);
ret = hashmap_del(server->conn_map, conn->nd);
pthread_spin_unlock(&server->conn_lock);
log_debug("del conn(%lu-%p) from server(%p) conn_map(%p)", conn->nd, conn, server, server->conn_map);
return ret >= 0;
}
void conn_disconnect(connection *conn) {
worker *worker = NULL;
struct rdma_listener *server = NULL;
worker = get_worker_by_nd((uintptr_t) conn->cm_id->context);
int state = get_conn_state(conn);
if (state != CONN_STATE_DISCONNECTING && state != CONN_STATE_DISCONNECTED && conn->cm_id != NULL) {
while(conn->ref > 0) {
usleep(5);
}
set_conn_state(conn, CONN_STATE_DISCONNECTING);
rdma_disconnect(conn->cm_id);
log_debug("conn(%lu-%p) exec rdma_disconnect", conn->nd, conn);
}
wait_event(conn->close_fd);
//release resource
if (conn->conn_type == CONN_TYPE_SERVER) {//server
server = (struct rdma_listener*)conn->context;
del_conn_from_server(conn, server);
} else {//client
}
del_conn_from_worker(conn->nd, worker, worker->nd_map);
//del_conn_from_worker(conn->nd, conn->worker, conn->worker->closing_nd_map);
destroy_conn_qp(conn);
rdma_destroy_id(conn->cm_id);
rdma_destroy_ioBuf(conn);
destroy_connection(conn);
log_debug("conn(%lu-%p) disconnect, resource release completed", conn->nd, conn);
return;
}
/*
int rdma_post_send_cmd(connection *conn, rdma_ctl_cmd *cmd) {
int state = get_conn_state(conn);
if(state != CONN_STATE_CONNECTED) {
log_error("post send cmd failed: conn(%lu-%p) state is not connected: state(%d)", conn->nd, conn, state);
return C_ERR;
}
int ret = conn_rdma_post_send(conn, cmd);
return ret;
}
*/
/*
int rdma_post_recv_cmd(connection *conn, rdma_ctl_cmd *cmd) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) {
log_error("post recv cmd failed: conn(%lu-%p) state is not connected: state(%d)", conn->nd, conn, state);
return C_ERR;
}
int ret = conn_rdma_post_recv(conn, cmd);
return ret;
}
*/
int rdma_exchange_rx(connection *conn) {
rdma_ctl_cmd *cmd = get_cmd_buffer(conn);
if (cmd == NULL) {
return C_ERR;
}
cmd->memory.opcode = htons(EXCHANGE_MEMORY);
cmd->memory.addr = htonu64((uint64_t)conn->rx->addr);
cmd->memory.length = htonl(conn->rx->length);
cmd->memory.key = htonl(conn->rx->mr->rkey);
conn->rx->offset = 0;
conn->rx->pos = 0;
conn->rx_full_offset = 0;
return conn_rdma_post_send(conn, cmd);
}
int rdma_notify_buf_full(connection *conn) {
rdma_ctl_cmd *cmd = get_cmd_buffer(conn);
if (cmd == NULL) {
return C_ERR;
}
cmd->full_msg.opcode = htons(NOTIFY_FULLBUF);
cmd->full_msg.tx_full_offset = htonl(conn->tx_full_offset);
return conn_rdma_post_send(conn, cmd);
}
int conn_app_write_external_buffer(connection *conn, void *buffer, uint32_t size) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) { //在使用之前需要判断连接的状态
log_error("conn(%lu-%p) app write failed: conn state is not connected: state(%d)", conn->nd, conn, state);
return C_ERR;
}
struct rdma_cm_id *cm_id = conn->cm_id;
struct ibv_send_wr send_wr, *bad_wr;
struct ibv_sge sge;
char *addr = (char*)buffer;
char *remote_addr;
int ret;
while(1) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) {
log_error("conn(%lu-%p) is not in connected state: state(%d)", conn->nd, conn, state);
return C_ERR;
}
assert(conn->tx->offset <= conn->tx->length);
if (conn->tx->pos <= conn->tx->offset) {
if (conn->tx->length - conn->tx->offset >= size) {
conn->tx->offset += size;
} else {
conn->tx->offset = 0;
log_error("conn(%lu-%p) get data buffer failed, no more data buffer can get, reset offset = 0", conn->nd, conn);
continue;
}
} else {
if (conn->tx->pos - conn->tx->offset >= size) {
conn->tx->offset += size;
} else {
log_error("conn(%lu-%p) get data buffer failed, no more data buffer can get", conn->nd, conn);
continue;
}
}
/*
assert(conn->tx->offset <= conn->tx->length);
if (conn->tx->length - conn->tx->offset < size) {
if (conn->tx_full_offset == 0) {
conn->tx_full_offset = conn->tx->offset;
ret = rdma_notify_buf_full(conn); //todo error handler
if (ret == C_ERR) {
log_error("conn(%lu-%p) tx full, notify remote side failed");
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
return C_ERR;
}
}
//pthread_spin_unlock(&conn->spin_lock);
log_error("conn(%lu-%p) get data buffer failed, no more data buffer can get", conn->nd, conn);
//usleep(5);
continue;
}
*/
//conn->tx->offset += size;
remote_addr = conn->remote_rx_addr + conn->tx->offset - size;
log_debug("conn(%lu-%p) tx start(%d) end(%d)", conn->nd, conn, conn->tx->offset - size, conn->tx->offset);
break;
}
sge.addr = (uint64_t)addr;
sge.lkey = conn->tx->mr->lkey;
sge.length = size;
send_wr.sg_list = &sge;
send_wr.num_sge = 1;
send_wr.opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
send_wr.send_flags = IBV_SEND_SIGNALED;
send_wr.imm_data = htonl(size);
send_wr.wr.rdma.remote_addr = (uint64_t)remote_addr;
send_wr.wr.rdma.rkey = conn->remote_rx_key;
send_wr.wr_id = conn->nd;
send_wr.next = NULL;
ret = ibv_post_send(conn->qp, &send_wr, &bad_wr);
if (ret != 0) {
log_error("conn(%lu-%p) ibv post send: remote write failed: %d", conn->nd,conn, ret);
set_conn_state(conn, CONN_STATE_ERROR);//TODO
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
return C_ERR;
}
return C_OK;
}
int conn_app_write(connection *conn, data_entry *entry, uint32_t size) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) { //在使用之前需要判断连接的状态
log_error("conn(%lu-%p) app write failed: conn state is not connected: state(%d)", conn->nd, conn, state);
return C_ERR;
}
struct rdma_cm_id *cm_id = conn->cm_id;
struct ibv_send_wr send_wr, *bad_wr;
struct ibv_sge sge;
char *addr = entry->addr;
char *remote_addr = entry->remote_addr;
uint32_t mem_len = entry->mem_len;
int ret;
sge.addr = (uint64_t)addr;
sge.lkey = conn->tx->mr->lkey;
sge.length = size;
send_wr.sg_list = &sge;
send_wr.num_sge = 1;
send_wr.opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
send_wr.send_flags = IBV_SEND_SIGNALED;
send_wr.imm_data = htonl(mem_len);
send_wr.wr.rdma.remote_addr = (uint64_t)remote_addr;
send_wr.wr.rdma.rkey = conn->remote_rx_key;
send_wr.wr_id = conn->nd;
send_wr.next = NULL;
ret = ibv_post_send(conn->qp, &send_wr, &bad_wr);
if (ret != 0) {
log_error("conn(%lu-%p) ibv post send: remote write failed: %d", conn->nd,conn, ret);
set_conn_state(conn, CONN_STATE_ERROR);//TODO
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
return C_ERR;
}
return C_OK;
}
void* get_pool_data_buffer(uint32_t size, int64_t *ret_size) {
*ret_size = 0;
while(1) {
int quotient = size / rdma_env_config->mem_block_size;
int remainder = size % rdma_env_config->mem_block_size;
if(remainder > 0) {
quotient++;
}
int index = buddy_alloc(rdma_pool->memory_pool->allocation, quotient);
if(index == -1) {
log_error("get pool data buffer failed, no more data buffer can get");
continue;
}
//buddy_dump(rdmaPool->memoryPool->allocation);
int s = buddy_size(rdma_pool->memory_pool->allocation,index);
assert(s * rdma_env_config->mem_block_size >= size);
*ret_size = s * rdma_env_config->mem_block_size;
void* data_buffer = rdma_pool->memory_pool->original_mem + index * rdma_env_config->mem_block_size;
return data_buffer;
}
}
data_entry* get_conn_tx_data_buffer(connection *conn, uint32_t size) {
while(1) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) {
log_error("conn(%lu-%p) is not in connected state: state(%d)", conn->nd, conn, state);
return NULL;
}
//pthread_spin_lock(&conn->spin_lock);
assert(conn->tx->offset <= conn->tx->length);
if (conn->tx->pos <= conn->tx->offset) {
if (conn->tx->length - conn->tx->offset >= size) {
conn->tx->offset += size;
} else {
conn->tx->offset = 0;
log_error("conn(%lu-%p) get data buffer failed, no more data buffer can get, reset offset = 0", conn->nd, conn);
continue;
}
} else {
if (conn->tx->pos - conn->tx->offset >= size) {
conn->tx->offset += size;
} else {
log_error("conn(%lu-%p) get data buffer failed, no more data buffer can get", conn->nd, conn);
continue;
}
}
/*
if (conn->tx->length - conn->tx->offset < size) {
if (conn->tx_full_offset == 0) {
conn->tx_full_offset = conn->tx->offset;
int ret = rdma_notify_buf_full(conn); //todo error handler
if (ret == C_ERR) {
log_error("conn(%lu-%p) tx full, notify remote side failed");
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
return NULL;
}
}
//pthread_spin_unlock(&conn->spin_lock);
log_error("conn(%lu-%p) get data buffer failed, no more data buffer can get", conn->nd, conn);
//usleep(5);
continue;
}
*/
data_entry *entry = (data_entry*)malloc(sizeof(data_entry));
if (entry == NULL) {
log_error("conn(%lu-%p) malloc data entry failed", conn->nd, conn);
return NULL;
}
entry->addr = conn->tx->addr + conn->tx->offset - size;
entry->remote_addr = conn->remote_rx_addr + conn->tx->offset - size;
entry->mem_len = size;
//conn->tx->offset += size;
log_debug("conn(%lu-%p) tx start(%d) end(%d)", conn->nd, conn, conn->tx->offset - size, conn->tx->offset);
//pthread_spin_unlock(&conn->spin_lock);
return entry;
}
}
rdma_ctl_cmd* get_cmd_buffer(connection *conn) {
rdma_ctl_cmd *cmd = NULL;
while(1) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED && state != CONN_STATE_CONNECTING) {
log_error("conn(%lu-%p) is not in connected state: state(%d)", conn->nd, conn, state);
return NULL;
}
pthread_spin_lock(&(conn->free_list_lock));
DeQueue(conn->free_list, (Item*)&cmd);
pthread_spin_unlock(&(conn->free_list_lock));
if (cmd == NULL) {//(Item *)
log_error("conn(%lu-%p) get cmd buffer failed, no more cmd buffer can get", conn->nd, conn);
//usleep(5);
continue;
}
return cmd;
}
}
data_entry* get_recv_msg_buffer(connection *conn) {
while(1) {
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED) { //在使用之前需要判断连接的状态
log_error("conn(%lu-%p) get recv msg failed: conn state is not connected: state(%d)", conn->nd, conn, state);
return NULL;
}
//wait_event(conn->msg_fd);
//state = get_conn_state(conn);
//if (state != CONN_STATE_CONNECTED) { //在使用之前需要判断连接的状态
// log_error("conn(%lu-%p) get recv msg failed: conn state is not connected: state(%d)", conn->nd, conn, state);
// return NULL;
//}
log_debug("wait event: conn(%lu-%p) msg_fd(%d)", conn->nd, conn, conn->msg_fd);
data_entry *msg = NULL;
pthread_spin_lock(&(conn->msg_list_lock));
DeQueue(conn->msg_list, (Item*)&msg);
pthread_spin_unlock(&(conn->msg_list_lock));
if (msg == NULL) {//(Item *)
log_error("conn(%lu-%p) get recv msg buffer failed: dequeue(%p) entry is null", conn->nd, conn, conn->msg_list);
continue;
//return NULL;
//TODO
}
log_debug("conn(%lu-%p) get recv msg buffer success: dequeue(%p) entry is %p", conn->nd, conn, conn->msg_list, msg);
return msg;
}
//return msg;
}
void set_conn_context(connection* conn, void* conn_context) {
conn->conn_context = conn_context;
return;
}
void set_send_timeout_us(connection* conn, int64_t timeout_us) {
if(timeout_us > 0) {
conn->send_timeout_ns = timeout_us * 1000;
} else {
conn->send_timeout_ns = -1;
}
log_debug("conn(%lu-%p) set send timeout us:%ld", conn->nd, conn, conn->send_timeout_ns);
return;
} //todo lock
void set_recv_timeout_us(connection* conn, int64_t timeout_us) {
if(timeout_us > 0) {
conn->recv_timeout_ns = timeout_us * 1000;
} else {
conn->recv_timeout_ns = -1;
}
log_debug("conn(%lu-%p) set recv timeout us:%ld", conn->nd, conn, conn->recv_timeout_ns);
return;
} //todo lock
int release_cmd_buffer(connection *conn, rdma_ctl_cmd *cmd) {
int state = get_conn_state(conn);
if(state != CONN_STATE_CONNECTED && state != CONN_STATE_CONNECTING) {
log_error("conn(%lu-%p) release cmd buffer failed: conn state is not connected: state(%d)", conn->nd, conn, state);
return C_ERR;
}
pthread_spin_lock(&(conn->free_list_lock));
if(EnQueue(conn->free_list, cmd) == NULL) {
pthread_spin_unlock(&(conn->free_list_lock));
log_error("conn(%lu-%p) release cmd buffer failed: no more memory can be malloced", conn->nd, conn);
return C_ERR;
};
pthread_spin_unlock(&(conn->free_list_lock));
return C_OK;
}
int release_pool_data_buffer(connection *conn, void* buff, uint32_t size) {
int index = (int)(((char*)buff - (rdma_pool->memory_pool->original_mem)) / (rdma_env_config->mem_block_size));
buddy_free(rdma_pool->memory_pool->allocation, index);
//buddy_dump(rdmaPool->memoryPool->allocation);
if (conn->tx->pos + size > conn->tx->length) {
conn->tx->pos = 0;
}
conn->tx->pos += size;
log_debug("conn(%lu-%p) tx pos:%d", conn->nd, conn, conn->tx->pos);
return C_OK;
}
int release_conn_rx_data_buffer(connection *conn, data_entry *data) {
int state = get_conn_state(conn);
if(state != CONN_STATE_CONNECTED) {
log_error("conn(%lu-%p) release rx data buffer failed: conn state is not connected: state(%d)", conn->nd, conn, state);
free(data);
return C_ERR;
}
//pthread_spin_lock(&conn->spin_lock);
//assert(conn->rx->addr + conn->rx->pos == data->addr);
if (conn->rx->pos + data->mem_len > conn->rx->length) {
conn->rx->pos = 0;
}
conn->rx->pos += data->mem_len;
log_debug("conn(%lu-%p) rx pos:%d", conn->nd, conn, conn->rx->pos);
/*
assert(conn->rx->addr + conn->rx->pos == data->addr);
conn->rx->pos += data->mem_len;
log_debug("conn(%lu-%p) rx pos:%d", conn->nd, conn, conn->rx->pos);
if (conn->rx->pos == conn->rx->offset && conn->rx_full_offset == conn->rx->pos) {
int ret = rdma_exchange_rx(conn); //TODO error handler
if (ret == C_ERR) {
log_error("conn(%lu-%p) release rx buffer failed: exchange rx return error");
free(data);
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
return C_ERR;
}
}
*/
//pthread_spin_unlock(&conn->spin_lock);
free(data);
return C_OK;
}
int release_conn_tx_data_buffer(connection *conn, data_entry *data) {
int state = get_conn_state(conn);
if(state != CONN_STATE_CONNECTED) {
log_error("conn(%lu-%p) release rx data buffer failed: conn state is not connected: state(%d)", conn->nd, conn, state);
free(data);
return C_ERR;
}
if (conn->tx->pos + data->mem_len > conn->tx->length) {
assert(data->addr == conn->tx->addr);
conn->tx->pos = 0;
} else {
assert(conn->tx->addr + conn->tx->pos == data->addr);
}
conn->tx->pos += data->mem_len;
log_debug("conn(%lu-%p) tx pos:%d", conn->nd, conn, conn->tx->pos);
free(data);
return C_OK;
}

View File

@ -0,0 +1,86 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <endian.h>
//#include "wait_group.h"
#include "rdma_proto.h"
//#include "rdma_proto.h"
//#include "transfer_event.h"
//#include "connection_event.h"
static const int trace = 0;
#define TRACE_PRINT(fn) if (trace) fn
#define ntohu64(v) be64toh(v)
#define htonu64(v) htobe64(v)
int64_t get_time_ns();
int conn_rdma_post_recv(connection *conn, rdma_ctl_cmd *cmd);
int conn_rdma_post_send(connection *conn, rdma_ctl_cmd *cmd);
void rdma_destroy_ioBuf(connection *conn);
int rdma_setup_ioBuf(connection *conn);
int rdma_adjust_txBuf(connection *conn, uint32_t length);
void destroy_connection(connection *conn);
connection* init_connection(uint64_t nd, int conn_type);
void destroy_conn_qp(connection *conn);
int create_conn_qp(connection *conn, struct rdma_cm_id* id);
int add_conn_to_server(connection *conn, struct rdma_listener *server);
int del_conn_from_server(connection *conn, struct rdma_listener *server);
void conn_disconnect(connection *conn);
//int rdma_post_send_cmd(connection *conn, rdma_ctl_cmd *cmd);
//int rdma_post_recv_cmd(connection *conn, rdma_ctl_cmd *cmd);
int rdma_exchange_rx(connection *conn);
int rdma_notify_buf_full(connection *conn);
int conn_app_write_external_buffer(connection *conn, void *buffer, uint32_t size);
int conn_app_write(connection *conn, data_entry *entry, uint32_t size);
void* get_pool_data_buffer(uint32_t size, int64_t *ret_size);
data_entry* get_conn_tx_data_buffer(connection *conn, uint32_t size);
rdma_ctl_cmd* get_cmd_buffer(connection *conn);
data_entry* get_recv_msg_buffer(connection *conn);
void set_conn_context(connection* conn, void* conn_context);
void set_send_timeout_us(connection* conn, int64_t timeout_us);
void set_recv_timeout_us(connection* conn, int64_t timeout_us);
int release_cmd_buffer(connection *conn, rdma_ctl_cmd *cmd);
int release_pool_data_buffer(connection *conn, void* buff, uint32_t size);
int release_conn_rx_data_buffer(connection *conn, data_entry *data);
int release_conn_tx_data_buffer(connection *conn, data_entry *data);
#endif

View File

@ -0,0 +1,274 @@
#include "connection_event.h"
void build_params(struct rdma_conn_param *params) {
memset(params, 0, sizeof(*params));
params->initiator_depth = params->responder_resources = 1; //指定发送队列的深度
params->rnr_retry_count = 7; /* infinite retry */
params->retry_count = 7;
}
void on_addr_resolved(struct rdma_cm_id *id) {//client
log_debug("on_addr_resolved:%p", id);
connection *conn = NULL;
worker *worker = NULL;
get_worker_and_connect_by_nd((uintptr_t) id->context, &worker, &conn);
if (conn == NULL) {
log_debug("get worker and connect by nd: conn is null");
//already closed
return;
}
id->verbs = g_net_env->pd->context;
int ret = create_conn_qp(conn, id);
if (ret != C_OK) {
log_error("conn(%lu-%p) create qp failed, errno:%d", conn->nd, conn, errno);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
return;
}
ret = rdma_setup_ioBuf(conn);
if (ret != C_OK) {
log_error("conn(%lu-%p) reg mem failed, errno:%d", conn->nd, conn, errno);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
return;
}
ret = rdma_resolve_route(id, TIMEOUT_IN_MS);
if (ret != 0) {
log_error("conn(%lu-%p) resolve failed, errno:%d", conn->nd, conn, errno);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
return;
}
log_debug("conn(%lu-%p) addr resolved", conn->nd, conn);
return;
}
void on_route_resolved(struct rdma_cm_id *id) {//client
log_debug("on_route_resolved:%p", id);
connection *conn = NULL;
worker *worker = NULL;
get_worker_and_connect_by_nd((uintptr_t) id->context, &worker, &conn);
if (conn == NULL) {
//already closed
log_error("get worker and connect by nd: conn is null");
return;
}
struct rdma_conn_param cm_params;
build_params(&cm_params);
int ret = rdma_connect(id, &cm_params);
if (ret) {
log_error("conn(%lu-%p) failed to connect to remote host , errno: %d, call on_disconnected(%p)", conn->nd, conn, errno, id);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
return;
}
log_debug("conn(%lu-%p) rdma connect, cmid:%p", conn->nd, conn, id);
}
void on_accept(struct rdma_cm_id* listen_id, struct rdma_cm_id* id) {//server
log_debug("on_accept:%p/%p", listen_id, id);
int ret = 0;
struct rdma_listener* server = (struct rdma_listener*)listen_id->context;
uint64_t nd = allocate_nd(0);
connection * conn = init_connection(nd, CONN_TYPE_SERVER);
if (conn == NULL) {
log_error("server(%lu-%p) init connection return null", server->nd, server);
rdma_reject(id, NULL, 0);
return;
}
id->verbs = g_net_env->pd->context;
ret = create_conn_qp(conn, id);
if (ret != C_OK) {
log_error("conn(%lu-%p) create qp failed, errno:%d", conn->nd, conn, errno);
rdma_reject(id, NULL, 0);
goto err_free;
}
ret = rdma_setup_ioBuf(conn);
if (ret != C_OK) {
log_error("conn(%lu-%p) reg mem failed, err:%d", conn->nd, conn, errno);
rdma_reject(id, NULL, 0);
goto err_destroy_qp;
}
id->context = (void*)conn->nd;
struct rdma_conn_param cm_params;
build_params(&cm_params);
ret = rdma_accept(id, &cm_params);
if (ret != 0) {
log_error("server(%lu-%p) conn(%lu-%p) accept failed, errno:%d", server->nd, server, conn->nd ,conn, errno);
rdma_reject(id, NULL, 0);
goto err_destroy_iobuf;
//goto err_destroy_qp;
}
log_debug("server(%lu-%p) conn(%lu-%p) accept cmid:%p", server->nd, server, conn->nd, conn, id);
add_conn_to_server(conn, server);
add_conn_to_worker(conn, conn->worker, conn->worker->nd_map);
conn->cm_id = id;
return;
err_destroy_iobuf:
rdma_destroy_ioBuf(conn);
err_destroy_qp:
destroy_conn_qp(conn);
err_free:
destroy_connection(conn);
free(conn);
return;
}
void on_connected(struct rdma_cm_id *id) {//server and client
connection *conn = NULL;
worker *worker = NULL;
struct rdma_listener *server = NULL;
get_worker_and_connect_by_nd((uintptr_t) id->context, &worker, &conn);
if (conn == NULL) {
//already closed
log_error("get worker and connect by nd: conn is null");
return;
}
struct sockaddr *local_addr = rdma_get_local_addr(id);// 获取本地地址
struct sockaddr *remote_addr = rdma_get_peer_addr(id);// 获取远程地址
// 获取本地 IPv4 地址和端口号
struct sockaddr_in *local_ipv4 = (struct sockaddr_in *)local_addr;
inet_ntop(AF_INET, &(local_ipv4->sin_addr), conn->local_addr, INET_ADDRSTRLEN);
snprintf(conn->local_addr + strlen(conn->local_addr), sizeof(conn->local_addr) - strlen(conn->local_addr),
":%d", ntohs(local_ipv4->sin_port));
// 获取远程 IPv4 地址和端口号
struct sockaddr_in *remote_ipv4 = (struct sockaddr_in *)remote_addr;
inet_ntop(AF_INET, &(remote_ipv4->sin_addr), conn->remote_addr, INET_ADDRSTRLEN);
snprintf(conn->remote_addr + strlen(conn->remote_addr), sizeof(conn->remote_addr) - strlen(conn->remote_addr),
":%d", ntohs(remote_ipv4->sin_port));
int ret = rdma_exchange_rx(conn); //TODO error handler
if (ret == C_ERR) {
log_error("conn(%lu-%p) on_connected failed: exchange rx return error");
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
}
log_debug("conn(%lu-%p) on_connected; conn finished", conn->nd, conn);
return;
}
void on_disconnected(struct rdma_cm_id* id) {//server and client
connection *conn = NULL;
worker *worker = NULL;
struct rdma_listener *server = NULL;
get_worker_and_connect_by_nd((uintptr_t) id->context, &worker, &conn);
if (conn == NULL) {
//already closed
log_error("get worker and connect by nd: conn is null");
//rdma_destroy_id(id);
return;
}
log_debug("conn(%lu-%p) proccess disconnected event, close begin", conn->nd, conn);
int state = get_conn_state(conn);
set_conn_state(conn, CONN_STATE_DISCONNECTED);
//notify all fds when disconnecting to avoid infinite waiting
if (conn->conn_type == CONN_TYPE_SERVER) {//server
} else {//client
notify_event(conn->connect_fd, 0);
}
notify_event(conn->msg_fd, 0);
notify_event(conn->close_fd, 0);
if (state == CONN_STATE_CONNECTING) {//release resources directly when an error occurs during the connection build process
//release resource
if (conn->conn_type == CONN_TYPE_SERVER) {//server
server = (struct rdma_listener*)conn->context;
del_conn_from_server(conn, server);
} else {//client
}
del_conn_from_worker(conn->nd, worker, worker->nd_map);
//del_conn_from_worker(conn->nd, conn->worker, conn->worker->closing_nd_map);
destroy_conn_qp(conn);
rdma_destroy_id(id);
rdma_destroy_ioBuf(conn);
destroy_connection(conn);
}
return;
}
void process_cm_event(struct rdma_cm_id *conn_id, struct rdma_cm_id *listen_id, int event_type) {
log_debug("process_net_event:%d->%s", event_type, rdma_event_str(event_type));
switch(event_type) {
case RDMA_CM_EVENT_ADDR_RESOLVED:
on_addr_resolved(conn_id);
break;
case RDMA_CM_EVENT_ROUTE_RESOLVED:
on_route_resolved(conn_id);
break;
case RDMA_CM_EVENT_CONNECT_REQUEST:
on_accept(listen_id, conn_id);
break;
case RDMA_CM_EVENT_ESTABLISHED:
on_connected(conn_id);
break;
case RDMA_CM_EVENT_DISCONNECTED:
on_disconnected(conn_id);
break;
case RDMA_CM_EVENT_REJECTED:
on_disconnected(conn_id);
break;
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
break;
default :
log_error("event channel received:unknown event:%d", event_type);
assert(event_type == 0);
break;
}
}
void *cm_thread(void *ctx) {
struct net_env_st *env = (struct net_env_st*)ctx;
struct rdma_cm_event *event;
struct rdma_cm_id *conn_id;
struct rdma_cm_id *listen_id;
int event_type;
while(1) {
if(env->close == 1) {
goto exit;
}
int ret = rdma_get_cm_event(env->event_channel, &event);
if (ret != 0) {
goto error;
}
conn_id = event->id;
listen_id = event->listen_id;
event_type = event->event;
rdma_ack_cm_event(event);
process_cm_event(conn_id, listen_id, event_type);
}
error:
//TODO
exit:
pthread_exit(NULL);
}

View File

@ -0,0 +1,31 @@
#ifndef CONNECTION_EVENT_H
#define CONNECTION_EVENT_H
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>
//#include "queue.h"
#include "hashmap.h"
#include "rdma_proto.h"
//#include "rdma_pool.h"
#include "log.h"
#include "connection.h"
void on_addr_resolved(struct rdma_cm_id *id);
void on_route_resolved(struct rdma_cm_id *id);
void on_accept(struct rdma_cm_id* listen_id, struct rdma_cm_id* id);
void on_connected(struct rdma_cm_id *id);
void on_disconnected(struct rdma_cm_id* id);
void process_cm_event(struct rdma_cm_id *conn_id, struct rdma_cm_id *listen_id, int event_type);
extern void *cm_thread(void *ctx);
#endif

View File

@ -0,0 +1,54 @@
#include "hashmap.h"
khash_t(map) *hashmap_create() {
return kh_init(map);
}
void hashmap_destroy(khash_t(map) *hashmap) {
kh_destroy(map, hashmap);
return;
}
uint64_t hashmap_get(khash_t(map) *hashmap, uint64_t key) {
khiter_t idx;
idx = kh_get(map, hashmap, key);
if (idx == kh_end(hashmap)) {
// ucs_warn("key not exists, key(0x%lx)", key);
return 0;
}
return kh_value(hashmap, idx);
}
int32_t hashmap_put(khash_t(map) *hashmap, uint64_t key, uint64_t value) {
int32_t rc;
khiter_t idx;
idx = kh_put(map, hashmap, key, &rc);
if(rc == -1) {
//ucs_error("key(0x%lx), value(0x%lx)", key, value);
return -1;
}
kh_value(hashmap, idx) = value;
return 0;
}
int32_t hashmap_exist(khash_t(map) *hashmap, uint64_t key) {
khiter_t idx = kh_get(map, hashmap, key);
return idx != kh_end(hashmap) ? 1 : 0;
}
int hashmap_del(khash_t(map) *hashmap, uint64_t key) {
khiter_t idx;
idx = kh_get(map, hashmap, key);
if (idx == kh_end(hashmap)) {
//ucs_warn("key not exists, key(0x%lx)", key);
return 0;
}
kh_del(map, hashmap, idx);
return 0;
}

View File

@ -0,0 +1,14 @@
#ifndef _CB_HASH_MAP_H_
#define _CB_HASH_MAP_H_
#include <stdint.h>
#include "khash.h"
KHASH_MAP_INIT_INT64(map, uint64_t);
khash_t(map) *hashmap_create();
void hashmap_destroy(khash_t(map) *hashmap);
uint64_t hashmap_get(khash_t(map) *hashmap, uint64_t key);
int32_t hashmap_put(khash_t(map) *hashmap, uint64_t key, uint64_t value);
int32_t hashmap_exist(khash_t(map) *hashmap, uint64_t key);
int hashmap_del(khash_t(map) *hashmap, uint64_t key);
#endif

View File

@ -0,0 +1,708 @@
/* The MIT License
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
An example:
#include "khash.h"
KHASH_MAP_INIT_INT(32, char)
int main() {
int ret, is_missing;
khiter_t k;
khash_t(32) *h = kh_init(32);
k = kh_put(32, h, 5, &ret);
kh_value(h, k) = 10;
k = kh_get(32, h, 10);
is_missing = (k == kh_end(h));
k = kh_get(32, h, 5);
kh_del(32, h, k);
for (k = kh_begin(h); k != kh_end(h); ++k)
if (kh_exist(h, k)) kh_value(h, k) = 1;
kh_destroy(32, h);
return 0;
}
*/
/*
2013-05-02 (0.2.8):
* Use quadratic probing. When the capacity is power of 2, stepping function
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
hashing on cache performance and is more robust than linear probing.
In theory, double hashing should be more robust than quadratic probing.
However, my implementation is probably not for large hash tables, because
the second hash function is closely tied to the first hash function,
which reduce the effectiveness of double hashing.
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
2011-12-29 (0.2.7):
* Minor code clean up; no actual effect.
2011-09-16 (0.2.6):
* The capacity is a power of 2. This seems to dramatically improve the
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
- http://code.google.com/p/ulib/
- http://nothings.org/computer/judy/
* Allow to optionally use linear probing which usually has better
performance for random input. Double hashing is still the default as it
is more robust to certain non-random input.
* Added Wang's integer hash function (not used by default). This hash
function is more robust to certain non-random input.
2011-02-14 (0.2.5):
* Allow to declare global functions.
2009-09-26 (0.2.4):
* Improve portability
2008-09-19 (0.2.3):
* Corrected the example
* Improved interfaces
2008-09-11 (0.2.2):
* Improved speed a little in kh_put()
2008-09-10 (0.2.1):
* Added kh_clear()
* Fixed a compiling error
2008-09-02 (0.2.0):
* Changed to token concatenation which increases flexibility.
2008-08-31 (0.1.2):
* Fixed a bug in kh_get(), which has not been tested previously.
2008-08-31 (0.1.1):
* Added destructor
*/
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
/*!
@header
Generic hash table library.
*/
#define AC_VERSION_KHASH_H "0.2.8"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* Clang analyzer thinks that `h->flags` can be NULL, but this is
* the wrong assumption - add `kassert()` to suppress the warning */
#ifdef __clang_analyzer__
#include <assert.h>
#define kassert(...) assert(__VA_ARGS__)
#define kmemset_analyzer(P, Z, N) kmemset(P, Z, N)
#else
#define kassert(...)
#define kmemset_analyzer(P, Z, N)
#endif
/* compiler specific configuration */
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
#elif ULONG_MAX == 0xffffffffu
typedef unsigned long khint32_t;
#endif
#if ULONG_MAX == ULLONG_MAX
typedef unsigned long khint64_t;
#else
typedef unsigned long long khint64_t;
#endif
#ifndef kh_inline
#ifdef _MSC_VER
#define kh_inline __inline
#else
#define kh_inline inline
#endif
#endif /* kh_inline */
#ifndef klib_unused
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
#define klib_unused __attribute__ ((__unused__))
#else
#define klib_unused
#endif
#endif /* klib_unused */
typedef khint32_t khint_t;
typedef khint_t khiter_t;
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
#ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
#ifndef kcalloc
#define kcalloc(N,Z) calloc(N,Z)
#endif
#ifndef kmalloc
#define kmalloc(Z) malloc(Z)
#endif
#ifndef krealloc
#define krealloc(P,Z) realloc(P,Z)
#endif
#ifndef kfree
#define kfree(P) free(P)
#endif
#ifndef kmemset
#define kmemset(P,Z,N) memset(P,Z,N)
#endif
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct kh_##name##_s { \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
khval_t *vals; \
} kh_##name##_t;
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(void); \
extern kh_##name##_t *kh_init_##name##_inplace(kh_##name##_t *h); \
extern void kh_destroy_##name(kh_##name##_t *h); \
extern void kh_destroy_##name##_inplace(kh_##name##_t *h); \
extern void kh_clear_##name(kh_##name##_t *h); \
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
SCOPE kh_##name##_t *kh_init_##name(void) { \
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
} \
SCOPE kh_##name##_t *kh_init_##name##_inplace(kh_##name##_t *h) { \
return (kh_##name##_t*)kmemset(h, 0, sizeof(kh_##name##_t)); \
} \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
kfree((void *)h->keys); kfree(h->flags); \
kfree((void *)h->vals); \
kfree(h); \
} \
} \
SCOPE void kh_destroy_##name##_inplace(kh_##name##_t *h) \
{ \
kfree((void *)h->keys); \
kfree((void *)h->flags); \
kfree((void *)h->vals); \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
\
kassert(h->flags != NULL); \
\
k = __hash_func(key); i = k & mask; \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
i = (i + (++step)) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (!new_flags) return -1; \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (!new_keys) { kfree(new_flags); return -1; } \
h->keys = new_keys; \
kmemset_analyzer(h->keys + (h->n_buckets * sizeof(khkey_t)), 0, \
(new_n_buckets - h->n_buckets) * sizeof(khkey_t)); \
if (kh_is_map) { \
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
if (!new_vals) { kfree(new_flags); return -1; } \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) h->vals[i] = val; \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
return 0; \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size<<1)) { \
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
*ret = -1; return h->n_buckets; \
} \
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
*ret = -1; return h->n_buckets; \
} \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
\
kassert(h->flags != NULL); \
\
{ \
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
i = (i + (++step)) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
else x = i; \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; ++h->n_occupied; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; \
*ret = 2; \
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#define KHASH_DECLARE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
#define KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t)
#define KHASH_IMPL(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
__KHASH_IMPL(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
#define KHASH_STATIC_INITIALIZER {0}
/* --- BEGIN OF HASH FUNCTIONS --- */
/*! @function
@abstract Integer hash function
@param key The integer [khint32_t]
@return The hash value [khint_t]
*/
#define kh_int_hash_func(key) (khint32_t)(key)
/*! @function
@abstract Integer comparison function
*/
#define kh_int_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract 64-bit integer hash function
@param key The integer [khint64_t]
@return The hash value [khint_t]
*/
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
/*! @function
@abstract 64-bit integer comparison function
*/
#define kh_int64_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract const char* hash function
@param s Pointer to a null terminated string
@return The hash value
*/
static kh_inline khint_t __ac_X31_hash_string(const char *s)
{
khint_t h = (khint_t)*s;
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
return h;
}
/*! @function
@abstract Another interface to const char* hash function
@param key Pointer to a null terminated string [const char*]
@return The hash value [khint_t]
*/
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
/*! @function
@abstract Const char* comparison function
*/
static inline int kh_str_hash_equal(const char *str1, const char *str2)
{
/* Add NULL assertions to silence cppcheck */
kassert(str1 != NULL);
kassert(str2 != NULL);
return strcmp(str1, str2) == 0;
}
static kh_inline khint_t __ac_Wang_hash(khint_t key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
/* --- END OF HASH FUNCTIONS --- */
/* Other convenient macros... */
/*!
@abstract Type of the hash table.
@param name Name of the hash table [symbol]
*/
#define khash_t(name) kh_##name##_t
/*! @function
@abstract Initiate a hash table.
@param name Name of the hash table [symbol]
@return Pointer to the hash table [khash_t(name)*]
*/
#define kh_init(name) kh_init_##name()
/*! @function
@abstract Initiate a hash table if the in-place case.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_init_inplace(name, h) kh_init_##name##_inplace(h)
/*! @function
@abstract Destroy a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
@abstract Destroy a hash table if the in-place case.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy_inplace(name, h) kh_destroy_##name##_inplace(h)
/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_clear(name, h) kh_clear_##name(h)
/*! @function
@abstract Resize a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param s New size [khint_t]
*/
#define kh_resize(name, h, s) kh_resize_##name(h, s)
/*! @function
@abstract Insert a key to the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@param r Extra return code: -1 if the operation failed;
0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
*/
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
typedef enum ucs_kh_put {
UCS_KH_PUT_FAILED = -1,
UCS_KH_PUT_KEY_PRESENT = 0,
UCS_KH_PUT_BUCKET_EMPTY = 1,
UCS_KH_PUT_BUCKET_CLEAR = 2
} ucs_kh_put_t;
/*! @function
@abstract Retrieve a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
/*! @function
@abstract Remove a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Iterator to the element to be deleted [khint_t]
*/
#define kh_del(name, h, k) kh_del_##name(h, k)
/*! @function
@abstract Test whether a bucket contains data.
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return 1 if containing data; 0 otherwise [int]
*/
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
/*! @function
@abstract Get key given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Key [type of keys]
*/
#define kh_key(h, x) ((h)->keys[x])
/*! @function
@abstract Get value given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Value [type of values]
@discussion For hash sets, calling this results in segfault.
*/
#define kh_val(h, x) ((h)->vals[x])
/*! @function
@abstract Alias of kh_val()
*/
#define kh_value(h, x) ((h)->vals[x])
/*! @function
@abstract Get the start iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The start iterator [khint_t]
*/
#define kh_begin(h) (khint_t)(0)
/*! @function
@abstract Get the end iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The end iterator [khint_t]
*/
#define kh_end(h) ((h)->n_buckets)
/*! @function
@abstract Get the number of elements in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of elements in the hash table [khint_t]
*/
#define kh_size(h) ((h)->size)
/*! @function
@abstract Get the number of buckets in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of buckets in the hash table [khint_t]
*/
#define kh_n_buckets(h) ((h)->n_buckets)
/*! @function
@abstract Iterate over the entries in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
(vvar) = kh_val(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the keys in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param code Block of code to execute
*/
#define kh_foreach_key(h, kvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the values in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(vvar) = kh_val(h,__i); \
code; \
} }
/* More convenient interfaces */
/*! @function
@abstract Instantiate a hash set containing integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT(name) \
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT(name, khval_t) \
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT64(name) \
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT64(name, khval_t) \
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
typedef const char *kh_cstr_t;
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_STR(name) \
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#endif /* __AC_KHASH_H */

View File

@ -0,0 +1,176 @@
/*
* Copyright (c) 2020 rxi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "log.h"
#define MAX_CALLBACKS 32
typedef struct {
log_LogFn fn;
void *udata;
int level;
} Callback;
static struct {
void *udata;
log_LockFn lock;
int level;
bool quiet;
Callback callbacks[MAX_CALLBACKS];
} L;
static const char *level_strings[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};
#ifdef LOG_USE_COLOR
static const char *level_colors[] = {
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
};
#endif
static void stdout_callback(log_Event *ev) {
char tmp[64];
tmp[strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
char buf[74];
snprintf(buf, sizeof(buf), "%s.%06ld", tmp, ev->time_usec);
#ifdef LOG_USE_COLOR
fprintf(
ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
buf, level_colors[ev->level], level_strings[ev->level],
ev->file, ev->line);
#else
fprintf(
ev->udata, "%s %-5s %s:%d: ",
buf, level_strings[ev->level], ev->file, ev->line);
#endif
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}
static void file_callback(log_Event *ev) {
char tmp[64];
tmp[strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
char buf[74];
snprintf(buf, sizeof(buf), "%s.%06ld", tmp, ev->time_usec);
fprintf(
ev->udata, "%s %-5s %s:%d: ",
buf, level_strings[ev->level], ev->file, ev->line);
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}
static void lock(void) {
if (L.lock) { L.lock(true, L.udata); }
}
static void unlock(void) {
if (L.lock) { L.lock(false, L.udata); }
}
const char* log_level_string(int level) {
return level_strings[level];
}
void log_set_lock(log_LockFn fn, void *udata) {
L.lock = fn;
L.udata = udata;
}
void log_set_level(int level) {
L.level = level;
}
void log_set_quiet(bool enable) {
L.quiet = enable;
}
int log_add_callback(log_LogFn fn, void *udata, int level) {
for (int i = 0; i < MAX_CALLBACKS; i++) {
if (!L.callbacks[i].fn) {
L.callbacks[i] = (Callback) { fn, udata, level };
return 0;
}
}
return -1;
}
int log_add_fp(FILE *fp, int level) {
return log_add_callback(file_callback, fp, level);
}
static void init_event(log_Event *ev, void *udata) {
if (!ev->time) {
//time_t t = time(NULL);
//ev->time = localtime(&t);
struct timeval tv;
gettimeofday(&tv, NULL);
ev->time = localtime(&tv.tv_sec);
ev->time_usec = tv.tv_usec;
}
ev->udata = udata;
}
void log_log(int level, const char *file, int line, const char *fmt, ...) {
log_Event ev = {
.fmt = fmt,
.file = file,
.line = line,
.level = level,
};
lock();
if (!L.quiet && level >= L.level) {
init_event(&ev, stderr);
va_start(ev.ap, fmt);
stdout_callback(&ev);
va_end(ev.ap);
}
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
Callback *cb = &L.callbacks[i];
if (level >= cb->level) {
init_event(&ev, cb->udata);
va_start(ev.ap, fmt);
cb->fn(&ev);
va_end(ev.ap);
}
}
unlock();
}

View File

@ -0,0 +1,51 @@
/**
* Copyright (c) 2020 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See `log.c` for details.
*/
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <time.h>
#include <sys/time.h>
#define LOG_VERSION "0.1.0"
typedef struct {
va_list ap;
const char *fmt;
const char *file;
struct tm *time;
suseconds_t time_usec;
void *udata;
int line;
int level;
} log_Event;
typedef void (*log_LogFn)(log_Event *ev);
typedef void (*log_LockFn)(bool lock, void *udata);
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
const char* log_level_string(int level);
void log_set_lock(log_LockFn fn, void *udata);
void log_set_level(int level);
void log_set_quiet(bool enable);
int log_add_callback(log_LogFn fn, void *udata, int level);
int log_add_fp(FILE *fp, int level);
void log_log(int level, const char *file, int line, const char *fmt, ...);
#endif

View File

@ -0,0 +1,61 @@
#include "memory_pool.h"
void *page_aligned_zalloc(size_t size) {
void *tmp;
size_t aligned_size, page_size = sysconf(_SC_PAGESIZE);
aligned_size = (size + page_size - 1) & (~(page_size - 1));
if (posix_memalign(&tmp, page_size, aligned_size)) {
log_error("posix_memalign failed");
return tmp;
}
memset(tmp, 0x00, aligned_size);
return tmp;
}
memory_pool* init_memory_pool(int block_num, int block_size, int level, struct ibv_pd* pd) {
memory_pool * pool = (memory_pool*)malloc(sizeof(memory_pool));
if (pool == NULL) {
return NULL;
}
pool->size = (uint64_t)block_num * (uint64_t)block_size;
pool->original_mem = page_aligned_zalloc(pool->size);
if (pool->original_mem == NULL) {
log_error("malloc pool original memory failed");
goto err_free_pool;
}
pool->allocation = buddy_new(level);
pool->pd = pd;
int access = IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_WRITE;
pool->mr = ibv_reg_mr(pool->pd, pool->original_mem, pool->size, access);
if (pool->mr == NULL) {
log_error("RDMA: reg mr for data buffer failed, errno:%d", errno);
goto err_delete_buddy;
}
return pool;
err_delete_buddy:
buddy_delete(pool->allocation);
free(pool->original_mem);
err_free_pool:
free(pool);
return NULL;
}
void close_memory_pool(memory_pool* pool) {
if (pool != NULL) {
if (pool->mr != NULL) {
ibv_dereg_mr(pool->mr);
}
pool->pd = NULL;
if (pool->allocation != NULL) {
buddy_delete(pool->allocation);
}
if (pool->original_mem != NULL) {
free(pool->original_mem);
}
free(pool);
}
}

View File

@ -0,0 +1,29 @@
#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H
#include <stdlib.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>
#include <unistd.h>
#include "buddy.h"
#include "log.h"
static int MEMORY_BLOCK_SIZE = 128;
static int MEMORY_BLOCK_COUNT = 1280;
typedef struct memory_pool {
char* original_mem;
struct buddy* allocation;
uint64_t size;
struct ibv_pd* pd;
struct ibv_mr* mr;
} memory_pool;
void *page_aligned_zalloc(size_t size);
memory_pool* init_memory_pool(int block_num, int block_size, int level, struct ibv_pd* pd);
void close_memory_pool(memory_pool* pool);
#endif

View File

@ -0,0 +1,105 @@
#include "queue.h"
#include <malloc.h>
#include <stdio.h>
/*构造一个空队列*/
Queue *InitQueue() {
Queue *pqueue = (Queue *)malloc(sizeof(Queue));
if(pqueue != NULL) {
pqueue->front = NULL;
pqueue->rear = NULL;
pqueue->size = 0;
}
return pqueue;
}
/*销毁一个队列*/
void DestroyQueue(Queue *pqueue) {
if(IsEmpty(pqueue) != 1)
ClearQueue(pqueue);
free(pqueue);
}
/*清空一个队列*/
void ClearQueue(Queue *pqueue)
{
while(IsEmpty(pqueue)!=1) {
DeQueue(pqueue,NULL);
}
}
/*判断队列是否为空*/
int IsEmpty(Queue *pqueue) {
if(pqueue->front==NULL&&pqueue->rear==NULL&&pqueue->size==0)
return 1;
else
return 0;
}
/*返回队列大小*/
int GetSize(Queue *pqueue) {
return pqueue->size;
}
/*返回队头元素*/
PNode GetFront(Queue *pqueue,Item *pitem) {
if(IsEmpty(pqueue)!=1&&pitem!=NULL) {
*pitem = pqueue->front->data;
}
return pqueue->front;
}
/*返回队尾元素*/
PNode GetRear(Queue *pqueue,Item *pitem) {
if(IsEmpty(pqueue)!=1&&pitem!=NULL) {
*pitem = pqueue->rear->data;
}
return pqueue->rear;
}
/*将新元素入队*/
PNode EnQueue(Queue *pqueue,Item item) {
PNode pnode = (PNode)malloc(sizeof(Node));
if(pnode != NULL) {
pnode->data = item;
pnode->next = NULL;
//printf("enqueue %d\n",pnode->data);
if(IsEmpty(pqueue)) {
pqueue->front = pnode;
}
else {
pqueue->rear->next = pnode;
}
pqueue->rear = pnode;
pqueue->size++;
}
return pnode;
}
/*队头元素出队*/
PNode DeQueue(Queue *pqueue,Item *pitem) {
PNode pnode = pqueue->front;
if(IsEmpty(pqueue)!=1&&pnode!=NULL) {
if(pitem!=NULL)
//printf("dequeue %d\n",pnode->data);
*pitem = pnode->data;
pqueue->size--;
pqueue->front = pnode->next;
free(pnode);
if(pqueue->size==0)
pqueue->rear = NULL;
}
return pqueue->front;
}
/*遍历队列并对各数据项调用visit函数*/
void QueueTraverse(Queue *pqueue,void (*visit)()) {
PNode pnode = pqueue->front;
int i = pqueue->size;
while(i--) {
visit(pnode->data);
pnode = pnode->next;
}
}

View File

@ -0,0 +1,47 @@
#ifndef QUEUE_H
#define QUEUE_H
typedef void* Item;
typedef struct node * PNode;
typedef struct node {
Item data;
PNode next;
} Node;
typedef struct {
PNode front;
PNode rear;
int size;
} Queue;
/*构造一个空队列*/
Queue* InitQueue();
/*销毁一个队列*/
void DestroyQueue(Queue *pqueue);
/*清空一个队列*/
void ClearQueue(Queue *pqueue);
/*判断队列是否为空*/
int IsEmpty(Queue *pqueue);
/*返回队列大小*/
int GetSize(Queue *pqueue);
/*返回队头元素*/
PNode GetFront(Queue *pqueue,Item *pitem);
/*返回队尾元素*/
PNode GetRear(Queue *pqueue,Item *pitem);
/*将新元素入队*/
PNode EnQueue(Queue *pqueue,Item item);
/*队头元素出队*/
PNode DeQueue(Queue *pqueue,Item *pitem);
/*遍历队列并对各数据项调用visit函数*/
void QueueTraverse(Queue *pqueue,void (*visit)());
#endif

View File

@ -0,0 +1,345 @@
package rdma
/*
#cgo LDFLAGS: -libverbs -lrdmacm -lrt -lpthread
#cgo CFLAGS: -std=gnu99 -g
#include "client.h"
#include "server.h"
*/
import "C"
import (
"fmt"
"net"
"reflect"
"sync"
"sync/atomic"
"time"
"unsafe"
)
const (
CONN_ST_CLOSED = 1
CONN_ST_CONNECTING = 2
CONN_ST_CONNECTED = 3
CLIENT_CONN = 4
SERVER_CONN = 5
)
type RdmaAddr struct {
network string
address string
}
type Server struct {
LocalIp string
LocalPort string
RemoteIp string
RemotePort string
cListener unsafe.Pointer
}
type Connection struct {
cConn unsafe.Pointer
state int32
mu sync.RWMutex
//recvMsgList *list.List
dataMap sync.Map
recvDataMap sync.Map
rFd chan struct{}
wFd chan struct{}
conntype int
Ctx interface{}
localAddr *RdmaAddr
remoteAddr *RdmaAddr
TargetIp string
TargetPort string
}
func CbuffToSlice(ptr unsafe.Pointer, length int) []byte {
var buffer []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buffer))
hdr.Data = uintptr(ptr)
hdr.Len = int(length)
hdr.Cap = int(length)
return buffer
}
func NewRdmaServer(targetIp, targetPort string) (server *Server, err error) { //, memoryPool *MemoryPool, headerPool *ObjectPool, responsePool *ObjectPool
server = &Server{}
server.RemoteIp = ""
server.RemotePort = ""
server.LocalIp = targetIp
server.LocalPort = targetPort
cCtx := C.start_rdma_server_by_addr(C.CString(server.LocalIp), C.CString(server.LocalPort))
if cCtx == nil {
return nil, fmt.Errorf("server(%p) start failed", server)
}
server.cListener = unsafe.Pointer(cCtx)
return server, nil
}
func (server *Server) Accept() (*Connection, error) {
conn := &Connection{}
cConn := C.get_rdma_server_conn((*C.struct_rdma_listener)(server.cListener))
if cConn == nil {
return nil, fmt.Errorf("server(%p) accept failed", server)
}
atomic.StoreInt32(&conn.state, CONN_ST_CONNECTING)
conn.init(cConn)
conn.conntype = SERVER_CONN
conn.Ctx = server
conn.localAddr = &RdmaAddr{address: C.GoString(&(cConn.local_addr[0])), network: "rdma"}
conn.remoteAddr = &RdmaAddr{address: C.GoString(&(cConn.remote_addr[0])), network: "rdma"}
atomic.StoreInt32(&conn.state, CONN_ST_CONNECTED)
return conn, nil
}
func (server *Server) Close() {
C.close_rdma_server((*C.struct_rdma_listener)(server.cListener))
return
}
func (conn *Connection) Dial(targetIp, targetPort string) error {
cConn := C.rdma_connect_by_addr(C.CString(targetIp), C.CString(targetPort))
if cConn == nil {
return fmt.Errorf("conn(%p) dial failed", conn)
}
atomic.StoreInt32(&conn.state, CONN_ST_CONNECTING)
conn.init(cConn)
conn.conntype = CLIENT_CONN
conn.localAddr = &RdmaAddr{address: C.GoString(&(cConn.local_addr[0])), network: "rdma"}
conn.remoteAddr = &RdmaAddr{address: C.GoString(&(cConn.remote_addr[0])), network: "rdma"}
atomic.StoreInt32(&conn.state, CONN_ST_CONNECTED)
return nil
}
func (rdmaAddr *RdmaAddr) Network() string {
return rdmaAddr.network
}
func (rdmaAddr *RdmaAddr) String() string {
return rdmaAddr.address
}
func (conn *Connection) LocalAddr() net.Addr {
return conn.localAddr
}
func (conn *Connection) RemoteAddr() net.Addr {
return conn.remoteAddr
}
func (conn *Connection) init(cConn *C.connection) {
conn.cConn = unsafe.Pointer(cConn)
//conn.SetDeadline(time.Now().Add(200 * time.Millisecond))
//C.set_conn_context(cConn, unsafe.Pointer(conn))
conn.rFd = make(chan struct{}, 100)
conn.wFd = make(chan struct{}, 100)
}
func (conn *Connection) SetDeadline(t time.Time) error {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED && atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTING {
return fmt.Errorf("set deadline failed, conn(%p) has been closed", conn)
}
C.set_send_timeout_us((*C.connection)(conn.cConn), (C.int64_t(t.UnixNano()-time.Now().UnixNano()) / 1000))
C.set_recv_timeout_us((*C.connection)(conn.cConn), (C.int64_t(t.UnixNano()-time.Now().UnixNano()) / 1000))
return nil
}
func (conn *Connection) SetWriteDeadline(t time.Time) error {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED {
return fmt.Errorf("set deadline failed, conn(%p) has been closed", conn)
}
C.set_send_timeout_us((*C.connection)(conn.cConn), (C.int64_t(t.UnixNano()-time.Now().UnixNano()) / 1000))
return nil
}
func (conn *Connection) SetReadDeadline(t time.Time) error {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED {
return fmt.Errorf("set deadline failed, conn(%p) has been closed", conn)
}
C.set_recv_timeout_us((*C.connection)(conn.cConn), (C.int64_t(t.UnixNano()-time.Now().UnixNano()) / 1000))
return nil
}
func (conn *Connection) Close() (err error) {
if atomic.LoadInt32(&conn.state) != CONN_ST_CLOSED {
C.conn_disconnect((*C.connection)(conn.cConn)) //TODO
atomic.StoreInt32(&conn.state, CONN_ST_CLOSED)
}
return nil
}
func GetDataBuffer(len uint32) ([]byte, error) {
var bufferSize C.int64_t
dataPtr := C.get_pool_data_buffer(C.uint32_t(len), &bufferSize)
dataBuffer := CbuffToSlice(dataPtr, int(bufferSize))
return dataBuffer, nil
}
func ReleaseDataBuffer(conn *Connection, dataBuffer []byte, size uint32) error {
C.release_pool_data_buffer((*C.connection)(conn.cConn) ,unsafe.Pointer(&dataBuffer[0]), C.uint32_t(size))
return nil
}
func (conn *Connection) GetConnTxDataBuffer(len uint32) ([]byte, error) {
dataEntry := C.get_conn_tx_data_buffer((*C.connection)(conn.cConn), C.uint32_t(len))
if dataEntry == nil {
return nil, fmt.Errorf("conn(%p) get tx data buffer failed", conn)
}
dataBuffer := CbuffToSlice(unsafe.Pointer(dataEntry.addr), int(dataEntry.mem_len))
conn.dataMap.Store(&dataBuffer[0], dataEntry)
return dataBuffer, nil
}
func (conn *Connection) ReleaseConnTxDataBuffer(dataBuffer []byte) error {
entry, ok := conn.dataMap.Load(&dataBuffer[0])
if !ok {
return fmt.Errorf("conn(%p) release tx data buffer failed, no such dataEntry", conn)
}
dataEntry, ok := entry.(*C.data_entry)
if !ok {
return fmt.Errorf("conn(%p) release tx data buffer failed, type convert error", conn)
}
C.release_conn_tx_data_buffer((*C.connection)(conn.cConn), (*C.data_entry)(dataEntry))
conn.dataMap.Delete(&dataBuffer[0])
return nil
}
func (conn *Connection) Write(data []byte) (int, error) {
return 0, nil
}
func (conn *Connection) WriteExternalBuffer(data []byte, size int) (int, error) {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED {
return -1, fmt.Errorf("conn(%p) has been closed", conn)
}
ret := C.conn_app_write_external_buffer((*C.connection)(conn.cConn), unsafe.Pointer(&data[0]), C.uint32_t(size))
if ret != 0 {
return -1, fmt.Errorf("conn(%p) write external data buffer failed", conn)
}
return size, nil
}
func (conn *Connection) WriteBuffer(data []byte, size int) (int, error) {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED {
return -1, fmt.Errorf("conn(%p) has been closed", conn)
}
entry, ok := conn.dataMap.Load(&data[0])
if !ok {
return -1, fmt.Errorf("conn(%p) write buffer failed, no such dataEntry", conn)
}
dataEntry, ok := entry.(*C.data_entry)
if !ok {
return -1, fmt.Errorf("conn(%p) write buffer failed, type convert error", conn)
}
ret := C.conn_app_write((*C.connection)(conn.cConn), (*C.data_entry)(dataEntry), C.uint32_t(size))
if ret != 0 {
return -1, fmt.Errorf("conn(%p) write data buffer failed", conn)
}
return size, nil
}
func (conn *Connection) Read([]byte) (int, error) {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED {
return -1, fmt.Errorf("conn(%p) has been closed", conn)
}
return 0, nil
}
func (conn *Connection) ReleaseConnRxDataBuffer(recvDataBuffer []byte) error {
entry, ok := conn.recvDataMap.Load(&recvDataBuffer[0])
if !ok {
return fmt.Errorf("conn(%p) release rx data buffer failed, no such dataEntry", conn)
}
dataEntry, ok := entry.(*C.data_entry)
if !ok {
return fmt.Errorf("conn(%p) release rx data buffer failed, type convert error", conn)
}
ret := C.release_conn_rx_data_buffer((*C.connection)(conn.cConn), (*C.data_entry)(dataEntry))
conn.recvDataMap.Delete(&recvDataBuffer[0])
if ret == 1 {
return fmt.Errorf("conn(%p) release rx data buffer failed", conn)
}
return nil
}
func (conn *Connection) GetRecvMsgBuffer() ([]byte, error) {
if atomic.LoadInt32(&conn.state) != CONN_ST_CONNECTED {
return nil, fmt.Errorf("conn(%p) has been closed", conn)
}
dataEntry := C.get_recv_msg_buffer((*C.connection)(conn.cConn))
if dataEntry == nil {
return nil, fmt.Errorf("conn(%p) get recv msg failed", conn)
}
recvDataBuffer := CbuffToSlice(unsafe.Pointer(dataEntry.addr), int(dataEntry.data_len))
conn.recvDataMap.Store(&recvDataBuffer[0], dataEntry)
return recvDataBuffer, nil
}
type RdmaEnvConfig struct {
RdmaPort string
MemBlockNum int
MemBlockSize int
MemPoolLevel int
ConnDataSize int
WqDepth int
MinCqeNum int
EnableRdmaLog bool
RdmaLogDir string
WorkerNum int
}
func parseRdmaEnvConfig(gCfg *RdmaEnvConfig, cCfg *C.struct_rdma_env_config) error {
if cCfg == nil {
return fmt.Errorf("gCfg parse to cCfg failed: cCfg is NULL")
}
if gCfg == nil {
return nil
}
if gCfg.MemBlockNum != 0 {
cCfg.mem_block_num = C.int(gCfg.MemBlockNum)
}
if gCfg.MemBlockSize != 0 {
cCfg.mem_block_size = C.int(gCfg.MemBlockSize)
}
if gCfg.MemPoolLevel != 0 {
cCfg.mem_pool_level = C.int(gCfg.MemPoolLevel)
}
if gCfg.ConnDataSize != 0 {
cCfg.conn_data_size = C.int(gCfg.ConnDataSize)
}
if gCfg.WqDepth != 0 {
cCfg.wq_depth = C.int(gCfg.WqDepth)
}
if gCfg.MinCqeNum != 0 {
cCfg.min_cqe_num = C.int(gCfg.MinCqeNum)
}
if gCfg.EnableRdmaLog {
cCfg.enable_rdma_log = C.int(1)
}
if gCfg.RdmaLogDir != "" {
cCfg.rdma_log_dir = C.CString(gCfg.RdmaLogDir)
}
if gCfg.WorkerNum != 0 {
cCfg.worker_num = C.int(gCfg.WorkerNum)
}
return nil
}
func InitPool(cfg *RdmaEnvConfig) error {
cCfg := C.get_rdma_env_config()
if err := parseRdmaEnvConfig(cfg, cCfg); err != nil {
return err
}
ret := int(C.init_rdma_env(cCfg))
if ret != 0 {
return fmt.Errorf("init pool failed")
}
return nil
}
func DestroyPool() {
C.destroy_rdma_env()
}

View File

@ -0,0 +1,36 @@
#ifndef RDMA_H
#define RDMA_H
#include <stdint.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "connection.h"
#include "transfer_event.h"
#include "connection_event.h"
#include <sys/eventfd.h>
#define TEST_NZ(x) if ( (x)) fprintf(stderr, "%s:%d %d, %s\n", __FILE__, __LINE__, errno, strerror(errno));
#define TEST_Z(x) if (!(x)) fprintf(stderr, "%s:%d %d, %s\n", __FILE__, __LINE__, errno, strerror(errno));
#define TEST_NZ_(x) if ((x)) { \
fprintf(stderr, "%s:%d %d, %s\n", __FILE__, __LINE__, errno, strerror(errno)); \
return NULL; \
}
#define TEST_Z_(x) if (!(x)) { \
fprintf(stderr, "%s:%d %d, %s\n", __FILE__, __LINE__, errno, strerror(errno)); \
return NULL; \
}
#endif

View File

@ -0,0 +1,416 @@
#include "rdma_proto.h"
int WQ_DEPTH = 32;
int WQ_SG_DEPTH = 2;
int MIN_CQE_NUM = 1024;
int CONN_DATA_SIZE = 128*1024*32;
struct rdma_pool *rdma_pool = NULL;
struct rdma_env_config *rdma_env_config = NULL;
FILE *debug_fp = NULL;
FILE *error_fp = NULL;
struct net_env_st *g_net_env = NULL;
uint64_t allocate_nd(int type) {
int id_index = ID_GEN_CTRL;
union conn_nd_union id;
id_index += 1;
id.nd_.worker_id = __sync_fetch_and_add((g_net_env->id_gen + id_index), 1) & 0xFF;
id.nd_.type = type & 0xFF;
id.nd_.m1 = 'c';
id.nd_.m2 = 'b';
id.nd_.id = __sync_fetch_and_add((g_net_env->id_gen + ID_GEN_MAX -1), 1);
return id.nd;
}
void cbrdma_parse_nd(uint64_t nd, int *id, int * worker_id, int * is_server, int * is_active) {
*id = (nd & 0xFFFFFFFF);
*worker_id = ((nd >> 32) & 0xFF);
uint8_t type = (((nd >> 32) & 0xFF00) >> 8);
*is_server = type & 0x80;
*is_active = type & 0x40;
}
struct rdma_env_config* get_rdma_env_config() {
rdma_env_config = (struct rdma_env_config*)malloc(sizeof(struct rdma_env_config));
memset(rdma_env_config, 0, sizeof(struct rdma_env_config));
rdma_env_config->mem_block_num = 1 * 1024;
rdma_env_config->mem_block_size = 4 * 1024;
rdma_env_config->mem_pool_level = 10;
rdma_env_config->conn_data_size = 1024 * 1024;
rdma_env_config->wq_depth = 32;
rdma_env_config->min_cqe_num = 1024;
rdma_env_config->enable_rdma_log = 0;
rdma_env_config->rdma_log_dir = malloc(256 * sizeof(char));
strcpy(rdma_env_config->rdma_log_dir, "/");
rdma_env_config->worker_num = 4;
return rdma_env_config;
}
int init_worker(worker *worker, event_callback cb, int index) {
int ret = 0;
char str[20];
cpu_set_t cpuset;
worker->pd = g_net_env->pd;
//log_debug("ibv_alloc_pd:%p", worker->pd);
worker->comp_channel = ibv_create_comp_channel(g_net_env->ctx);
if (worker->comp_channel == NULL) {
log_error("worker(%p) ibv create comp channel failed", worker);
return C_ERR;
}
//log_debug("ibv_create_comp_channel:%p",worker->comp_channel);
worker->cq = ibv_create_cq(g_net_env->ctx, MIN_CQE_NUM, NULL, worker->comp_channel, 0);
if (worker->cq == NULL) {
//return assert,ignore resource free
log_error("worker(%p) create cq failed, errno:%d", worker, errno);
goto err_destroy_compchannel;
}
//log_debug("ibv_create_cq:%p", worker->cq);
ibv_req_notify_cq(worker->cq, 0);
ret = pthread_spin_init(&(worker->lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("worker(%p) init spin lock failed, err:%d", worker, ret);
goto err_destroy_cq;
}
ret = pthread_spin_init(&(worker->nd_map_lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("worker(%p) init spin nd map lock failed, err:%d", worker, ret);
goto err_destroy_workerlock;
}
worker->nd_map = hashmap_create();
worker->closing_nd_map = hashmap_create();
//list_head_init(&worker->conn_list);
worker->conn_list = InitQueue();
if (worker->conn_list == NULL) {
log_error("worker(%p) init conn list failed", worker);
goto err_destroy_map;
}
worker->w_pid = 0;
pthread_create(&worker->cq_poller_thread, NULL, cb, worker);
sprintf(str, "cq_worker:%d", index);
pthread_setname_np(worker->cq_poller_thread, str);
__CPU_ZERO_S(sizeof(cpu_set_t), &cpuset);
__CPU_SET_S(index, sizeof(cpu_set_t), &cpuset);
pthread_setaffinity_np(worker->cq_poller_thread, sizeof(cpu_set_t), &cpuset);
return C_OK;
err_destroy_map:
hashmap_destroy(worker->closing_nd_map);
hashmap_destroy(worker->nd_map);
pthread_spin_destroy(&worker->nd_map_lock);
err_destroy_workerlock:
pthread_spin_destroy(&worker->lock);
err_destroy_cq:
ibv_destroy_cq(worker->cq);
err_destroy_compchannel:
ibv_destroy_comp_channel(worker->comp_channel);
return C_ERR;
}
void destroy_worker(worker *worker) {
worker->close = 1;
pthread_join(worker->cq_poller_thread, NULL);
worker->w_pid = 0;
if (worker->conn_list != NULL) {
DestroyQueue(worker->conn_list);
worker->conn_list = NULL;
}
if (worker->closing_nd_map != NULL) {
hashmap_destroy(worker->closing_nd_map);
worker->closing_nd_map = NULL;
}
if (worker->nd_map != NULL) {
hashmap_destroy(worker->nd_map);
worker->nd_map = NULL;
}
pthread_spin_destroy(&worker->nd_map_lock);
pthread_spin_destroy(&worker->lock);
if (worker->cq != NULL) {
log_debug("worker(%p) ibv_destroy_cq:%p", worker, worker->cq);
ibv_destroy_cq(worker->cq);
worker->cq = NULL;
}
if(worker->comp_channel != NULL) {
log_debug("worker(%p) ibv_destroy_comp_channel:%p", worker, worker->comp_channel);
ibv_destroy_comp_channel(worker->comp_channel);
worker->comp_channel = NULL;
}
worker->pd = NULL;
}
void destroy_rdma_env() {
if (g_net_env != NULL) {
for (int i = 0; i < g_net_env->worker_num; i++) {
destroy_worker(g_net_env->worker + i);
}
if (g_net_env->event_channel != NULL) {
rdma_destroy_event_channel(g_net_env->event_channel);
g_net_env->event_channel = NULL;
}
g_net_env->close = 1;
pthread_join(g_net_env->cm_event_loop_thread, NULL);
if (g_net_env->all_devs != NULL) {
rdma_free_devices(g_net_env->all_devs);
g_net_env->all_devs = NULL;
}
pthread_spin_destroy(&g_net_env->server_lock);
hashmap_destroy(g_net_env->server_map);
free(g_net_env);
g_net_env = NULL;
}
if (rdma_pool != NULL) {
if(rdma_pool->memory_pool != NULL) {
close_memory_pool(rdma_pool->memory_pool);
}
free(rdma_pool);
}
if (rdma_env_config != NULL) {
free(rdma_env_config->rdma_log_dir);
free(rdma_env_config);
}
if (debug_fp != NULL) {
fclose(debug_fp);
}
if (error_fp != NULL) {
fclose(error_fp);
}
}
int init_rdma_env(struct rdma_env_config* config) {
if(config == NULL) {
return C_ERR;
}
rdma_env_config = config;
if (rdma_env_config->enable_rdma_log == 1) {
log_set_level(0);
log_set_quiet(0);
char* debug_name = "rdma_debug.log";
char* error_name = "rdma_error.log";
char* debug_path = (char*)malloc(strlen(rdma_env_config->rdma_log_dir) + strlen(debug_name));
char* error_path = (char*)malloc(strlen(rdma_env_config->rdma_log_dir) + strlen(error_name));
strcpy(debug_path, rdma_env_config->rdma_log_dir);
strcat(debug_path, debug_name);
strcpy(error_path, rdma_env_config->rdma_log_dir);
strcat(error_path, error_name);
debug_fp = fopen(debug_path, "ab");
if (debug_fp == NULL) {
goto err_free_config;
}
log_add_fp(debug_fp, LOG_DEBUG);
error_fp = fopen(error_path, "ab");
if (error_fp == NULL) {
goto err_close_debug_fp;
}
log_add_fp(error_fp, LOG_ERROR);
} else {
log_set_quiet(1);
}
int len = sizeof(struct net_env_st) + config->worker_num * sizeof(worker);
g_net_env = (struct net_env_st*)malloc(len);
if (g_net_env == NULL) {
log_error("init env failed: no enough memory");
goto err_close_error_fp;
}
g_net_env->worker_num = config->worker_num;//32
g_net_env->server_map = hashmap_create();
if (pthread_spin_init(&(g_net_env->server_lock), PTHREAD_PROCESS_SHARED) != 0) {
log_error("init g_net_env->server_lock spin lock failed");
goto err_free_gnetenv;
}
g_net_env->all_devs = rdma_get_devices(&g_net_env->ib_dev_cnt);
if (g_net_env->all_devs == NULL) {
log_error("init env failed: get rdma devices failed");
goto err_destroy_spinlock;
}
log_debug("rdma_get_devices find ib_dev_cnt:%d", g_net_env->ib_dev_cnt);
if (g_net_env->ib_dev_cnt > 0) {
g_net_env->ctx = g_net_env->all_devs[0];
} else {
log_error("can not find rdma dev");
goto err_free_devices;
}
g_net_env->event_channel = rdma_create_event_channel();
g_net_env->pd = ibv_alloc_pd(g_net_env->ctx);
if (g_net_env->pd == NULL) {
log_error("alloc pd failed, errno:%d", errno);
goto err_destroy_eventchannel;
}
//log_debug("g net env alloc pd:%p",g_net_env->pd);
pthread_create(&g_net_env->cm_event_loop_thread, NULL, cm_thread, g_net_env);
pthread_setname_np(g_net_env->cm_event_loop_thread, "cm_worker");
int index;
for (index = 0; index < g_net_env->worker_num; index++) {
log_debug("init worker(%d-%p)", index, g_net_env->worker + index);
g_net_env->worker[index].id = index;
if(init_worker(g_net_env->worker + index, cq_thread, index) == C_ERR) {
log_error("init env failed: init worker(%d-%p) failed", index, g_net_env->worker + index);
goto err_destroy_worker;
}
}
WQ_DEPTH = rdma_env_config->wq_depth;
MIN_CQE_NUM = rdma_env_config->min_cqe_num;
CONN_DATA_SIZE = rdma_env_config->conn_data_size;
rdma_pool = (struct rdma_pool*)malloc(sizeof(struct rdma_pool));
if (rdma_pool == NULL) {
log_error("malloc rdma pool failed");
goto err_destroy_worker;
}
memset(rdma_pool, 0, sizeof(struct rdma_pool));
rdma_pool->memory_pool = init_memory_pool(rdma_env_config->mem_block_num, rdma_env_config->mem_block_size, rdma_env_config->mem_pool_level, g_net_env->pd);
if(rdma_pool->memory_pool == NULL) {
log_error("init rdma memory pool failed");
goto err_free_rdmapool;
}
return C_OK;
err_free_rdmapool:
free(rdma_pool);
err_destroy_worker:
ibv_dealloc_pd(g_net_env->pd);
for (int i = 0; i < index; i++) {
destroy_worker(g_net_env->worker + i);
}
err_destroy_eventchannel:
rdma_destroy_event_channel(g_net_env->event_channel);
err_free_devices:
rdma_free_devices(g_net_env->all_devs);
err_destroy_spinlock:
pthread_spin_destroy(&g_net_env->server_lock);
err_free_gnetenv:
hashmap_destroy(g_net_env->server_map);
free(g_net_env);
err_close_error_fp:
fclose(error_fp);
err_close_debug_fp:
fclose(debug_fp);
err_free_config:
free(rdma_env_config);
return C_ERR;
}
void conn_add_ref(connection* conn) {
pthread_spin_lock(&conn->spin_lock);
int old_ref = conn->ref;
if (conn->ref > 0) {
conn->ref++;
};
pthread_spin_unlock(&conn->spin_lock);
log_debug("conn(%lu-%p) ref: %d-->%d", conn->nd, conn, old_ref, conn->ref);
return;
}
void conn_del_ref(connection* conn) {
pthread_spin_lock(&conn->spin_lock);
int old_ref = conn->ref;
if (conn->ref > 0) {
conn->ref--;
};
pthread_spin_unlock(&conn->spin_lock);
log_debug("conn(%lu-%p) ref: %d-->%d", conn->nd, conn, old_ref, conn->ref);
return;
}
void set_conn_state(connection* conn, int state) {
pthread_spin_lock(&conn->spin_lock);
int old_state = conn->state;
conn->state = state;
pthread_spin_unlock(&conn->spin_lock);
log_debug("conn(%lu-%p) state: %d-->%d", conn->nd, conn, old_state, state);
return;
}
int get_conn_state(connection* conn) {
pthread_spin_lock(&conn->spin_lock);
int state = conn->state;
pthread_spin_unlock(&conn->spin_lock);
return state;
}
worker* get_worker_by_nd(uint64_t nd) {
int worker_id = ((nd) >>32) % g_net_env->worker_num;//CONN_ID_BIT_LEN
log_debug("get worker by nd: worker_id:%d",worker_id);
return g_net_env->worker + worker_id;
}
int add_conn_to_worker(connection * conn, worker * worker, khash_t(map) *hmap) {
int ret = 0;
pthread_spin_lock(&worker->nd_map_lock);
ret = hashmap_put(hmap, conn->nd, (uint64_t)conn);
pthread_spin_unlock(&worker->nd_map_lock);
log_debug("add conn(%p nd:%d) from worker(%p) nd_map(%p)",conn,conn->nd,worker,worker->nd_map);
return ret >= 0;
}
int del_conn_from_worker(uint64_t nd, worker * worker, khash_t(map) *hmap) {
int ret = 0;
pthread_spin_lock(&worker->nd_map_lock);
ret = hashmap_del(hmap, nd);
pthread_spin_unlock(&worker->nd_map_lock);
log_debug("del conn(nd:%d) from worker(%p) nd_map(%p)",nd,worker,worker->nd_map);
return ret >= 0;
}
void get_worker_and_connect_by_nd(uint64_t nd, worker ** worker, connection** conn) {
*worker = get_worker_by_nd(nd);
pthread_spin_lock(&(*worker)->nd_map_lock);
*conn = (connection*)hashmap_get((*worker)->nd_map, nd);
pthread_spin_unlock(&(*worker)->nd_map_lock);
}
int add_server_to_env(struct rdma_listener *server, khash_t(map) *hmap) {
int ret = 0;
pthread_spin_lock(&g_net_env->server_lock);
ret = hashmap_put(hmap, server->nd, (uint64_t)server);
pthread_spin_unlock(&g_net_env->server_lock);
return ret >= 0;
}
int del_server_from_env(struct rdma_listener *server) {
int ret = 0;
pthread_spin_lock(&g_net_env->server_lock);
ret = hashmap_del(g_net_env->server_map, server->nd);
pthread_spin_unlock(&g_net_env->server_lock);
return ret >= 0;
}
inline int open_event_fd() {
return eventfd(0, EFD_SEMAPHORE);
}
inline int wait_event(int fd) {
uint64_t value = 0;
return read(fd, &value, 8);
}
inline int notify_event(int fd, int flag) {
if (flag == 0) {
uint64_t value = 1;
return write(fd, &value, 8);
} else {
close(fd);
return 0;
}
}

View File

@ -0,0 +1,280 @@
#ifndef RDMA_PROTO_H
#define RDMA_PROTO_H
#define _GNU_SOURCE
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>
#include <stdbool.h>
#include <netdb.h>
#include <sys/eventfd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include "memory_pool.h"
#include "hashmap.h"
#include "queue.h"
#include "log.h"
#define C_OK 0
#define C_ERR 1
#define RDMA_INVALID_OPCODE 0xffff
#define RDMA_MAX_WQE 1024
#define SERVER_MAX_CONN 32
#define CONN_ID_BIT_LEN 32
#define WORKER_ID_BIT_LEN 8
#define CONN_TYPE_BIT_LEN 8
#define CONN_ID_MASK_BIT_LEN 8
static const int TIMEOUT_IN_MS = 500;
typedef void *event_callback(void *ctx);
void *cm_thread(void *ctx);
void *cq_thread(void *ctx);
extern int WQ_DEPTH;
extern int WQ_SG_DEPTH;
extern int MIN_CQE_NUM;
extern int CONN_DATA_SIZE;
extern struct rdma_pool *rdma_pool;
extern struct rdma_env_config *rdma_env_config;
extern FILE *fp;
extern struct net_env_st *g_net_env;
struct rdma_pool {
memory_pool *memory_pool;
};
struct rdma_env_config {
int mem_block_num;
int mem_block_size;
int mem_pool_level;
int conn_data_size;
int wq_depth;
int min_cqe_num;
int enable_rdma_log;
char* rdma_log_dir;
int worker_num;
};
typedef enum conn_type_bit_mask {
CONN_SERVER_BIT = 1 << 7,
CONN_ACTIVE_BIT = 1 << 6,
} conn_type_mask;
typedef enum id_gen_enum {
ID_GEN_CTRL,
ID_GEN_DATA,
ID_GEN_MAX
} IdGenEnum;
struct conn_nd_t {
uint64_t id:32;//CONN_ID_BIT_LEN; //32
uint64_t worker_id:8;//WORKER_ID_BIT_LEN; //8
uint64_t type:8;//CONN_TYPE_BIT_LEN; //8 RDMA/TCP Server/Conn ACtive/Passive
uint64_t m2:8;//CONN_ID_MASK_BIT_LEN; //8 b
uint64_t m1:8;//CONN_ID_MASK_BIT_LEN; //8 c
};
union conn_nd_union {
struct conn_nd_t nd_;
uint64_t nd;
};
typedef struct worker {
struct ibv_pd *pd;
struct ibv_cq *cq;
struct ibv_comp_channel *comp_channel;
pthread_t cq_poller_thread;
pthread_spinlock_t nd_map_lock;
khash_t(map) *nd_map;
khash_t(map) *closing_nd_map; //TODO
pthread_spinlock_t lock; //TODO
Queue *conn_list; //TODO
uint8_t id;
uint32_t qp_cnt;
pthread_t w_pid;
int close;
} worker;
struct net_env_st {
uint8_t worker_num;
int8_t pad[6];
struct ibv_context **all_devs;
struct ibv_context *ctx;
struct ibv_pd *pd;
struct rdma_event_channel *event_channel;
pthread_t cm_event_loop_thread;
pthread_spinlock_t lock;
uint32_t server_cnt;
int32_t ib_dev_cnt;
pthread_spinlock_t server_lock;
khash_t(map) *server_map;
uint32_t id_gen[ID_GEN_MAX];
int close;
worker worker[];
};
typedef struct rdma_memory {
uint16_t opcode;
uint8_t rsvd[14];
uint64_t addr;
uint32_t length;
uint32_t key;
} rdma_memory;
typedef struct rdma_full_msg {
uint16_t opcode;
uint8_t rsvd[26];
uint32_t tx_full_offset;
} rdma_full_msg;
typedef union rdma_ctl_cmd {
rdma_memory memory;
rdma_full_msg full_msg;
} rdma_ctl_cmd;
typedef enum rdma_opcode {
EXCHANGE_MEMORY = 0,
NOTIFY_FULLBUF = 1,
} rdma_opcode;
typedef struct data_buf {
struct ibv_mr *mr;
char *addr;
uint32_t length;
uint32_t offset;
uint32_t pos;
} data_buf;
typedef struct cmd_entry {
rdma_ctl_cmd *cmd;
uint64_t nd;
} cmd_entry;
typedef struct data_entry {
char *addr;
char *remote_addr;
uint32_t data_len;
uint32_t mem_len;
} data_entry;
typedef enum connection_state {
CONN_STATE_NONE = 0,
CONN_STATE_CONNECTING,
CONN_STATE_CONNECTED,
CONN_STATE_ERROR,
CONN_STATE_DISCONNECTING,
CONN_STATE_DISCONNECTED
} connection_state;
typedef enum connection_type {
CONN_TYPE_SERVER = 1,
CONN_TYPE_CLIENT
} connection_type;
typedef struct connection {
uint64_t nd;
char local_addr[INET_ADDRSTRLEN + NI_MAXSERV];
char remote_addr[INET_ADDRSTRLEN + NI_MAXSERV];
int conn_type;
struct rdma_cm_id * cm_id;
struct ibv_qp *qp;
struct ibv_mr *mr;
//TX
data_buf *tx;
char *remote_rx_addr;
uint32_t remote_rx_key;
uint32_t remote_rx_length;
uint32_t remote_rx_offset;
//RX
data_buf *rx;
//control_buff
rdma_ctl_cmd *ctl_buf;
struct ibv_mr *ctl_buf_mr;
uint32_t tx_full_offset;
uint32_t rx_full_offset;
Queue *free_list;
Queue *msg_list;
pthread_spinlock_t free_list_lock;
pthread_spinlock_t msg_list_lock;
void* context;
void* conn_context;
connection_state state;
int connect_fd;
int msg_fd;
int close_fd;
pthread_spinlock_t spin_lock;
int64_t send_timeout_ns;
int64_t recv_timeout_ns;
worker *worker;
int ref;
} connection;
struct rdma_listener {
uint64_t nd;
struct rdma_cm_id *listen_id;
char* ip;
char* port;
pthread_spinlock_t conn_lock;
khash_t(map) *conn_map;
pthread_spinlock_t wait_conns_lock;
Queue *wait_conns;
int connect_fd;//sem_t*
};
uint64_t allocate_nd(int type);
void cbrdma_parse_nd(uint64_t nd, int *id, int * worker_id, int * is_server, int * is_active);
struct rdma_env_config* get_rdma_env_config();
int init_worker(worker *worker, event_callback cb, int index);
void destroy_worker(worker *worker);
void destroy_rdma_env();
int init_rdma_env(struct rdma_env_config* config);
void conn_add_ref(connection* conn);
void conn_del_ref(connection* conn);
void set_conn_state(connection* conn, int state);
int get_conn_state(connection* conn);
worker* get_worker_by_nd(uint64_t nd);
int add_conn_to_worker(connection * conn, worker * worker, khash_t(map) *hmap);
int del_conn_from_worker(uint64_t nd, worker * worker, khash_t(map) *hmap);
void get_worker_and_connect_by_nd(uint64_t nd, worker ** worker, connection** conn);
int add_server_to_env(struct rdma_listener *server, khash_t(map) *hmap);
int del_server_from_env(struct rdma_listener *server);
int open_event_fd();
int wait_event(int fd);
int notify_event(int fd, int flag);
#endif

View File

@ -0,0 +1,114 @@
#include "server.h"
connection* get_rdma_server_conn(struct rdma_listener *server) {
wait_event(server->connect_fd);
connection *conn;
pthread_spin_lock(&(server->wait_conns_lock));
DeQueue(server->wait_conns, (Item*)&conn);
pthread_spin_unlock(&(server->wait_conns_lock));
if(conn == NULL) {
log_error("server(%lu-%p) get conn failed: conn is null", server->nd, server);
return NULL;
}
return conn;
}
struct rdma_listener* start_rdma_server_by_addr(char* ip, char* port) {
struct rdma_listener* server = (struct rdma_listener*)malloc(sizeof(struct rdma_listener));
if (server == NULL) {
log_error("create server failed: malloc failed");
return NULL;
}
server->nd = allocate_nd(CONN_SERVER_BIT);
server->ip = ip;
server->port = port;
server->connect_fd = -1;
int ret = pthread_spin_init(&(server->conn_lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("server(%lu-%p) init conn lock failed, err:%d", server->nd, server, ret);
goto err_free;
}
ret = pthread_spin_init(&(server->wait_conns_lock), PTHREAD_PROCESS_SHARED);
if (ret != 0) {
log_error("server(%lu-%p) init wait conns list lock failed, err:%d", server->nd, server, ret);
goto err_destroy_spin_lock;
}
server->connect_fd = open_event_fd();
if (server->connect_fd < 0) {
log_error("server(%lu-%p) open event fd failed", server->nd, server);
goto err_destroy_wait_conns_lock;
}
server->conn_map = hashmap_create();
if (server->conn_map == NULL) {
log_error("server(%lu-%p) create conn map failed", server->nd, server);
goto err_destroy_fd;
}
server->wait_conns = InitQueue();
if (server->wait_conns == NULL) {
log_error("server(%lu-%p) init wait conns queue failed", server->nd, server);
goto err_destroy_map;
}
struct rdma_addrinfo hints, *res;
memset(&hints, 0, sizeof hints);
hints.ai_flags = RAI_PASSIVE;
hints.ai_port_space = RDMA_PS_TCP;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port));
addr.sin_addr.s_addr = inet_addr(ip);
ret = rdma_create_id(g_net_env->event_channel, &server->listen_id, server, RDMA_PS_TCP);
if (ret != 0) {
log_error("server(%lu-%p) create id failed, errno:%d", server->nd, server, errno);
goto err_destroy_queue;
}
log_debug("server(%lu-%p): listen_id:%p", server->nd, server, server->listen_id);
ret = rdma_bind_addr(server->listen_id, (struct sockaddr *)&addr);
if (ret != 0) {
log_error("server(%lu-%p) bind addr failed, errno:%d", server->nd, server, errno);
goto err_destroy_id;
}
ret = rdma_listen(server->listen_id, 10);
if (ret != 0) {
log_error("server(%lu-%p) listen failed, errno:%d", server->nd, server, errno);
goto err_destroy_id;
}
add_server_to_env(server, g_net_env->server_map);
return server;
err_destroy_id:
rdma_destroy_id(server->listen_id);
err_destroy_queue:
DestroyQueue(server->wait_conns);
err_destroy_map:
hashmap_destroy(server->conn_map);
err_destroy_fd:
notify_event(server->connect_fd,1);
server->connect_fd = -1;
err_destroy_wait_conns_lock:
pthread_spin_destroy(&server->wait_conns_lock);
err_destroy_spin_lock:
pthread_spin_destroy(&server->conn_lock);
err_free:
free(server);
return NULL;
}
void close_rdma_server(struct rdma_listener* server) {
if (server != NULL) {
del_server_from_env(server);
notify_event(server->connect_fd,1);
server->connect_fd = -1;
DestroyQueue(server->wait_conns);
hashmap_destroy(server->conn_map);
pthread_spin_destroy(&server->conn_lock);
pthread_spin_destroy(&server->wait_conns_lock);
if (server->listen_id != NULL) {
rdma_destroy_id(server->listen_id);
}
free(server);
return;
}
}

View File

@ -0,0 +1,20 @@
#ifndef SERVER_H
#define SERVER_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "rdma.h"
connection* get_rdma_server_conn(struct rdma_listener *server);
struct rdma_listener* start_rdma_server_by_addr(char* ip, char* port);
void close_rdma_server(struct rdma_listener* server);
#endif

View File

@ -0,0 +1,236 @@
#include "transfer_event.h"
#include "connection.h"
int process_recv_event(connection *conn, cmd_entry *entry) {
rdma_ctl_cmd *cmd = entry->cmd;
switch (ntohs(cmd->memory.opcode)) {
case EXCHANGE_MEMORY:
conn->remote_rx_addr = (char*)ntohu64(cmd->memory.addr);
conn->tx->length = ntohl(cmd->memory.length);
conn->remote_rx_key = ntohl(cmd->memory.key);
conn->tx->offset = 0;
conn->tx_full_offset = 0;
rdma_adjust_txBuf(conn, conn->tx->length);//TODO error handle
int state = get_conn_state(conn);
if(state == CONN_STATE_CONNECTING) {//first time to exchange memory
set_conn_state(conn, CONN_STATE_CONNECTED);
if (conn->conn_type == CONN_TYPE_SERVER) {
struct rdma_listener *server = (struct rdma_listener*)conn->context;
pthread_spin_lock(&(server->wait_conns_lock));
if(EnQueue(server->wait_conns,conn) == NULL) {
pthread_spin_unlock(&(server->wait_conns_lock));
log_error("server(%lu-%p) wait conns has no more memory can be malloced", server->nd, server);
return C_ERR;
}
pthread_spin_unlock(&(server->wait_conns_lock));
notify_event(server->connect_fd, 0);
} else {
notify_event(conn->connect_fd, 0);
}
}
break;
case NOTIFY_FULLBUF:
conn->rx_full_offset = ntohl(cmd->full_msg.tx_full_offset);
if (conn->rx->pos == conn->rx->offset && conn->rx_full_offset == conn->rx->pos) {
int ret = rdma_exchange_rx(conn);//TODO error handler
if (ret == C_ERR) {
log_error("conn(%lu-%p) process recv event failed: exchange rx return error");
return C_ERR;
}
}
break;
default:
log_error("unknown cmd");
return C_ERR;
}
return conn_rdma_post_recv(conn, cmd);
}
int process_send_event(connection *conn, cmd_entry *entry) {
rdma_ctl_cmd *cmd = entry->cmd;
memset(cmd, 0x00, sizeof(rdma_ctl_cmd));
int ret = release_cmd_buffer(conn, entry->cmd);
return ret;
}
int process_write_event(connection *conn) {
return C_OK;
}
int process_recv_imm_event(connection *conn, cmd_entry *entry, uint32_t offset_add, uint32_t byte_len) {
rdma_ctl_cmd *cmd = entry->cmd;
//pthread_spin_lock(&conn->spin_lock);
if (conn->rx->offset + offset_add > conn->rx->length) {
conn->rx->offset = 0;
}
conn->rx->offset += offset_add;
//assert(offset_add + conn->rx->offset <= conn->rx->length);
//conn->rx->offset += offset_add;
log_debug("conn(%lu-%p) rx start(%d) end(%d)", conn->nd, conn, conn->rx->offset - offset_add, conn->rx->offset);
data_entry *msg = (data_entry*)malloc(sizeof(data_entry));
if (msg == NULL) {
//pthread_spin_unlock(&conn->spin_lock);
log_error("conn(%lu-%p) malloc data entry failed", conn->nd, conn);
return C_ERR;
}
msg->addr = conn->rx->addr + conn->rx->offset - offset_add;
msg->data_len = byte_len;
msg->mem_len = offset_add;
//pthread_spin_unlock(&conn->spin_lock);
pthread_spin_lock(&(conn->msg_list_lock));
if (EnQueue(conn->msg_list, msg) == NULL) {
pthread_spin_unlock(&(conn->msg_list_lock));
log_error("conn(%lu-%p) msg list enQueue failed, no more memory can be malloced", conn->nd, conn);
return C_ERR;
}
pthread_spin_unlock(&(conn->msg_list_lock));
log_debug("conn(%lu-%p) msg list enQueue(%p) msg(%p) success, wait msg size: %d", conn->nd, conn, conn->msg_list, msg, GetSize(conn->msg_list));
//notify_event(conn->msg_fd, 0);
return conn_rdma_post_recv(conn, cmd);
}
void process_cq_event(struct ibv_wc *wcs, int num, worker *worker) {
struct ibv_wc *wc = NULL;
connection *conn = NULL;
cmd_entry *entry = NULL;
uint64_t nd;
int ret;
//log_debug("process cq event: num(%d)",num);
for (int i = 0; i < num; i++) {
wc = wcs + i;
if(wc->opcode == IBV_WC_RDMA_WRITE) {
nd = wc->wr_id;
} else { //send and recv
entry = (cmd_entry*)wc->wr_id;
nd = entry->nd;
}
conn = (connection*)hashmap_get((worker)->nd_map, nd);
if (conn == NULL) {
continue;
}
int state = get_conn_state(conn);
if (state != CONN_STATE_CONNECTED && state != CONN_STATE_CONNECTING) { //在使用之前需要判断连接的状态
//log_error("conn(%lu-%p) process cq event failed: conn state is not connected: state(%d)", conn->nd, conn, state);
//already closed, no need deal
continue;
}
if (wc->status != IBV_WC_SUCCESS) {
log_error("conn:(%lu-%p) failed: %d %d %d %s", conn->nd, conn, wc->opcode, wc->byte_len, wc->status, ibv_wc_status_str(wc->status));
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
continue;
}
log_debug("op code:%d, status:%d, %d", wc->opcode, wc->status, wc->byte_len);
switch (wc->opcode) {
case IBV_WC_RECV: //128
ret = process_recv_event(conn, entry);
if (ret == C_ERR) {
log_error("process recv event failed");
if (state == CONN_STATE_CONNECTED) {
set_conn_state(conn, CONN_STATE_ERROR);
}
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
}
free(entry);
break;
case IBV_WC_SEND:
ret = process_send_event(conn, entry);
if (ret == C_ERR) {
log_error("process send event failed");
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
}
free(entry);
break;
case IBV_WC_RDMA_WRITE:
ret = process_write_event(conn);
if (ret == C_ERR) {
log_error("process write event failed");
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
}
break;
case IBV_WC_RECV_RDMA_WITH_IMM:
ret = process_recv_imm_event(conn, entry, ntohl(wc->imm_data), wc->byte_len);
if (ret == C_ERR) {
log_error("process recv imm event failed");
set_conn_state(conn, CONN_STATE_ERROR);
rdma_disconnect(conn->cm_id);
//conn_disconnect(conn);
//del_conn_from_worker(conn->nd, worker, worker->nd_map);
//add_conn_to_worker(conn, worker, worker->closing_nd_map);
}
free(entry);
break;
case IBV_WC_RDMA_READ:
default:
log_error("not support wc->opcode:%d", wc->opcode);
break;
}
}
}
void *cq_thread(void *ctx) {
struct worker *worker = (struct worker*)ctx;
int ret = 0;
struct ibv_wc wcs[32];
struct ibv_cq *ev_cq;
void *ev_ctx;
if (worker->w_pid == 0) {
worker->w_pid = pthread_self();
}
memset(wcs, 0 , 32 * sizeof(struct ibv_wc));
while(1) {
if (worker->close == 1) {
goto exit;
}
/*
//log_debug("cq_thread: work comp channel:%p", worker->comp_channel);
ret = ibv_get_cq_event(worker->comp_channel, &ev_cq, &ev_ctx);
if(ret != 0) {
log_debug("ibv get cq event error\n");
goto error;
}
log_debug("ibv_get_cq_event success");
//ibv_ack_cq_events(worker->cq, 1);
ret = ibv_req_notify_cq(worker->cq, 0);
if (ret != 0) {
log_debug("ibv req notify cq error\n");
goto error;
}
*/
ret = ibv_poll_cq(worker->cq, 32, wcs);
if (ret < 0) {
log_error("ibv poll cq failed: %d", ret);
goto error;
}
process_cq_event(wcs, ret, worker);
//ibv_ack_cq_events(worker->cq, ret);
//log_debug("process cq event finish");
}
error:
//TODO
exit:
pthread_exit(NULL);
}

View File

@ -0,0 +1,27 @@
#ifndef TRANSFER_EVENT_H
#define TRANSFER_EVENT_H
#include <stdio.h>
#include <string.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>
#include <arpa/inet.h>
#include "rdma_proto.h"
#include "log.h"
#include "connection.h"
int process_recv_event(connection *conn, cmd_entry *entry);
int process_send_event(connection *conn, cmd_entry *entry);
int process_write_event(connection *conn);
int process_recv_imm_event(connection *conn, cmd_entry *entry, uint32_t offset_add, uint32_t byte_len);
void process_cq_event(struct ibv_wc *wcs, int num, worker *worker);
extern void *cq_thread(void *ctx);
#endif

Binary file not shown.

View File

@ -0,0 +1,115 @@
package main
import (
"net"
"rdma_test/common"
"rdma_test/rdma"
"time"
)
var exit = false
var Config = &rdma.RdmaEnvConfig{}
func ReadBytes(conn net.Conn, buf []byte) error {
offset := 0
for offset < len(buf) {
n, err := conn.Read(buf[offset:])
if n == -1 || (err != nil) {
println("ReadBytes failed, err: ", err)
return err
}
offset += n
}
return nil
}
func testRdma() {
if err := rdma.InitPool(Config); err != nil {
println(err.Error())
return
}
server, _ := rdma.NewRdmaServer(common.GParam.Ip, common.GParam.Port)
defer server.Close()
for {
conn,_ := server.Accept()
go func() {
for !exit {
beginTm := time.Now()
request := common.NewPacket()
if err := request.ReadFromRDMAConnFromCli(conn, -1); err != nil {
println(err.Error())
break
}
if request.Opcode == common.OpWrite {
if err := conn.ReleaseConnRxDataBuffer(request.RdmaBuffer); err != nil {
println(err.Error())
break
}
request.PacketOkReply()
}
if err := request.SendRespToRDMAConn(conn); err != nil {
println(err.Error())
break
}
common.Stat().AddSumTime(common.GParam.IoSize, time.Now().UnixNano() / 1000 - beginTm.UnixNano() / 1000)
}
conn.Close()
}()
}
}
func testTcp() {
common.InitBufferPool(0)
server, _ := net.Listen("tcp", common.GParam.Ip + ":" + common.GParam.Port)
defer server.Close()
for {
c, _ := server.Accept()
conn, _ := c.(*net.TCPConn)
conn.SetKeepAlive(true)
conn.SetNoDelay(true)
go func() {
for !exit {
beginTm := time.Now()
request := common.NewPacket()
if err := request.ReadFromTCPConnFromCli(conn, -1); err != nil {
println(err.Error())
break
}
if request.Opcode == common.OpWrite {
request.PacketOkReply()
}
if err := request.WriteToConn(conn); err != nil {
println(err.Error())
break
}
common.Stat().AddSumTime(common.GParam.IoSize, time.Now().UnixNano() / 1000 - beginTm.UnixNano() / 1000)
}
}()
}
}
func main() {
common.ParseParam()
if common.GParam.Protocol == "rdma" {
go testRdma()
} else {
go testTcp()
}
for !exit {
time.Sleep(time.Millisecond * 1000)
common.Stat().Print()
}
}