Conversation
| __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, | ||
| __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, | ||
| void *data, __u32 metadata_len, void *metadata, __u32 timeout_ms, | ||
| __u32 *result); |
There was a problem hiding this comment.
Could you leave the orignal formatting of the function as it is and just add the __attribute__. This way it's clear this change is only adding the attribute. Mixing whitespace changes into code changes should best be avoided, it makes the reviewing harder than it needs to be. Thanks
|
|
||
| static const int default_timeout = 1000; /* milliseconds; endpoints may | ||
| override */ | ||
| static const int default_timeout = 1000; /* milliseconds; endpoints may override */ |
There was a problem hiding this comment.
Please do not change the formatting if there is no good reason for it. It perfectly fine to use to 80 chars limits. There is no fix rule we nee do to use 100 chars.
| struct nvme_mi_control_req *control_req, | ||
| __u8 opcode, __u16 cpsp) | ||
| struct nvme_mi_control_req *control_req, __u8 opcode, | ||
| __u16 cpsp) |
There was a problem hiding this comment.
The original formatting looks saner in my opinion. Let's keep it this way, please.
In an ideal world we would have used something like clang formatter from the beginning but I think this ship has sailed and we live in an imperfect world. And I really think checkpatch.pl is really a crutch, it helps for new submissions to minimize the review work but for the existing code I'd rather leave as it is unless it is completely offensive to read (e.g. the plugin code used to be very bad). Though the mi.c file is one of the very nicely formatted files and Jeremy is really taking care it stays this way.
|
Allowing arbitrary override of the library functions sounds like a recipe for disaster, and we don't have a good rationale for this change. The PR description is empty. Can you provide some information about why you're trying to do this? |
|
The idea with the weak symbol for the passthru is that the user is able to implement their own logging. For the non-mi passthru we already got this. nvme-cli is able to annotate the ioctl. Before that there was a lot of random printfs sprinkled in the libnvme, which I really hated. Thus the compromise was to allow the overwrite the low level wrapper around ioctl. Yes, it opens the door for other disasters but then it's not our problem :) E.g. the # nvme id-ctrl -vv /dev/nvme0
opcode : 06
flags : 00
rsvd1 : 0000
nsid : 00000000
cdw2 : 00000000
cdw3 : 00000000
data_len : 00001000
metadata_len : 00000000
addr : 564b82b40000
metadata : 0
cdw10 : 00000001
cdw11 : 00000000
cdw12 : 00000000
cdw13 : 00000000
cdw14 : 00000000
cdw15 : 00000000
timeout_ms : 00000000
result : 00000000
err : 0
latency : 2317 us |
|
Ah! thanks for the context. In that case, I'd agree with the prior review - there's a lot of churn in this change, can we just stick to the attribute addition? |
| __u32 cdw13, __u32 cdw14, __u32 cdw15, | ||
| __u32 data_len, void *data, __u32 metadata_len, | ||
| void *metadata, __u32 timeout_ms, __u32 *result) | ||
| int nvme_mi_admin_passthru(nvme_mi_ctrl_t ctrl, struct nvme_passthru_cmd *cmd, __u32 *result) |
There was a problem hiding this comment.
We'll end up with two functions with similar names:
nvme_mi_admin_passthru; andnvme_mi_admin_admin_passthru
- is there a batter way to distinguish these?
fa7312c to
3653dec
Compare
|
Deleted the commits for coding style changes. Also change the function name |
3653dec to
b4cd68e
Compare
Just thinking about this a little - would it make more sense to have a weak function lower in the stack, at the point where the MI command is submitted? The default implementation of that function would be a no-op, but users could override to get per-command logging. At present this is just for the passthrough interface, and we'd need to make a lot more changes to get full coverage. |
|
The current weak functions are just a wrapper around the ioctl call. I didn't thought of using an empty default implementation. Sounds like a good idea! |
|
OK, in that case (if I understand the overall intention correctly) we would want to hook into So, two options that I can see there:
This would match the "hook at the ioctl" concept more closely, rather than just hooking the mi_passthrough call, which is used only for custom commands. [That said, the MCTP transport makes this fairly easy to debug without having to modify any code; we can just tcpdump / wireshark the MCTP messages to observe the requests/responses. However, that's at a different level, so there still may be valid usecases for doing the debug in-process] |
|
After a bit of pondering, I'd say the first API is the one we should do. nvme-cli can do all the logging with those hooks and it avoids duplicating any code, e.g. error handling. Maybe it would be good to allow passing around a opaque pointer... |
|
Thanks for your comments and advices. The structures |
OK, but we'd need somewhere to stash that, and set it up. Would it belong in the root, or the endpoint?
Yes, good point. What information do you want access to for these hooks? |
Ah I remember now why I have chosen the second API approach. The scoping of the function is nice but has the downside that all users need to provide the complete implementation. Anyway, what I had in mind was something like: // libnvme
__attribute__((weak)) void *nvme_submit_passthru_hook_entry(struct nvme_passthru_cmd *cmd) { }
__attribute__((weak)) void nvme_submit_passthru_hook_exit(struct nvme_passthru_cmd *cmd, int err, void *user_data) { }
int nvme_submit_passthru(int fd, unsigned long ioctl_cmd,
struct nvme_passthru_cmd *cmd, __u32 *result)
{
void *user_data;
user_data = nvme_submit_passthru_hook_entry(cmd);
int err = ioctl(fd, ioctl_cmd, cmd);
if (err >= 0 && result)
*result = cmd->result;
nvme_submit_passthru_hook_exit(cmd, err, user_data);
return err;
}
// nvme-cli
struct submit_data
{
struct timeval start;
struct timeval end;
};
static struct submit_data sb;
void *nvme_sumbit_passthru_hook_entry(struct nvme_passthru_cmd *cmd)
{
sb = { 0, };
if (log_level >= LOG_DEBUG)
gettimeofday(&sb.start, NULL);
return &sb;
}
void nvme_sumbit_passthru_hook_exit(struct nvme_passthru_cmd *cmd, int err, void *user_data)
{
struct submit_data *sb = user_data;
if (log_level >= LOG_DEBUG) {
gettimeofday(sb->end, NULL);
nvme_show_command(cmd, err);
nvme_show_latency(sb->start, sb->end);
}
}Obviously, it's a bit stupid that there is a global data struct in nvme-cli but that could be made also a bit smarter. I just want to avoid adding things to libnvme which is way harder to change later. |
|
One question: why the reference to 'passthru' here? Do you only want logging for passthru-style commands (ie, those that are bypassing the general libnvme-mi API), or for all commands? |
ok, so the |
|
How about the changes below as the struct nvme_mi_msg_hdr can be used for API? tokunori@tokunori-desktop:~/nvme-cli/subprojects/libnvme$ git diff
diff --git a/src/nvme/mi.c b/src/nvme/mi.c
index 4640b0a9..d4922cf9 100644
--- a/src/nvme/mi.c
+++ b/src/nvme/mi.c
@@ -414,11 +414,16 @@ static int nvme_mi_verify_resp_mic(struct nvme_mi_resp *resp)
return resp->mic != ~crc;
}
+__attribute__((weak)) void nvme_mi_submit_entry(nvme_mi_ep_t ep, struct nvme_mi_msg_hdr *req) { }
+__attribute__((weak)) void nvme_mi_submit_exit(nvme_mi_ep_t ep, struct nvme_mi_msg_hdr *resp) { }
+
int nvme_mi_submit(nvme_mi_ep_t ep, struct nvme_mi_req *req,
struct nvme_mi_resp *resp)
{
int rc;
+ nvme_mi_submit_entry(ep, req->hdr);
+
if (req->hdr_len < sizeof(struct nvme_mi_msg_hdr)) {
errno = EINVAL;
return -1;
@@ -502,6 +507,8 @@ int nvme_mi_submit(nvme_mi_ep_t ep, struct nvme_mi_req *req,
return -1;
}
+ nvme_mi_submit_exit(ep, resp->hdr);
+
return 0;
} |
I was just using the existing code as example, how I think we should pass in an opaque pointer. Sorry should have added this.
For this case I think your proposal looks 1) good just with the additional pointer passing. |
that looks nice and neat, but I don't have much info about your aims here. From my previous comment: What information do you want access from these hooks? Given that we're writing a stable API here, we'll want to get this right first-time. When passing the header (ie Also: I assume the request/response data should be I would also suggest that you'll want to pass both the request and the response to the As @igaw has brought up, there should probably also be some way to correlate a request (a call to |
|
Hi, printf("opcode : %02x\n", cfg.opcode);
printf("flags : %02x\n", cfg.flags);
printf("rsvd1 : %04x\n", cfg.rsvd);
printf("nsid : %08x\n", cfg.namespace_id);
printf("cdw2 : %08x\n", cfg.cdw2);
printf("cdw3 : %08x\n", cfg.cdw3);
printf("data_len : %08x\n", cfg.data_len);
printf("metadata_len : %08x\n", cfg.metadata_len);
printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)data);
printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mdata);
printf("cdw10 : %08x\n", cfg.cdw10);
printf("cdw11 : %08x\n", cfg.cdw11);
printf("cdw12 : %08x\n", cfg.cdw12);
printf("cdw13 : %08x\n", cfg.cdw13);
printf("cdw14 : %08x\n", cfg.cdw14);
printf("cdw15 : %08x\n", cfg.cdw15);
printf("timeout_ms : %08x\n", nvme_cfg.timeout);So it okay to pass the struct nvme_mi_admin_req_hdr {
struct nvme_mi_msg_hdr hdr;
__u8 opcode;
__u8 flags;
__le16 ctrl_id;
__le32 cdw1, cdw2, cdw3, cdw4, cdw5;
__le32 doff;
__le32 dlen;
__le32 rsvd0, rsvd1;
__le32 cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;
} __attribute((packed));Also the following information also can be parsed from the dwords information above. printf("opcode : %02x\n", opcode);
printf("nsid : %02x\n", cfg.namespace_id);
printf("flags : %02x\n", 0);
printf("control : %04x\n", control);
printf("nblocks : %04x\n", nblocks);
printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mbuffer);
printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)buffer);
printf("slba : %"PRIx64"\n", (uint64_t)cfg.start_block);
printf("dsmgmt : %08x\n", dsmgmt);
printf("reftag : %"PRIx64"\n", (uint64_t)cfg.ref_tag);
printf("apptag : %04x\n", cfg.app_tag);
printf("appmask : %04x\n", cfg.app_tag_mask);
printf("storagetagcheck : %04x\n", cfg.storage_tag_check);
printf("storagetag : %"PRIx64"\n", (uint64_t)cfg.storage_tag);
printf("pif : %02x\n", pif);
printf("sts : %02x\n", sts);I understand the It is also same for other structures for example struct nvme_mi_control_req {
struct nvme_mi_msg_hdr hdr;
__u8 opcode;
__u8 tag;
__le16 cpsp;
} __attribute((packed));And it can be checked by the struct nvme_mi_msg_hdr {
__u8 type;
__u8 nmp;
__u8 meb;
__u8 rsvd0;
} __attribute__((packed));Yes I will do update the changes to use const paramter then push the commit later. |
b4cd68e to
4a8614a
Compare
OK, thanks for the info. are you looking to access the message payload data as well?
That's true for now, but is an implementation detail of the internal workings of libnvme. The proposed change will codify the layout of libnvme's own buffers into a stable ABI. For example: if we changed to a scatter-gather buffer¹, the MI header data may no longer be contiguous with the type-specific header. Just passing the With the function prototypes as they are at the moment, the only thing that the function can assume access to is the In practice, it will be unlikely that we change the message layout in memory. But it's a bit sketchy for a hook implementation to be poking around past the end of the structure it was passed. (Also, as you mention, it may not be an admin header that follows; it could be a MI header, a control header, or PCIe command header in future. A hook implementation would need to check the type before safely interpreting the full header data) For making forward process here: I would think that a reasonable approach may be for us to decide (and document!) that the header data must be contiguous for all future libnvme², and include the actual header size in the hook arguments. The latter provides some indication to the hook implementation that there is valid data beyond the passed struct. An alternative would be to pass a union of all header types, but that seems a bit messy. 1: say, for more efficient message creation - which we have already done for the header/payload/MIC split |
|
So just for the sake of logging, I don't want to expose currently internal data structures. From what we've seen in the last few years, there's always someone using them, and we can't really change them easily without breaking things afterwards. |
|
Sure, that's a reasonable approach. Just to clarify though - this isn't exposing anything outside of the (currently public) API - and in this case those definitions are just the MI spec's message layout. What this is exposing is a "contract" in how we're layout-out the buffers with those messages, and specifically that the MI header and the rest of the command header (MI, Admin, Control or PCI) is contiguous. I don't think that's too onerous to stick with. if we later make those discontiguous, we have a backwards-compat "escape hatch" should we need one: we can linearise just for the hooks. But since we have no idea whether the hooks have been overridden, we would need to re-linearise in every invocation of |
4a8614a to
8564bd9
Compare
|
Changed the hook functions only for the MI message type: admin at this moment and also added the message payload data as mentioned. Thank you. |
|
Are you sure you want to take that approach? sounds like we'll need a lot of hooks there... |
8564bd9 to
4402fa5
Compare
|
@jk-ozlabs Yes right so fixed the patch then please review again. Thank you. |
|
That looks like the right approach to me, in terms of the data we're passing to the hook (and the implicit ABI around the header / data layouts). I can't speak much for the consumer side though, @igaw mentioned facilities for maintaining context across those too calls - is that still desirable? |
|
Yes, I still like to have to possibility to pass a pointer around from the call side. #983 (comment) |
About the suggestion above there
|
|
Very sorry about the above comment #983 (comment) I misunderstood as the PR mention #983 (comment) and the nvme-cli PR #2751 comments are same but actually those related but not same. So I will do consder this PR comment above again to fix. Thank you. |
4402fa5 to
2af0809
Compare
|
Fix for the comment so please review again. Thank you. |
| return resp->mic != ~crc; | ||
| } | ||
|
|
||
| __attribute__((weak)) void *nvme_mi_submit_entry(__u8 type, const void *hdr, size_t hdr_len, |
There was a problem hiding this comment.
Why the void * here? it's always at least a struct nvme_mi_msg_hdr, right?
(hook implementations can then cast according to type, if necessary...)
There was a problem hiding this comment.
Yes so fixed the void * to struct nvme_mi_msg_hdr *. Thank you.
There was a problem hiding this comment.
Yes so fixed the
void *tostruct nvme_mi_msg_hdr *. Thank you.
but now we've lost the const; is that intentional?
There was a problem hiding this comment.
No missed to remain the const so fixed the patch again.
| __attribute__((weak)) void *nvme_mi_submit_entry(__u8 type, const void *hdr, size_t hdr_len, | ||
| const void *data, size_t data_len) { return NULL; } | ||
|
|
||
| __attribute__((weak)) void nvme_mi_submit_exit(__u8 type, const void *hdr, size_t hdr_len, |
There was a problem hiding this comment.
struct nvme_mi_msg_resp for hdr here too?
There was a problem hiding this comment.
This also fixed. Thank you.
2af0809 to
c2b1b72
Compare
These are for the user to implement their own logging. Signed-off-by: Tokunori Ikegami <[email protected]>
c2b1b72 to
32dca97
Compare
|
Thanks! |
No description provided.