cubefs
1// Copyright 2022 The CubeFS Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12// implied. See the License for the specific language governing
13// permissions and limitations under the License.
14
15package ec16
17import (18"github.com/cubefs/cubefs/blobstore/common/codemode"19"github.com/cubefs/cubefs/blobstore/common/resourcepool"20"github.com/cubefs/cubefs/blobstore/util/log"21)
22
23// Buffer one ec blob's reused buffer
24// Manually manage the DataBuf in Ranged mode, do not Split it
25// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26// | data | align bytes | partiy | local |
27// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
28// | DataBuf |
29// |<--DataSize->|
30// - - - - - - - - - - - - - - - - - -
31// | ECDataBuf |
32// |<-- ECDataSize -->|
33// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34// | ECBuf |
35// |<--- ECSize --->|
36type Buffer struct {37tactic codemode.Tactic38pool *resourcepool.MemPool39
40// DataBuf real data buffer41DataBuf []byte42// ECDataBuf ec data buffer to Split,43// is nil if Buffer is Ranged mode44ECDataBuf []byte45// BufferSizes all sizes46BufferSizes
47}
48
49// BufferSizes all sizes
50// ECSize is sum of all ec shards size,
51// not equal the capacity of DataBuf in Ranged mode
52type BufferSizes struct {53ShardSize int // per shard size54DataSize int // real data size55ECDataSize int // ec data size only with data56ECSize int // ec buffer size with partiy and local shards57From int // ranged from58To int // ranged to59}
60
61func isOutOfRange(dataSize, from, to int) bool {62return dataSize <= 0 || to < from ||63from < 0 || from > dataSize ||64to < 0 || to > dataSize65}
66
67func newBuffer(dataSize, from, to int, tactic codemode.Tactic, pool *resourcepool.MemPool, hasParity bool) (*Buffer, error) {68if isOutOfRange(dataSize, from, to) {69return nil, ErrShortData70}71
72shardN := tactic.N73if shardN <= 0 {74return nil, ErrInvalidCodeMode75}76
77shardSize := (dataSize + shardN - 1) / shardN78// align per shard with tactic MinShardSize79if shardSize < tactic.MinShardSize {80shardSize = tactic.MinShardSize81}82
83ecDataSize := shardSize * tactic.N84ecSize := shardSize * (tactic.N + tactic.M + tactic.L)85
86var (87err error88
89buf []byte90dataBuf []byte91ecDataBuf []byte92)93if pool != nil {94size := ecSize95if !hasParity {96size = to - from97}98
99buf, err = pool.Get(size)100if err == resourcepool.ErrNoSuitableSizeClass {101log.Warn(err, "for", size, "try to alloc bytes")102buf, err = pool.Alloc(size)103}104if err != nil {105return nil, err106}107
108if !hasParity {109dataBuf = buf[:size]110ecDataBuf = nil111} else {112// zero the padding bytes of data section113pool.Zero(buf[dataSize:ecDataSize])114dataBuf = buf[:dataSize]115ecDataBuf = buf[:ecDataSize]116}117}118
119return &Buffer{120tactic: tactic,121pool: pool,122DataBuf: dataBuf,123ECDataBuf: ecDataBuf,124BufferSizes: BufferSizes{125ShardSize: shardSize,126DataSize: dataSize,127ECDataSize: ecDataSize,128ECSize: ecSize,129From: from,130To: to,131},132}, nil133}
134
135// NewBuffer new ec buffer with data size and ec mode
136func NewBuffer(dataSize int, tactic codemode.Tactic, pool *resourcepool.MemPool) (*Buffer, error) {137return newBuffer(dataSize, 0, dataSize, tactic, pool, true)138}
139
140// NewRangeBuffer ranged buffer with least data size
141func NewRangeBuffer(dataSize, from, to int, tactic codemode.Tactic, pool *resourcepool.MemPool) (*Buffer, error) {142return newBuffer(dataSize, from, to, tactic, pool, false)143}
144
145// GetBufferSizes calculate ec buffer sizes
146func GetBufferSizes(dataSize int, tactic codemode.Tactic) (BufferSizes, error) {147buf, err := NewBuffer(dataSize, tactic, nil)148if err != nil {149return BufferSizes{}, err150}151return buf.BufferSizes, nil152}
153
154// Resize re-calculate ec buffer, alloc a new buffer if oversize
155func (b *Buffer) Resize(dataSize int) error {156if dataSize == b.BufferSizes.DataSize {157return nil158}159
160sizes, err := GetBufferSizes(dataSize, b.tactic)161if err != nil {162return err163}164
165// buffer is enough166if sizes.ECSize <= cap(b.DataBuf) {167buf := b.DataBuf[:cap(b.DataBuf)]168// zero the padding bytes of data section169b.pool.Zero(buf[sizes.DataSize:sizes.ECDataSize])170
171b.DataBuf = buf[:sizes.DataSize]172b.ECDataBuf = buf[:sizes.ECDataSize]173b.BufferSizes = sizes174return nil175}176
177newb, err := NewBuffer(dataSize, b.tactic, b.pool)178if err != nil {179return err180}181
182b.Release()183*b = *newb184return nil185}
186
187// Release recycles the buffer into pool
188func (b *Buffer) Release() error {189if b == nil {190return nil191}192if b.DataBuf == nil {193b.pool = nil194return nil195}196
197buf := b.DataBuf198b.DataBuf = nil199b.ECDataBuf = nil200
201pool := b.pool202b.pool = nil203if pool != nil {204return pool.Put(buf)205}206
207return nil208}
209