Skip to content

Changes to support TP 4068 and TP 4084 #358

Merged
igaw merged 3 commits intomasterfrom
unknown repository
May 20, 2022
Merged

Changes to support TP 4068 and TP 4084 #358
igaw merged 3 commits intomasterfrom
unknown repository

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Apr 29, 2022

TP 4068 - Protection Information Enchancement
TP 4084 - Time-to-Ready Enhancements

Copy link
Copy Markdown
Collaborator

@igaw igaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this PR breaks the stable API guarantee we gave with the initial 1.0 release. You need to add these changes in a way that it is backwards compatible. E.g. when a struct *_args is extended we have to support the old version as the new one. So there needs some sort of compatibility code introduced:


if (args->size == v1_size) {
  /* access members of v1 */
} else if (args->size = v2_size) {
  /* access members up to v2 */
}

Comment thread src/nvme/types.h Outdated
Comment thread src/nvme/types.h Outdated
Comment thread src/nvme/ioctl.c Outdated
Comment thread src/libnvme.map Outdated
Comment thread src/nvme/ioctl.c Outdated
Comment thread src/nvme/ioctl.h Outdated
Comment thread src/nvme/ioctl.h Outdated
Comment thread src/nvme/ioctl.h Outdated
Comment thread src/nvme/ioctl.h Outdated
Comment thread src/nvme/ioctl.h Outdated
Signed-off-by: Jeff Lien <jeff.lien@wdc.com>
@ghost
Copy link
Copy Markdown
Author

ghost commented May 16, 2022

I see the packed attribute is causing CI jobs to fail because of warning messages. I'll have to figure out a different fix to determine the structure size of for each version.

@igaw
Copy link
Copy Markdown
Collaborator

igaw commented May 17, 2022

I see the packed attribute is causing CI jobs to fail because of warning messages. I'll have to figure out a different fix to determine the structure size of for each version.

Maybe this could work:

if  (args->size == offsetof(struct foo_args, last_member_v1))
  // version 1
else if (args->size == offsetof(struct foo_args, last_member_v2))
  // version 2

@ghost
Copy link
Copy Markdown
Author

ghost commented May 17, 2022

@igaw Looks like I can fix the warning messages in the CI process by removing the packed attribute from the nvme_io_args structure. The nvme_copy_args structure also does not need the packed attribute. The nvme_format_nvm_args and nvme_zns_append_args structures still need it though. This should resolve the failures from the CI jobs - they were all from references to pointers to specific fields in the nvme_io_args structure. Does that sound acceptable to you?

This option doesn't appear to work either. The problem is the nvme_format_nvm_args struct is only 1 byte difference in length from version 1 and 2 and it's padded out either 7 or 6 bytes. The version 1 nvme_zns_append_args struct is padded out 4 bytes. The version 2 struct is 4 bytes longer then version 1 so it uses up the 4 padded bytes and ends up the exact same length. Here's the lengths of the structs in both cases:
V1 format nvm args size - 48 (not packed, packed it would be 41)
V1 io args size - 80
V1 copy args size - 64
V1 zns append args size - 72 (not packed, packed it would be 68)

V2 format nvm args size - 42 (packed, not packed it would be 48)
V2 io args size - 88
V2 copy args size - 72
V2 zns append args size - 72 (packed, not packed, it would still be 72)

@igaw
Copy link
Copy Markdown
Collaborator

igaw commented May 18, 2022

I think you need to add explicit padding for the new member you want to add if it doesn't align naturally. Let's say you have this here

v1:

struct foo_args {
  u64 bar;
  u32 baz;
};

and now you want to add another u64 member.

v2:

struct foo_args {
  u64 bar;
  u32 baz;
  u8 rsvd6[4];
  u64 bax;
};

The compiler naturally adds padding to the v1 version of the struct, hence we need to add this to our calculation as well. Something like does the trick:

#include <stddef.h>                                                                                                                                             
#include <stdio.h>                                                                                                                                              
#include <stdint.h>                                                                                                                                             
                                                                                                                                                                
typedef uint64_t u64;                                                                                                                                           
typedef uint32_t u32;                                                                                                                                           
typedef uint8_t u8;                                                                                                                                             
                                                                                                                                                                
struct foo_args_v1 {                                                                                                                                            
  u64 bar;                                                                                                                                                      
  u32 baz;                                                                                                                                                      
};                                                                                                                                                              
                                                                                                                                                                
struct foo_args_v2 {                                                                                                                                            
  u64 bar;                                                                                                                                                      
  u32 baz;                                                                                                                                                      
  u8  rsvd6[4];                                                                                                                                                 
  u64 box;                                                                                                                                                      
};                                                                                                                                                              
                                                                                                                                                                
int main(int argc, char *argv[])                                                                                                                                
{                                                                                                                                                               
        int pad;                                                                                                                                                
        int baz;                                                                                                                                                
                                                                                                                                                                
        baz = offsetof(struct foo_args_v1, baz) + sizeof(u32);                                                                                                  
        pad = (sizeof(u64) - (baz%sizeof(u64))) % sizeof(u64);                                                                                                  
        printf("v1: baz %d pad %d\n", baz, pad);                                                                                                                
                                                                                                                                                                
        printf("v1: sizeof(struct foo_args) = %ld\n", sizeof(struct foo_args_v1));                                                                              
        printf("v1: offsetof(struct foo_args, baz) + sizeof(baz) + pad = %ld\n", offsetof(struct foo_args_v1, baz) + sizeof(u32) + pad);                        
                                                                                                                                                                
                                                                                                                                                                
        baz = offsetof(struct foo_args_v2, baz) + sizeof(u32);                                                                                                  
        pad = (sizeof(u64) - (baz%sizeof(u64))) % sizeof(u64);                                                                                                  
        printf("v2: baz %d pad %d\n", baz, pad);                                                                                                                
        printf("v2: sizeof(struct foo_args) = %ld\n", sizeof(struct foo_args_v2));                                                                              
        printf("v2: offsetof(struct foo_args, baz) + sizeof(baz) + pad  = %ld\n", offsetof(struct foo_args_v2, baz) + sizeof(u32) + pad);                       
        printf("v2: offsetof(struct foo_args, box) + sizeof(box) = %ld\n", offsetof(struct foo_args_v2, box) + sizeof(u64));                                    
}                                                                                                                                                               
v1: baz 12 pad 4
v1: sizeof(struct foo_args) = 16
v1: offsetof(struct foo_args, baz) + sizeof(baz) + pad = 16
v2: baz 12 pad 4
v2: sizeof(struct foo_args) = 24
v2: offsetof(struct foo_args, baz) + sizeof(baz) + pad  = 16
v2: offsetof(struct foo_args, box) + sizeof(box) = 24

We want probably to introduce a helper function for this kind of calculation

#define sizeof_args(type, member, align)					\
  ({										\
	  type s;								\
	  size_t t = offsetof(type, member) + sizeof(s.member);			\
	  size_t p = (sizeof(align) - (t % sizeof(align))) % sizeof(align);	\
	  t + p;								\
  })

 printf("v1: sizeof_args(struct foo_args, baz, u64) = %ld\n", sizeof_args(struct foo_args_v1, baz, u64));
 printf("v2: sizeof_args(struct foo_args, baz, u64) = %ld\n", sizeof_args(struct foo_args_v2, baz, u64));                                                            
 printf("v2: sizeof_args(struct foo_args, box, u64) = %ld\n", sizeof_args(struct foo_args_v2, box, u64)); 
v1: baz 12 pad 4
v1: sizeof(struct foo_args) = 16
v1: offsetof(struct foo_args, baz) + sizeof(baz) + pad = 16
v1: sizeof_args(struct foo_args, baz, u64) = 16
v2: baz 12 pad 4
v2: sizeof(struct foo_args) = 24
v2: offsetof(struct foo_args, baz) + sizeof(baz) + pad  = 16
v2: offsetof(struct foo_args, box) + sizeof(box) = 24
v2: sizeof_args(struct foo_args, baz, u64) = 16
v2: sizeof_args(struct foo_args, box, u64) = 24

@ghost
Copy link
Copy Markdown
Author

ghost commented May 18, 2022

Thanks for the feedback @igaw. I was hoping we could make a simpler fix but don't see how. I'll make changes based on this design and update the PR.

@ghost
Copy link
Copy Markdown
Author

ghost commented May 18, 2022

@igaw I think I have all the issues resolved now and have verified nvme cli with both libnvme 1.0 and 1.1 versions. Let me know if you have any comments or find any issues I missed. Thanks

Copy link
Copy Markdown
Collaborator

@igaw igaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there! Sorry for being pedantic but I want to get the versioning handling into good shape so that we have a blueprint for future changes.

Comment thread src/nvme/ioctl.c Outdated
Comment thread src/nvme/ioctl.c Outdated
Comment thread src/nvme/ioctl.c Outdated
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this:

int nvme_io(struct nvme_io_args *args, __u8 opcode)
{
	const size_t size_v1 = sizeof_args(struct nvme_io_args, dsm, __u64);
	const size_t size_v2 = sizeof_args(struct nvme_io_args, pif, __u64);
	__u32 cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;

	if (args->args_size < size_v1 || args->args_size > size_v2) {
		errno = EINVAL;
		return -1;
	}

	cdw10 = args->slba & 0xffffffff;
	cdw11 = args->slba >> 32;
	cdw12 = args->nlb | (args->control << 16);
	cdw13 = args->dsm | (args->dspec << 16);
	cdw15 = args->apptag | (args->appmask << 16);

	if (args->args_size == size_v1) {
		cdw2 = args->storage_tag & 0xffffffff;
		cdw3 = (args->storage_tag >> 32) & 0xffff;
		cdw14 = args->reftag;
	} else {
		if (nvme_set_var_size_tags(&cdw2, &cdw3, &cdw14,
					   args->pif,
					   args->sts,
					   args->reftag_u64,
					   args->storage_tag)) {
			errno = EINVAL;
			return -1;
		}
	}

	struct nvme_passthru_cmd cmd = {
		.opcode		= opcode,
		.nsid		= args->nsid,
		.cdw2		= cdw2,
		.cdw3		= cdw3,
		.cdw10		= cdw10,
		.cdw11		= cdw11,
		.cdw12		= cdw12,
		.cdw13		= cdw13,
		.cdw14		= cdw14,
		.cdw15		= cdw15,
		.data_len	= args->data_len,
		.metadata_len	= args->metadata_len,
		.addr		= (__u64)(uintptr_t)args->data,
		.metadata	= (__u64)(uintptr_t)args->metadata,
		.timeout_ms	= args->timeout,
	};
	return nvme_submit_io_passthru(args->fd, &cmd, args->result);
}

This follows the code order the nvme_format_nvm version above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the original changes were addressing a bug in the current handling of storage_tag, in that the spec defines both variable-sized tags as able to take the back 16 bits of cdw2, the full cdw3, and full cdw14.

Calling it a "storage tag" feels a bit wrong when it could be entirely part of a larger reference tag, but in any case it shouldn't be setting the full bits of cdw2 and should be setting cdw3.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the code to the suggested way above. Also fixed the original leg to be correct based on @bjpaupor comment.

Comment thread src/nvme/ioctl.c Outdated
Comment thread src/nvme/ioctl.h Outdated
Comment thread src/nvme/types.h Outdated
brandon-paupore-sndk and others added 2 commits May 20, 2022 10:17
Adds support for expanded reference and storage tags, automatically
placing them into the appropriate bits based on Protection Information
Format and Storage Tag Size. This includes updates to nvme_io_args
expanding the size of the tags and adding these STS and PIF values.

Also adds a new copy range format as defined by the NVMe 2.0 spec, as it
determines the reference/storage tags associated to the range being
copied.

Signed-off-by: Brandon Paupore <brandon.paupore@wdc.com>
Signed-off-by: Jeff Lien <jeff.lien@wdc.com>
Signed-off-by: Jeff Lien <jeff.lien@wdc.com>
@igaw
Copy link
Copy Markdown
Collaborator

igaw commented May 20, 2022

I've fixed a few whitespace and docs issues. No code changes.

@igaw igaw merged commit 91f66d5 into linux-nvme:master May 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants