mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
feat(rdma): Add rdma bench mark
Signed-off-by: Wu Huocheng <wuhuocheng@oppo.com>
This commit is contained in:
parent
49233e0a35
commit
65726f617b
67
util/rdma/rdma_benchmark/README.txt
Normal file
67
util/rdma/rdma_benchmark/README.txt
Normal 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]
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
BIN
util/rdma/rdma_benchmark/client/client
Normal file
BIN
util/rdma/rdma_benchmark/client/client
Normal file
Binary file not shown.
126
util/rdma/rdma_benchmark/client/client.go
Normal file
126
util/rdma/rdma_benchmark/client/client.go
Normal 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()
|
||||
}
|
||||
}
|
||||
|
||||
0
util/rdma/rdma_benchmark/client/output.log
Normal file
0
util/rdma/rdma_benchmark/client/output.log
Normal file
178
util/rdma/rdma_benchmark/common/buffer_pool.go
Normal file
178
util/rdma/rdma_benchmark/common/buffer_pool.go
Normal 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
|
||||
}
|
||||
56
util/rdma/rdma_benchmark/common/context/context.go
Normal file
56
util/rdma/rdma_benchmark/common/context/context.go
Normal 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
|
||||
}
|
||||
73
util/rdma/rdma_benchmark/common/context/go17.go
Normal file
73
util/rdma/rdma_benchmark/common/context/go17.go
Normal 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)
|
||||
}
|
||||
21
util/rdma/rdma_benchmark/common/context/go19.go
Normal file
21
util/rdma/rdma_benchmark/common/context/go19.go
Normal 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
|
||||
301
util/rdma/rdma_benchmark/common/context/pre_go17.go
Normal file
301
util/rdma/rdma_benchmark/common/context/pre_go17.go
Normal 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)
|
||||
}
|
||||
110
util/rdma/rdma_benchmark/common/context/pre_go19.go
Normal file
110
util/rdma/rdma_benchmark/common/context/pre_go19.go
Normal 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()
|
||||
379
util/rdma/rdma_benchmark/common/packet.go
Normal file
379
util/rdma/rdma_benchmark/common/packet.go
Normal 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
|
||||
}
|
||||
30
util/rdma/rdma_benchmark/common/param.go
Normal file
30
util/rdma/rdma_benchmark/common/param.go
Normal 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)
|
||||
}
|
||||
40
util/rdma/rdma_benchmark/common/perf_stat.go
Normal file
40
util/rdma/rdma_benchmark/common/perf_stat.go
Normal 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))
|
||||
}
|
||||
|
||||
|
||||
405
util/rdma/rdma_benchmark/common/rate/rate.go
Normal file
405
util/rdma/rdma_benchmark/common/rate/rate.go
Normal 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 Reservation’s 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)
|
||||
}
|
||||
3
util/rdma/rdma_benchmark/go.mod
Normal file
3
util/rdma/rdma_benchmark/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module "rdma_test"
|
||||
|
||||
go 1.17
|
||||
247
util/rdma/rdma_benchmark/rdma/buddy.c
Normal file
247
util/rdma/rdma_benchmark/rdma/buddy.c
Normal 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");
|
||||
}
|
||||
14
util/rdma/rdma_benchmark/rdma/buddy.h
Normal file
14
util/rdma/rdma_benchmark/rdma/buddy.h
Normal 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
|
||||
|
||||
44
util/rdma/rdma_benchmark/rdma/client.c
Normal file
44
util/rdma/rdma_benchmark/rdma/client.c
Normal 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;
|
||||
}
|
||||
15
util/rdma/rdma_benchmark/rdma/client.h
Normal file
15
util/rdma/rdma_benchmark/rdma/client.h
Normal 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
|
||||
852
util/rdma/rdma_benchmark/rdma/connection.c
Normal file
852
util/rdma/rdma_benchmark/rdma/connection.c
Normal 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;
|
||||
}
|
||||
86
util/rdma/rdma_benchmark/rdma/connection.h
Normal file
86
util/rdma/rdma_benchmark/rdma/connection.h
Normal 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
|
||||
274
util/rdma/rdma_benchmark/rdma/connection_event.c
Normal file
274
util/rdma/rdma_benchmark/rdma/connection_event.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
31
util/rdma/rdma_benchmark/rdma/connection_event.h
Normal file
31
util/rdma/rdma_benchmark/rdma/connection_event.h
Normal 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
|
||||
54
util/rdma/rdma_benchmark/rdma/hashmap.c
Normal file
54
util/rdma/rdma_benchmark/rdma/hashmap.c
Normal 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;
|
||||
}
|
||||
14
util/rdma/rdma_benchmark/rdma/hashmap.h
Normal file
14
util/rdma/rdma_benchmark/rdma/hashmap.h
Normal 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
|
||||
708
util/rdma/rdma_benchmark/rdma/khash.h
Normal file
708
util/rdma/rdma_benchmark/rdma/khash.h
Normal 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 */
|
||||
176
util/rdma/rdma_benchmark/rdma/log.c
Normal file
176
util/rdma/rdma_benchmark/rdma/log.c
Normal 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();
|
||||
}
|
||||
51
util/rdma/rdma_benchmark/rdma/log.h
Normal file
51
util/rdma/rdma_benchmark/rdma/log.h
Normal 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
|
||||
61
util/rdma/rdma_benchmark/rdma/memory_pool.c
Normal file
61
util/rdma/rdma_benchmark/rdma/memory_pool.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
29
util/rdma/rdma_benchmark/rdma/memory_pool.h
Normal file
29
util/rdma/rdma_benchmark/rdma/memory_pool.h
Normal 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
|
||||
105
util/rdma/rdma_benchmark/rdma/queue.c
Normal file
105
util/rdma/rdma_benchmark/rdma/queue.c
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
47
util/rdma/rdma_benchmark/rdma/queue.h
Normal file
47
util/rdma/rdma_benchmark/rdma/queue.h
Normal 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
|
||||
345
util/rdma/rdma_benchmark/rdma/rdma.go
Normal file
345
util/rdma/rdma_benchmark/rdma/rdma.go
Normal 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()
|
||||
}
|
||||
36
util/rdma/rdma_benchmark/rdma/rdma.h
Normal file
36
util/rdma/rdma_benchmark/rdma/rdma.h
Normal 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
|
||||
|
||||
416
util/rdma/rdma_benchmark/rdma/rdma_proto.c
Normal file
416
util/rdma/rdma_benchmark/rdma/rdma_proto.c
Normal 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;
|
||||
}
|
||||
}
|
||||
280
util/rdma/rdma_benchmark/rdma/rdma_proto.h
Normal file
280
util/rdma/rdma_benchmark/rdma/rdma_proto.h
Normal 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
|
||||
114
util/rdma/rdma_benchmark/rdma/server.c
Normal file
114
util/rdma/rdma_benchmark/rdma/server.c
Normal 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;
|
||||
}
|
||||
}
|
||||
20
util/rdma/rdma_benchmark/rdma/server.h
Normal file
20
util/rdma/rdma_benchmark/rdma/server.h
Normal 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
|
||||
236
util/rdma/rdma_benchmark/rdma/transfer_event.c
Normal file
236
util/rdma/rdma_benchmark/rdma/transfer_event.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
27
util/rdma/rdma_benchmark/rdma/transfer_event.h
Normal file
27
util/rdma/rdma_benchmark/rdma/transfer_event.h
Normal 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
|
||||
BIN
util/rdma/rdma_benchmark/server/server
Normal file
BIN
util/rdma/rdma_benchmark/server/server
Normal file
Binary file not shown.
115
util/rdma/rdma_benchmark/server/server.go
Normal file
115
util/rdma/rdma_benchmark/server/server.go
Normal 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()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user