Skip to content

Commit

Permalink
Support clustered allocation when formatting ext4 filesystem
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewSirenko committed Oct 10, 2023
1 parent e06c0d0 commit 749c9bf
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 6 deletions.
11 changes: 11 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Frequently Asked Questions

## CreateVolume (`StorageClass`) Parameters

### `ext4ClusterSize`

Setting this parameter will enable the experimental `bigalloc` `ext4` feature.

Warnings:
- The `bigalloc` feature may not be fully supported with your node's Linux kernel or may have various bugs. Please see the [ext4(5) man-page](https://man7.org/linux/man-pages/man5/ext4.5.html) for details.
- Linux kernel release 4.15 added support for resizing ext4 filesystems using clustered allocation. **Volumes mounted to nodes running prior kernel versions will fail to resize and require manual intervention and downtime of the volume. It is strongly recommended not to initiate a resize of a volume formatted with clustered allocation which is or will be attached to a node with a Linux kernel version prior to 4.15.**
2 changes: 2 additions & 0 deletions docs/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The AWS EBS CSI Driver supports [tagging](tagging.md) through `StorageClass.para
| "inodeSize" | | | The inode size to use when formatting the underlying filesystem. Only supported on linux nodes and with fstype `ext2`, `ext3`, `ext4`, or `xfs`. |
| "bytesPerInode" | | | The `bytes-per-inode` to use when formatting the underlying filesystem. Only supported on linux nodes and with fstype `ext2`, `ext3`, `ext4`. |
| "numberOfInodes" | | | The `number-of-inodes` to use when formatting the underlying filesystem. Only supported on linux nodes and with fstype `ext2`, `ext3`, `ext4`. |
| "ext4ClusterSize" | | | The cluster size to use when formatting an `ext4` filesystem using clustered allocation. Warning: setting this parameter enables the `bigalloc` `ext4` feature. See our [FAQ](/docs/faq.md). |

## Restrictions
* `gp3` is currently not supported on outposts. Outpost customers need to use a different type for their volumes.
* If the requested IOPS (either directly from `iops` or from `iopsPerGB` multiplied by the volume's capacity) produces a value above the maximum IOPS allowed for the [volume type](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html), the IOPS will be capped at the maximum value allowed. If the value is lower than the minimal supported IOPS value per volume, either an error is returned (the default behavior), or the value is increased to fit into the supported range when `allowautoiopspergbincrease` is `"true"`.
Expand Down
13 changes: 11 additions & 2 deletions pkg/driver/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ const (
// NumberOfInodesKey configures the `number-of-inodes` when formatting a volume
NumberOfInodesKey = "numberofinodes"

// Ext4ClusterSize enables the ext4 bigalloc option configures the cluster size when formatting an ext4 volume
Ext4ClusterSize = "ext4clustersize"

// TagKeyPrefix contains the prefix of a volume parameter that designates it as
// a tag to be attached to the resource
TagKeyPrefix = "tagSpecification"
Expand Down Expand Up @@ -188,10 +191,14 @@ func (fsConfig fileSystemConfig) isParameterSupported(paramName string) bool {
var (
FileSystemConfigs = map[string]fileSystemConfig{
FSTypeExt2: {
NotSupportedParams: map[string]struct{}{},
NotSupportedParams: map[string]struct{}{
Ext4ClusterSize: {},
},
},
FSTypeExt3: {
NotSupportedParams: map[string]struct{}{},
NotSupportedParams: map[string]struct{}{
Ext4ClusterSize: {},
},
},
FSTypeExt4: {
NotSupportedParams: map[string]struct{}{},
Expand All @@ -200,6 +207,7 @@ var (
NotSupportedParams: map[string]struct{}{
BytesPerInodeKey: {},
NumberOfInodesKey: {},
Ext4ClusterSize: {},
},
},
FSTypeNtfs: {
Expand All @@ -208,6 +216,7 @@ var (
InodeSizeKey: {},
BytesPerInodeKey: {},
NumberOfInodesKey: {},
Ext4ClusterSize: {},
},
},
}
Expand Down
20 changes: 16 additions & 4 deletions pkg/driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,11 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
cloud.VolumeNameTagKey: volName,
cloud.AwsEbsDriverTagKey: isManagedByDriver,
}
blockSize string
inodeSize string
bytesPerInode string
numberOfInodes string
blockSize string
inodeSize string
bytesPerInode string
numberOfInodes string
ext4ClusterSize string
)

tProps := new(template.PVProps)
Expand Down Expand Up @@ -207,6 +208,11 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
return nil, status.Errorf(codes.InvalidArgument, "Could not parse numberOfInodes (%s): %v", value, err)
}
numberOfInodes = value
case Ext4ClusterSize:
if isAlphanumeric := util.StringIsAlphanumeric(value); !isAlphanumeric {
return nil, status.Errorf(codes.InvalidArgument, "Could not parse ext4ClusterSize (%s): %v", value, err)
}
ext4ClusterSize = value
default:
if strings.HasPrefix(key, TagKeyPrefix) {
scTags = append(scTags, value)
Expand Down Expand Up @@ -242,6 +248,12 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
return nil, err
}
}
if len(ext4ClusterSize) > 0 {
responseCtx[Ext4ClusterSize] = ext4ClusterSize
if err = validateVolumeCapabilities(req.GetVolumeCapabilities(), Ext4ClusterSize, FileSystemConfigs); err != nil {
return nil, err
}
}

if blockExpress && volumeType != cloud.VolumeTypeIO2 {
return nil, status.Errorf(codes.InvalidArgument, "Block Express is only supported on io2 volumes")
Expand Down
14 changes: 14 additions & 0 deletions pkg/driver/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,13 @@ func TestCreateVolumeWithFormattingParameters(t *testing.T) {
},
errExpected: false,
},
{
name: "success with ext4 bigalloc cluster size",
formattingOptionParameters: map[string]string{
Ext4ClusterSize: "16384",
},
errExpected: false,
},
{
name: "failure with block size",
formattingOptionParameters: map[string]string{
Expand Down Expand Up @@ -1716,6 +1723,13 @@ func TestCreateVolumeWithFormattingParameters(t *testing.T) {
},
errExpected: true,
},
{
name: "failure with ext4 cluster size",
formattingOptionParameters: map[string]string{
Ext4ClusterSize: "wrong_value",
},
errExpected: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions pkg/driver/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
if err != nil {
return nil, err
}
ext4ClusterSize, err := recheckFormattingOptionParameter(context, Ext4ClusterSize, FileSystemConfigs, fsType)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -262,6 +263,9 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
if len(numInodes) > 0 {
formatOptions = append(formatOptions, "-N", numInodes)
}
if len(ext4ClusterSize) > 0 {
formatOptions = append(formatOptions, "-O", "bigalloc", "-C", ext4ClusterSize)
}
err = d.mounter.FormatAndMountSensitiveWithFormatOptions(source, target, fsType, mountOptions, nil, formatOptions)
if err != nil {
msg := fmt.Sprintf("could not format %q and mount it at %q: %v", source, target, err)
Expand Down
14 changes: 14 additions & 0 deletions pkg/driver/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,20 @@ func TestNodeStageVolume(t *testing.T) {
mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-N", "13107200"}))
},
},
{
name: "success with cluster size in ext4",
request: &csi.NodeStageVolumeRequest{
PublishContext: map[string]string{DevicePathKey: devicePath},
StagingTargetPath: targetPath,
VolumeCapability: stdVolCap,
VolumeId: volumeID,
VolumeContext: map[string]string{Ext4ClusterSize: "16384"},
},
expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
successExpectMock(mockMounter, mockDeviceIdentifier)
mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-O", "bigalloc", "-C", "16384"}))
},
},
{
name: "fail no VolumeId",
request: &csi.NodeStageVolumeRequest{
Expand Down
4 changes: 4 additions & 0 deletions tests/e2e/format_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ var (
CreateVolumeParameterKey: ebscsidriver.NumberOfInodesKey,
CreateVolumeParameterValue: "200192",
},
{
CreateVolumeParameterKey: ebscsidriver.Ext4ClusterSize,
CreateVolumeParameterValue: "16384",
},
}
)

Expand Down

0 comments on commit 749c9bf

Please sign in to comment.