Skip to content

Commit 3b3b35f

Browse files
committed
nvme: mi: dev: Implement Admin / Namespace Management / Create
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
1 parent f40ebdb commit 3b3b35f

4 files changed

Lines changed: 311 additions & 20 deletions

File tree

src/nvme.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,38 @@ impl ControllerListResponse {
450450
}
451451
}
452452

453+
// Base v2.1, 5.1.21, Figure 376, SEL
454+
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
455+
#[deku(ctx = "endian: Endian, sel: u8", endian = "endian", id = "sel")]
456+
#[repr(u8)]
457+
enum AdminNamespaceManagementSelect {
458+
#[deku(id = 0x00)]
459+
Create(NvmNamespaceManagementCreate),
460+
Delete = 0x01,
461+
}
462+
463+
// Base v2.1, 5.1.21
464+
// NVM Command Set v1.0c, 4.1.6, Figure 105
465+
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
466+
#[deku(ctx = "endian: Endian", endian = "endian")]
467+
struct NvmNamespaceManagementCreate {
468+
nsze: u64,
469+
ncap: u64,
470+
#[deku(seek_from_current = "10")]
471+
flbas: u8,
472+
#[deku(seek_from_current = "2")]
473+
dps: u8,
474+
nmic: u8,
475+
#[deku(seek_from_current = "61")]
476+
anagrpid: u32,
477+
#[deku(seek_from_current = "4")]
478+
nvmsetid: u16,
479+
endgid: u16,
480+
#[deku(seek_from_current = "280")]
481+
#[deku(pad_bytes_after = "3704")]
482+
lbstm: u64,
483+
}
484+
453485
// Base v2.1, 5.1.25, Figure 385
454486
// Base v2.1, 3.1.3.6, Figure 32
455487
#[derive(Debug, DekuRead, DekuWrite)]

src/nvme/mi.rs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use deku::{DekuError, DekuRead, DekuWrite};
33
use flagset::{FlagSet, flags};
44
use log::debug;
55

6+
use crate::nvme::AdminNamespaceManagementSelect;
67
use crate::wire::{WireFlagSet, WireVec};
78
use crate::{CommandEffectError, Discriminant, Encode, MAX_CONTROLLERS};
89

@@ -740,26 +741,28 @@ enum AdminCommandRequestType {
740741
Abort = 0x08, // P
741742
GetFeatures = 0x0a, // M
742743
AsynchronousEventRequest = 0x0c, // P
743-
KeepAlive = 0x18, // P
744-
DirectiveSend = 0x19, // P
745-
DirectiveReceive = 0x1a, // P
746-
NvmeMiSend = 0x1d, // P
747-
NvmeMiReceive = 0x1e, // P
744+
#[deku(id = 0x0d)]
745+
NamespaceManagement(AdminNamespaceManagementRequest),
746+
KeepAlive = 0x18, // P
747+
DirectiveSend = 0x19, // P
748+
DirectiveReceive = 0x1a, // P
749+
NvmeMiSend = 0x1d, // P
750+
NvmeMiReceive = 0x1e, // P
748751
DiscoveryInformationManagement = 0x21, // P
749-
FabricZoningReceive = 0x22, // P
750-
FabricZoningLookup = 0x25, // P
751-
FabricZoningSend = 0x29, // P
752-
SendDiscoveryLogPage = 0x39, // P
753-
TrackSend = 0x3d, // P
754-
TrackReceive = 0x3e, // P
755-
MigrationSend = 0x41, // P
756-
MigrationReceive = 0x42, // P
757-
ControllerDataQueue = 0x45, // P
758-
DoorbellBufferConfig = 0x7c, // P
759-
FabricsCommands = 0x7f, // P
760-
LoadProgram = 0x85, // P
761-
ProgramActivationManagement = 0x88, // P
762-
MemoryRangeSetManagement = 0x89, // P
752+
FabricZoningReceive = 0x22, // P
753+
FabricZoningLookup = 0x25, // P
754+
FabricZoningSend = 0x29, // P
755+
SendDiscoveryLogPage = 0x39, // P
756+
TrackSend = 0x3d, // P
757+
TrackReceive = 0x3e, // P
758+
MigrationSend = 0x41, // P
759+
MigrationReceive = 0x42, // P
760+
ControllerDataQueue = 0x45, // P
761+
DoorbellBufferConfig = 0x7c, // P
762+
FabricsCommands = 0x7f, // P
763+
LoadProgram = 0x85, // P
764+
ProgramActivationManagement = 0x88, // P
765+
MemoryRangeSetManagement = 0x89, // P
763766
}
764767
unsafe impl Discriminant<u8> for AdminCommandRequestType {}
765768

@@ -824,6 +827,24 @@ struct AdminIdentifyRequest {
824827
req: AdminIdentifyCnsRequestType,
825828
}
826829

830+
// MI v2.0, 6, Figure 136
831+
// Base v2.1, 5.1.21, Figures 367-369
832+
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
833+
#[deku(ctx = "endian: Endian", endian = "endian")]
834+
struct AdminNamespaceManagementRequest {
835+
nsid: u32,
836+
#[deku(seek_from_current = "16")]
837+
dofst: u32,
838+
dlen: u32,
839+
#[deku(seek_from_current = "8")]
840+
sel: u8, // NOTE: SEL is the bottom nibble
841+
#[deku(seek_from_current = "6")]
842+
csi: u8,
843+
#[deku(seek_from_current = "16")]
844+
#[deku(ctx = "*sel")]
845+
req: AdminNamespaceManagementSelect,
846+
}
847+
827848
// MI v2.0, 6, Figure 138
828849
#[derive(Debug, DekuRead, DekuWrite)]
829850
#[deku(endian = "little")]

src/nvme/mi/dev.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
LidSupportedAndEffectsDataStructure, LidSupportedAndEffectsFlags, LogPageAttributes,
2121
NamespaceIdentifierType, SmartHealthInformationLogPageResponse,
2222
mi::{
23-
AdminCommandRequestHeader, AdminCommandResponseHeader,
23+
AdminCommandRequestHeader, AdminCommandResponseHeader, AdminNamespaceManagementRequest,
2424
CompositeControllerStatusDataStructureResponse, CompositeControllerStatusFlagSet,
2525
ControllerFunctionAndReportingFlags, ControllerHealthDataStructure,
2626
ControllerHealthStatusPollResponse, ControllerInformationResponse,
@@ -827,6 +827,9 @@ impl RequestHandler for AdminCommandRequestHeader {
827827
AdminCommandRequestType::Identify(req) => {
828828
req.handle(ctx, mep, subsys, rest, resp, app).await
829829
}
830+
AdminCommandRequestType::NamespaceManagement(req) => {
831+
req.handle(ctx, mep, subsys, rest, resp, app).await
832+
}
830833
op if MI_PROHIBITED_ADMIN_COMMANDS.contains(&self.op) => {
831834
debug!("Prohibited MI admin command opcode: {:?}", op.id());
832835
Err(ResponseStatus::InvalidCommandOpcode)
@@ -1459,6 +1462,68 @@ impl RequestHandler for AdminIdentifyRequest {
14591462
}
14601463
}
14611464

1465+
impl RequestHandler for AdminNamespaceManagementRequest {
1466+
type Ctx = AdminCommandRequestHeader;
1467+
1468+
async fn handle<A, C>(
1469+
&self,
1470+
_ctx: &Self::Ctx,
1471+
_mep: &mut crate::ManagementEndpoint,
1472+
subsys: &mut crate::Subsystem,
1473+
rest: &[u8],
1474+
resp: &mut C,
1475+
_app: A,
1476+
) -> Result<(), ResponseStatus>
1477+
where
1478+
A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1479+
C: AsyncRespChannel,
1480+
{
1481+
if !rest.is_empty() {
1482+
debug!("Invalid request size for Admin Identify");
1483+
return Err(ResponseStatus::InvalidCommandSize);
1484+
}
1485+
1486+
match &self.req {
1487+
crate::nvme::mi::AdminNamespaceManagementSelect::Create(req) => {
1488+
if self.csi != 0 {
1489+
debug!("Support CSI {}", self.csi);
1490+
return Err(ResponseStatus::InternalError);
1491+
}
1492+
1493+
let Ok(nsid) = subsys.add_namespace(req.ncap) else {
1494+
debug!("Failed to create namespace");
1495+
// TODO: Implement Base v2.1, 5.1.21.1, Figure 370
1496+
return Err(ResponseStatus::InternalError);
1497+
};
1498+
let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?;
1499+
1500+
let acrh = AdminCommandResponseHeader {
1501+
status: ResponseStatus::Success,
1502+
cqedw0: nsid.0,
1503+
cqedw1: 0,
1504+
cqedw3: AdminIoCqeStatus {
1505+
cid: 0,
1506+
p: true,
1507+
status: AdminIoCqeStatusType::GenericCommandStatus(
1508+
AdminIoCqeGenericCommandStatus::SuccessfulCompletion,
1509+
),
1510+
crd: crate::nvme::CommandRetryDelay::None,
1511+
m: false,
1512+
dnr: false,
1513+
}
1514+
.into(),
1515+
}
1516+
.encode()?;
1517+
1518+
send_response(resp, &[&mh.0, &acrh.0]).await;
1519+
1520+
Ok(())
1521+
}
1522+
crate::nvme::mi::AdminNamespaceManagementSelect::Delete => todo!(),
1523+
}
1524+
}
1525+
}
1526+
14621527
impl crate::ManagementEndpoint {
14631528
fn update(&mut self, subsys: &crate::Subsystem) {
14641529
assert!(subsys.ctlrs.len() <= self.mecss.len());

tests/admin.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,3 +2507,176 @@ mod get_log_page {
25072507
});
25082508
}
25092509
}
2510+
2511+
mod namespace_management {
2512+
use mctp::MsgIC;
2513+
2514+
use crate::{
2515+
RESP_INVALID_COMMAND_SIZE,
2516+
common::{DeviceType, ExpectedRespChannel, new_device, setup},
2517+
};
2518+
2519+
#[test]
2520+
fn create_short() {
2521+
setup();
2522+
2523+
let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1aN0a0a);
2524+
2525+
#[rustfmt::skip]
2526+
const REQ: [u8; 71] = [
2527+
0x10, 0x00, 0x00,
2528+
0x0d, 0x00, 0x00, 0x00,
2529+
2530+
// SQE DWORD 1
2531+
0x00, 0x00, 0x00, 0x00,
2532+
0x00, 0x00, 0x00, 0x00,
2533+
0x00, 0x00, 0x00, 0x00,
2534+
0x00, 0x00, 0x00, 0x00,
2535+
0x00, 0x00, 0x00, 0x00,
2536+
2537+
// DOFST
2538+
0x00, 0x00, 0x00, 0x00,
2539+
0x00, 0x10, 0x00, 0x00,
2540+
2541+
// Reserved
2542+
0x00, 0x00, 0x00, 0x00,
2543+
0x00, 0x00, 0x00, 0x00,
2544+
2545+
// SQE DWORD 10
2546+
0x00, 0x00, 0x00, 0x00,
2547+
0x00, 0x00, 0x00, 0x00,
2548+
0x00, 0x00, 0x00, 0x00,
2549+
0x00, 0x00, 0x00, 0x00,
2550+
0x00, 0x00, 0x00, 0x00,
2551+
0x00, 0x00, 0x00, 0x00,
2552+
2553+
// Missing request data
2554+
2555+
// MIC
2556+
0x17, 0xc6, 0x4c, 0x65
2557+
];
2558+
2559+
let resp = ExpectedRespChannel::new(&RESP_INVALID_COMMAND_SIZE);
2560+
smol::block_on(async {
2561+
mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(()))
2562+
.await
2563+
});
2564+
}
2565+
2566+
#[test]
2567+
fn create_long() {
2568+
setup();
2569+
2570+
let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1aN0a0a);
2571+
2572+
#[rustfmt::skip]
2573+
const REQ_DATA: [u8; 67] = [
2574+
0x10, 0x00, 0x00,
2575+
0x0d, 0x00, 0x00, 0x00,
2576+
2577+
// SQE DWORD 1
2578+
0x00, 0x00, 0x00, 0x00,
2579+
0x00, 0x00, 0x00, 0x00,
2580+
0x00, 0x00, 0x00, 0x00,
2581+
0x00, 0x00, 0x00, 0x00,
2582+
0x00, 0x00, 0x00, 0x00,
2583+
2584+
// DOFST
2585+
0x00, 0x00, 0x00, 0x00,
2586+
0x00, 0x10, 0x00, 0x00,
2587+
2588+
// Reserved
2589+
0x00, 0x00, 0x00, 0x00,
2590+
0x00, 0x00, 0x00, 0x00,
2591+
2592+
// SQE DWORD 10
2593+
0x00, 0x00, 0x00, 0x00,
2594+
0x00, 0x00, 0x00, 0x00,
2595+
0x00, 0x00, 0x00, 0x00,
2596+
0x00, 0x00, 0x00, 0x00,
2597+
0x00, 0x00, 0x00, 0x00,
2598+
0x00, 0x00, 0x00, 0x00,
2599+
];
2600+
2601+
const REQ_MIC: [u8; 4] = [0x5d, 0x13, 0x77, 0x61];
2602+
2603+
let mut req = [0u8; { 71 + 4096 + 1 }];
2604+
let len = req.len();
2605+
req[..REQ_DATA.len()].copy_from_slice(&REQ_DATA);
2606+
req[{ len - REQ_MIC.len() }..].copy_from_slice(&REQ_MIC);
2607+
2608+
let resp = ExpectedRespChannel::new(&RESP_INVALID_COMMAND_SIZE);
2609+
smol::block_on(async {
2610+
mep.handle_async(&mut subsys, &req, MsgIC(true), resp, async |_| Ok(()))
2611+
.await
2612+
});
2613+
}
2614+
2615+
#[test]
2616+
fn create() {
2617+
setup();
2618+
2619+
let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1aN0a0a);
2620+
2621+
#[rustfmt::skip]
2622+
const REQ_DATA: [u8; 83] = [
2623+
0x10, 0x00, 0x00,
2624+
0x0d, 0x00, 0x00, 0x00,
2625+
2626+
// SQE DWORD 1
2627+
0x00, 0x00, 0x00, 0x00,
2628+
0x00, 0x00, 0x00, 0x00,
2629+
0x00, 0x00, 0x00, 0x00,
2630+
0x00, 0x00, 0x00, 0x00,
2631+
0x00, 0x00, 0x00, 0x00,
2632+
2633+
// DOFST
2634+
0x00, 0x00, 0x00, 0x00,
2635+
0x00, 0x10, 0x00, 0x00,
2636+
2637+
// Reserved
2638+
0x00, 0x00, 0x00, 0x00,
2639+
0x00, 0x00, 0x00, 0x00,
2640+
2641+
// SQE DWORD 10
2642+
0x00, 0x00, 0x00, 0x00,
2643+
0x00, 0x00, 0x00, 0x00,
2644+
0x00, 0x00, 0x00, 0x00,
2645+
0x00, 0x00, 0x00, 0x00,
2646+
0x00, 0x00, 0x00, 0x00,
2647+
0x00, 0x00, 0x00, 0x00,
2648+
2649+
// Request Data
2650+
// NSZE
2651+
0x00, 0x10, 0x00, 0x00,
2652+
0x00, 0x00, 0x00, 0x00,
2653+
2654+
// NCAP
2655+
0x00, 0x10, 0x00, 0x00,
2656+
0x00, 0x00, 0x00, 0x00,
2657+
];
2658+
2659+
const REQ_MIC: [u8; 4] = [0xd1, 0xf0, 0x5b, 0xd1];
2660+
2661+
let mut req = [0u8; { 71 + 4096 }];
2662+
let len = req.len();
2663+
req[..REQ_DATA.len()].copy_from_slice(&REQ_DATA);
2664+
req[{ len - REQ_MIC.len() }..].copy_from_slice(&REQ_MIC);
2665+
2666+
#[rustfmt::skip]
2667+
const RESP: [u8; 23] = [
2668+
0x90, 0x00, 0x00,
2669+
0x00, 0x00, 0x00, 0x00,
2670+
0x01, 0x00, 0x00, 0x00,
2671+
0x00, 0x00, 0x00, 0x00,
2672+
0x00, 0x00, 0x01, 0x00,
2673+
0x00, 0x01, 0xd3, 0xaa
2674+
];
2675+
2676+
let resp = ExpectedRespChannel::new(&RESP);
2677+
smol::block_on(async {
2678+
mep.handle_async(&mut subsys, &req, MsgIC(true), resp, async |_| Ok(()))
2679+
.await
2680+
});
2681+
}
2682+
}

0 commit comments

Comments
 (0)