-
Notifications
You must be signed in to change notification settings - Fork 1.4k
nuttx/can: in trunk support to Send message cancel mechanism. #17694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
nuttx/can: in trunk support to Send message cancel mechanism. #17694
Conversation
2f1e894 to
180f5f9
Compare
|
@OceanfromXiaomi fix: |
| } | ||
| } | ||
|
|
||
| #ifdef CONFIG_CAN_TXCANCEL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OceanfromXiaomi please include some comments explaining the idea, it will help someone looking this code in the future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
|
|
||
| CODE bool (*co_txempty)(FAR struct can_dev_s *dev); | ||
|
|
||
| CODE bool (*co_cancel)(FAR struct can_dev_s *dev, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this deserves some comment in the code. I suppose cancel should abort the msg that is passed as an argument, but should it also reorganize hardware buffers sending order if they are sent to the bus in FIFO order?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not use cancel ability if lower driver enable H/W FIFO to store msg.
I will add explaination in Kconfig file.
drivers/can/Kconfig
Outdated
| depends on CAN_TXPRIORITY | ||
| ---help--- | ||
| Enabling this feature adds support for the can cancel ability. | ||
| this ability can cancel the msg with the largest msgID in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix indentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
drivers/can/can_sender.c
Outdated
| callbackmsg_node = list_last_entry(&cd_sender->tx_sending, | ||
| struct can_msg_node_s, list); | ||
| if (dev->cd_ops->co_cancel != NULL && | ||
| dev_cancel(dev, &callbackmsg_node->msg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add assert above the statement ensuring the lower half actually has dev_cancel to simplify subsequent debugging.
Or just return false as the lower half is not capable of aborting the frame. I think that's even better than assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do these as follow:
DEBUGASSERT(dev->cd_ops->co_cancel != NULL)
...
if (dev_cancel(dev, &callbackmsg_node->msg))
{
...
return true;
}
return false;
drivers/can/Kconfig
Outdated
| config CAN_TXCANCEL | ||
| bool "Implement tx cancel ability" | ||
| default n | ||
| depends on CAN_TXPRIORITY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these have to be separate configuration options? I would say strict TX priority ordering is useless without the ability to abort the frame from hardware buffer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CAN_TXCANCEL deponds on CAN_TXPRIORITY, because cancel ability will update pending list.
I will add explaination to explain cancel ability deponds on that the hardware buffer has ability of aborting frame.
| #ifdef CONFIG_CAN_TXCANCEL | ||
| if (TX_PENDING(&dev->cd_sender) && can_txneed_cancel(&dev->cd_sender)) | ||
| { | ||
| if (can_cancel_mbmsg(dev)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be in a while loop? What if you need to cancel more messages? Let's say there are two CAN frames with IDs 1 and 2 already stored in HW buffers and you want to add CAN frame with ID 0. The current design, as I understand it, would have to abort both of these frames.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just cancel the msg with the max id in sending list.
In the current design, when the hardware transmit buffer is full and there are frames in the pending list, the can_txneed_cancel function checks whether the ID of the first frame in the pending list is smaller than the minimum ID in the sending list. If this condition is met, the can_cancel_mbmsg function is invoked to attempt to cancel the transmission of the frame with the largest ID in the sending list, and this frame is then reinserted into the pending list at a specified position.
Afterwards, dev_send is called to load the first frame from the pending list into the hardware transmit buffer.
| } | ||
|
|
||
| #ifdef CONFIG_CAN_TXCANCEL | ||
| if (TX_PENDING(&dev->cd_sender) && can_txneed_cancel(&dev->cd_sender)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to cancel the messages unless hardware buffers are actually full, we just need to reorganize the order in which they are sent to the bus (not possible with all CAN controllers, but same can do it). If I am not mistaken, this approach could lead to the following scenario:
- write CAN frame with ID 5 to first HW buffer
- write CAN frame with ID 0, we check for abort
- CAN frame with ID 5 is aborted and returned back to the software FIFO
- CAN frame 0 is inserted to first HW buffer
- CAN frame 5 is inserted to second HW buffer
The ideal scenario would be something like this
- write CAN frame with ID 5 to first HW buffer
- write CAN frame with ID 0 to second HW buffer
- change the order of HW buffers so the HW buffer 2 is sent before HW buffer 1
We avoid a lot of list operations and unnecessary aborts. This becomes even more significant if more hardware buffers are used and you need to abort more frames. Yes, the implementation is more complicated and it passes some of the logic to the lower half, because dev_send has to check the priority of the newly added frame and correctly change the order in which the buffers are sent to the bus. Also the cancel has to be handled a bit differently, because now it's the lower half that decides what frame is returned back to the software FIFO.
I am not forcing the change here as this is a lot of work to do, just passing some thoughts and ideas about possible future enhancements. We used this approach in RTEMS CAN stack in my diploma thesis actually -> multiple priority queues, lower half reorganizes the sending order and returns the frame back to SW FIFO. This is something I'd like to see in NuttX in the future as well. It's probably a bit different from your needs, because you are doing strict CAN ID ordering. The problem with that is that some protocols need to keep the sending order even for different identifiers, that's where you need multiple priority classes... you filter ID ranges to these classes and then ensures the highest priority class is sent first, but the order within one class is still kept.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change does not introduce excessive linked-list operations or unnecessary abort operations.
The reasons are as follows:
The prerequisite of this PR is the following loop:
while (TX_PENDING(&dev->cd_sender) && dev_txready(dev))
When this loop finishes, it means that either the hardware transmit buffer is full, there are no more frames in the pending list, or an error occurred during dev_send.
After that, the cancel condition is evaluated:
if (TX_PENDING(&dev->cd_sender) && can_txneed_cancel(&dev->cd_sender))
Only when there are still pending frames in the pending list and the can_txneed_cancel condition is satisfied will the cancel logic be executed.
Therefore, the cancel mechanism is triggered only when necessary, avoiding excessive list operations and unnecessary abort actions.
63c1bf3 to
aef5984
Compare
Done |
Introduce a priority-based CAN TX cancel mechanism to reorder pending frames and prevent priority inversion during message transmission. Signed-off-by: zhaohaiyang1 <zhaohaiyang1@xiaomi.com>
aef5984 to
1c9a2d1
Compare
acassis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OceanfromXiaomi please update the Documentation: https://nuttx.apache.org/docs/latest/components/drivers/character/can.html to include this new functionality
add the logic about "cancel mechanism" of sending can/canfd message based priority list.
Summary
This PR is intended to resolve the CAN message priority inversion issue that can occur during CAN frame transmission.
It fundamentally addresses the problem of CAN message priority inversion, thereby avoiding communication latency or errors caused by priority inversion.
The solution manages CAN message transmission by introducing a cancel mechanism combined with a linked list, along with predefined cancel conditions (can_txneed_cancel), to control and reorder CAN frame transmission as needed.
Impact
If users want to use this scheme, they only need to enable CONFIG_CAN_TXCANCEL.
This scheme does not affect the build process, security, or compatibility.
A new dev_cancel interface is introduced, which is used to cancel the transmission of CAN messages that are already queued in the CAN driver’s hardware transmit buffer.
Testing
This scheme has been applied in an automotive basic-software project and has undergone long-term testing and validation; it does not break existing code.
The scheme also ran stably on development boards based on the Cortex-M7 architecture.
Test procedure:
Observed behavior:
the high-priority message (ID 0x111) appears on the bus first, followed by the low-priority message (ID 0x222).