vblk:vmtd:oops: doxygen comments for external API

Jira SSV-12897

Change-Id: Ie4b8d54a601a3a904e02f9f93e4ae52ae0447b3c
Signed-off-by: Sanjith T D <std@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3336860
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Sreenivas Velpula <svelpula@nvidia.com>
Reviewed-by: Vipin Kumar <vipink@nvidia.com>
This commit is contained in:
Sanjith T D
2025-04-07 11:49:46 +00:00
committed by Jon Hunter
parent 59e320fc90
commit 89e5c02ff7
4 changed files with 1628 additions and 38 deletions

View File

@@ -127,6 +127,42 @@ static int32_t wait_for_fops_completion(struct vblk_dev *vblkdev_oops, bool is_r
return retry; return retry;
} }
/**
* @defgroup oops_driver_read_write OOPSDriver::Read/Write
*
* @ingroup oops_driver_read_write
* @{
*/
/**
* @brief Read data from virtual block device
*
* Reads data from virtual block device by:
* 1. Validating read parameters and context
* 2. Calculating block position and count
* 3. Preparing read request for Storage Server
* 4. Sending request via IVC
* 5. Waiting for response and copying data
*
* @param[out] buf Buffer to read data into
* @param[in] bytes Number of bytes to read
* @param[in] pos Starting position to read from
* @return Number of bytes read on success, negative errno on failure
*
* @pre Device must be initialized and IVC channel ready
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_oops_read(char *buf, size_t bytes, loff_t pos) static ssize_t vblk_oops_read(char *buf, size_t bytes, loff_t pos)
{ {
struct vsc_request *vsc_req; struct vsc_request *vsc_req;
@@ -221,6 +257,35 @@ fail:
return -ENOMSG; return -ENOMSG;
} }
/**
* @brief Write data to virtual block device
*
* Writes data to virtual block device by:
* 1. Validating write parameters and context
* 2. Calculating block position and count
* 3. Preparing write request for Storage Server
* 4. Sending request via IVC
* 5. Waiting for response and verifying completion
*
* @param[in] buf Buffer containing data to write
* @param[in] bytes Number of bytes to write
* @param[in] pos Starting position to write to
* @return Number of bytes written on success, negative errno on failure
*
* @pre Device must be initialized and IVC channel ready
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_oops_write(const char *buf, size_t bytes, static ssize_t vblk_oops_write(const char *buf, size_t bytes,
loff_t pos) loff_t pos)
{ {
@@ -241,7 +306,8 @@ static ssize_t vblk_oops_write(const char *buf, size_t bytes,
*/ */
if (in_atomic()) { if (in_atomic()) {
dev_warn(vblkdev_oops->device, dev_warn(vblkdev_oops->device,
"%s invoked in atomic context..aborting\n", __func__); "%s invoked in atomic context..returning EBUSY to retry from workqueue\n",
__func__);
return -EBUSY; return -EBUSY;
} }
@@ -334,6 +400,35 @@ fail:
* - no need to check for VSC response. Send request and assume it is all good * - no need to check for VSC response. Send request and assume it is all good
* since the caller is not going to do anything meaningful if we report error * since the caller is not going to do anything meaningful if we report error
*/ */
/**
* @brief Write data to virtual block device during panic
*
* Best-effort write during system panic by:
* 1. Validating basic parameters
* 2. Calculating block position and count
* 3. Preparing write request for Storage Server
* 4. Sending request via IVC without waiting for response
*
* @param[in] buf Buffer containing data to write
* @param[in] bytes Number of bytes to write
* @param[in] pos Starting position to write to
* @return Number of bytes written on success, 0 on failure
*
* @pre Device must be initialized, system in panic state
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: Yes
* - Signal handler: Yes
* - Thread-safe: No
* - Async/Sync: Async
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_oops_panic_write(const char *buf, size_t bytes, static ssize_t vblk_oops_panic_write(const char *buf, size_t bytes,
loff_t pos) loff_t pos)
{ {
@@ -415,6 +510,7 @@ static ssize_t vblk_oops_panic_write(const char *buf, size_t bytes,
*/ */
return bytes; return bytes;
} }
/** @} */
/* Set up virtual device. */ /* Set up virtual device. */
static void setup_device(struct vblk_dev *vblkdev) static void setup_device(struct vblk_dev *vblkdev)
@@ -683,6 +779,39 @@ static int vblk_oops_get_configinfo(struct vblk_dev *vblkdev)
return 0; return 0;
} }
/**
* @defgroup oops_driver_request_handler OOPSDriver::Request Handler
*
* @ingroup oops_driver_request_handler
* @{
*/
/**
* @brief Initialize the OOPS virtual block device
*
* Initializes the virtual block device by:
* 1. Checking if IVC channel reset is complete
* 2. If reset complete and data can be read from IVC channel:
* - Gets device configuration from Storage Server
* - Sets up device parameters
* - Registers with pstore_zone
*
* @param[in] ws Work structure pointer containing device info
* @return None
*
* @pre Device structure must be allocated and work structure initialized
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Async
* - Re-entrant: No
* - API Group
* - Init: Yes
* - Runtime: No
* - De-Init: No
*/
static void vblk_oops_init_device(struct work_struct *ws) static void vblk_oops_init_device(struct work_struct *ws)
{ {
struct vblk_dev *vblkdev = container_of(ws, struct vblk_dev, init.work); struct vblk_dev *vblkdev = container_of(ws, struct vblk_dev, init.work);
@@ -709,6 +838,40 @@ static void vblk_oops_init_device(struct work_struct *ws)
} }
} }
/**@} */
/**
* @defgroup oops_driver_probe OOPSDriver::Probe
*
* @ingroup oops_driver_probe
* @{
*/
/**
* @brief Probe function to initialize OOPS storage device driver
*
* Sets up virtual block device for storing kernel crash dumps by:
* - Allocating device structure
* - Reading device tree properties
* - Setting up IVC channel
* - Configuring pstore parameters
*
* @param[in] pdev Platform device pointer from device tree
* @return 0 on success, negative errno on failure
*
* @pre Platform must be running in virtualized environment with hypervisor
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: Yes
* - Runtime: No
* - De-Init: No
*/
static int tegra_hv_vblk_oops_probe(struct platform_device *pdev) static int tegra_hv_vblk_oops_probe(struct platform_device *pdev)
{ {
static struct device_node *vblk_node; static struct device_node *vblk_node;
@@ -804,6 +967,8 @@ fail:
return ret; return ret;
} }
/**@} */
static int tegra_hv_vblk_oops_remove(struct platform_device *pdev) static int tegra_hv_vblk_oops_remove(struct platform_device *pdev)
{ {
struct vblk_dev *vblkdev = platform_get_drvdata(pdev); struct vblk_dev *vblkdev = platform_get_drvdata(pdev);
@@ -815,7 +980,36 @@ static int tegra_hv_vblk_oops_remove(struct platform_device *pdev)
return 0; return 0;
} }
/**
* @defgroup oops_driver_suspend_resume OOPSDriver::Suspend/Resume
*
* @ingroup oops_driver_suspend_resume
* @{
*/
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/**
* @brief Suspend the OOPS storage device
*
* Resets the IVC channel during system suspend.
*
* @param[in] dev Device structure pointer
* @return 0 on success
*
* @pre Device must be initialized and active
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int tegra_hv_vblk_oops_suspend(struct device *dev) static int tegra_hv_vblk_oops_suspend(struct device *dev)
{ {
/* Reset the channel */ /* Reset the channel */
@@ -826,6 +1020,28 @@ static int tegra_hv_vblk_oops_suspend(struct device *dev)
return 0; return 0;
} }
/**
* @brief Resume the OOPS storage device
*
* Waits for IVC channel reset completion during system resume.
*
* @param[in] dev Device structure pointer
* @return 0 on success, -EIO on timeout
*
* @pre Device must be in suspended state
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int tegra_hv_vblk_oops_resume(struct device *dev) static int tegra_hv_vblk_oops_resume(struct device *dev)
{ {
int i = 0; int i = 0;
@@ -841,12 +1057,15 @@ static int tegra_hv_vblk_oops_resume(struct device *dev)
return 0; return 0;
} }
static const struct dev_pm_ops tegra_hv_vblk_oops_pm_ops = { static const struct dev_pm_ops tegra_hv_vblk_oops_pm_ops = {
.suspend_noirq = tegra_hv_vblk_oops_suspend, .suspend_noirq = tegra_hv_vblk_oops_suspend,
.resume_noirq = tegra_hv_vblk_oops_resume, .resume_noirq = tegra_hv_vblk_oops_resume,
}; };
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
/** @} */
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id tegra_hv_vblk_oops_match[] = { static const struct of_device_id tegra_hv_vblk_oops_match[] = {
{ .compatible = "nvidia,tegra-hv-oops-storage", }, { .compatible = "nvidia,tegra-hv-oops-storage", },
@@ -855,6 +1074,34 @@ static const struct of_device_id tegra_hv_vblk_oops_match[] = {
MODULE_DEVICE_TABLE(of, tegra_hv_vblk_oops_match); MODULE_DEVICE_TABLE(of, tegra_hv_vblk_oops_match);
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
/**
* @defgroup oops_driver_module_init_exit OOPSDriver::Module Init/Exit
*
* @ingroup oops_driver_module_init_exit
* @{
*/
/**
* @brief Function for OOPS device removal
*
* Unreserves the IVC channel and mempool during device removal.
*
* @param[in] pdev Platform device pointer
* @return void or int depending on kernel version
*
* @pre Device must have been previously probed successfully
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: No
* - De-Init: Yes
*/
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */ #if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
static void tegra_hv_vblk_oops_remove_wrapper(struct platform_device *pdev) static void tegra_hv_vblk_oops_remove_wrapper(struct platform_device *pdev)
{ {
@@ -880,18 +1127,62 @@ static struct platform_driver tegra_hv_vblk_oops_driver = {
}, },
}; };
/**
* @brief Initialize the Tegra Hypervisor Virtual OOPS Block Device driver
*
* Initializes the virtual block device driver for storing kernel crash dumps.
* Registers the platform driver for OOPS storage functionality.
*
* @return 0 on success, negative errno on failure
*
* @pre None
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: Yes
* - Runtime: No
* - De-Init: No
*/
static int __init tegra_hv_vblk_driver_init(void) static int __init tegra_hv_vblk_driver_init(void)
{ {
return platform_driver_register(&tegra_hv_vblk_oops_driver); return platform_driver_register(&tegra_hv_vblk_oops_driver);
} }
module_init(tegra_hv_vblk_driver_init); module_init(tegra_hv_vblk_driver_init);
/**
* @brief Cleanup and remove the Tegra Hypervisor Virtual OOPS Block Device driver
*
* Unregisters the platform driver for OOPS storage functionality.
*
* @return None
*
* @pre Driver must have been initialized via tegra_hv_vblk_driver_init()
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: No
* - De-Init: Yes
*/
static void __exit tegra_hv_vblk_driver_exit(void) static void __exit tegra_hv_vblk_driver_exit(void)
{ {
platform_driver_unregister(&tegra_hv_vblk_oops_driver); platform_driver_unregister(&tegra_hv_vblk_oops_driver);
} }
module_exit(tegra_hv_vblk_driver_exit); module_exit(tegra_hv_vblk_driver_exit);
/** @} */
MODULE_AUTHOR("Haribabu Narayanan <hnarayanan@nvidia.com>"); MODULE_AUTHOR("Haribabu Narayanan <hnarayanan@nvidia.com>");
MODULE_DESCRIPTION("Virtual OOPS storage device over Tegra Hypervisor IVC channel"); MODULE_DESCRIPTION("Virtual OOPS storage device over Tegra Hypervisor IVC channel");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@@ -191,7 +191,42 @@ static int vblk_common_ioctl(struct vblk_dev *vblkdev, fmode_t mode,
return ret; return ret;
} }
/**
* @defgroup vscd_ioctl LinuxVSCD::IOCTL
*
* @ingroup vscd_ioctl
* @{
*/
/**
* @brief Handles IOCTL commands for firmware update operations
*
* This function processes IOCTL commands specifically for firmware updates.
* It temporarily enables FFU passthrough command permissions, processes the IOCTL,
* then disables FFU permissions again. This provides controlled access to firmware
* update capabilities through a dedicated interface.
*
* @param[in] bdev Pointer to the block device structure
* @param[in] mode File mode flags
* @param[in] cmd IOCTL command code
* @param[in] arg Command-specific argument
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Block device must be opened through FFU interface
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
int vblk_ffu_ioctl(struct block_device *bdev, fmode_t mode, int vblk_ffu_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
@@ -207,7 +242,37 @@ int vblk_ffu_ioctl(struct block_device *bdev, fmode_t mode,
return ret; return ret;
} }
/* The ioctl() implementation */ /**
* @brief Handles IOCTL commands for normal block device operations
*
* This function processes IOCTL commands for the block device, supporting:
* - MMC IOC commands (single and multi)
* - SCSI generic (SG_IO) commands
* - UFS combo query commands
* The function validates permissions and delegates to specific handlers.
*
* @param[in] bdev Pointer to the block device structure
* @param[in] mode File mode flags
* @param[in] cmd IOCTL command code
* @param[in] arg Command-specific argument
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Block device must be opened
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
int vblk_ioctl(struct block_device *bdev, fmode_t mode, int vblk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
@@ -221,7 +286,35 @@ int vblk_ioctl(struct block_device *bdev, fmode_t mode,
return ret; return ret;
} }
/* The ioctl() implementation for block device node */ /**
* @brief Handles IOCTL commands for non-control device nodes
*
* This function rejects IOCTL commands that are only supported on the control device node.
* It returns -ENOTTY for MMC, SCSI, and UFS commands when called on regular block device nodes.
* This enforces access control by requiring privileged operations to use the control node.
*
* @param[in] bdev Pointer to the block device structure
* @param[in] mode File mode flags
* @param[in] cmd IOCTL command code
* @param[in] arg Command-specific argument
* @return -ENOTTY to indicate command is not supported
*
* @pre
* - Device must be initialized
* - Block device must be opened
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
int vblk_ioctl_not_supported(struct block_device *bdev, fmode_t mode, int vblk_ioctl_not_supported(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
@@ -245,3 +338,4 @@ int vblk_ioctl_not_supported(struct block_device *bdev, fmode_t mode,
return ret; return ret;
} }
/** @} */

View File

@@ -755,6 +755,47 @@ bio_exit:
return false; return false;
} }
/**
* @defgroup vscd_request_worker LinuxVSCD::Request Worker
*
* @ingroup vscd_request_worker
* @{
*/
/**
* @brief Worker thread that handles block device requests and completions
*
* This worker thread is responsible for:
* 1. Processing completed block I/O requests from the virtual storage server
* 2. Submitting new block I/O requests to the virtual storage server
* 3. Managing the request queue and IVC communication
*
* The worker runs in a loop waiting for requests to be queued. When woken up, it:
* - Acquires the IVC lock to synchronize access to the IVC channel
* - Processes any completed requests from the server via complete_bio_req()
* - Submits new pending requests to the server via submit_bio_req()
* - Continues processing until no more requests are pending
*
* @param[in] data Pointer to the vblk_dev device structure
* @param[out] None
* @return 0 on success, negative errno on failure
*
* @pre
* - vblk device must be initialized
* - IVC channel must be established
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Async
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int vblk_request_worker(void *data) static int vblk_request_worker(void *data)
{ {
struct vblk_dev *vblkdev = (struct vblk_dev *)data; struct vblk_dev *vblkdev = (struct vblk_dev *)data;
@@ -782,7 +823,43 @@ static int vblk_request_worker(void *data)
return 0; return 0;
} }
/* The simple form of the request function. */ /**
* @brief Block request handler callback for multi-queue block device
*
* This function is the main request handler for the virtual block device driver.
* When the block layer submits I/O requests, this callback:
* 1. Marks the request as started using blk_mq_start_request()
* 2. Allocates a new request entry structure
* 3. Adds the request to the device's pending request list
* 4. Wakes up the worker thread to process the request
*
* The actual I/O processing is done asynchronously by the worker thread, which:
* - Submits requests to the virtual storage server via IVC
* - Handles completions and error cases
* - Manages the request lifecycle
*
* @param[in] hctx Block multi-queue hardware context
* @param[in] bd Block request data containing the request to process
*
* @return BLK_STS_OK on success, BLK_STS_IOERR on failure
*
* @pre
* - Block device must be initialized
* - Request queue must be setup
* - Worker thread must be running
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Async
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static blk_status_t vblk_request(struct blk_mq_hw_ctx *hctx, static blk_status_t vblk_request(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd) const struct blk_mq_queue_data *bd)
{ {
@@ -813,8 +890,43 @@ static blk_status_t vblk_request(struct blk_mq_hw_ctx *hctx,
return BLK_STS_OK; return BLK_STS_OK;
} }
/** @} */
/* Open and release */ /* Open and release */
/**
* @defgroup vscd_open_release LinuxVSCD::Open/Release
*
* @ingroup vscd_open_release
* @{
*/
/**
* @brief Opens a virtual block device for normal I/O operations
*
* This function is called when a block device is opened for I/O operations.
* It increments the user count and checks for media changes if this is the first user.
* The media change check ensures the device state is current before allowing access.
*
* @param[in] disk Pointer to the gendisk structure representing the block device
* @param[in] mode File mode flags indicating how the device should be opened
* @return 0 on success
*
* @pre
* - Device must be initialized
* - Disk structure must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
#if defined(NV_BLOCK_DEVICE_OPERATIONS_OPEN_HAS_GENDISK_ARG) /* Linux v6.5 */ #if defined(NV_BLOCK_DEVICE_OPERATIONS_OPEN_HAS_GENDISK_ARG) /* Linux v6.5 */
static int vblk_open(struct gendisk *disk, fmode_t mode) static int vblk_open(struct gendisk *disk, fmode_t mode)
{ {
@@ -839,6 +951,33 @@ static int vblk_open(struct block_device *device, fmode_t mode)
return 0; return 0;
} }
/**
* @brief Opens a virtual block device for IOCTL operations
*
* This function is called when opening the IOCTL-specific device node.
* It increments the IOCTL user count, checks for media changes if first user,
* and initializes FFU passthrough command permissions to disabled state.
*
* @param[in] disk Pointer to the gendisk structure representing the block device
* @param[in] mode File mode flags indicating how the device should be opened
* @return 0 on success
*
* @pre
* - Device must be initialized
* - Disk structure must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
#if defined(NV_BLOCK_DEVICE_OPERATIONS_OPEN_HAS_GENDISK_ARG) /* Linux v6.5 */ #if defined(NV_BLOCK_DEVICE_OPERATIONS_OPEN_HAS_GENDISK_ARG) /* Linux v6.5 */
static int vblk_ioctl_open(struct gendisk *disk, fmode_t mode) static int vblk_ioctl_open(struct gendisk *disk, fmode_t mode)
{ {
@@ -864,6 +1003,33 @@ static int vblk_ioctl_open(struct block_device *device, fmode_t mode)
return 0; return 0;
} }
/**
* @brief Opens a virtual block device for firmware update operations
*
* This function is called when opening the FFU-specific device node.
* It increments the FFU user count, checks for media changes if first user,
* and initializes FFU passthrough command permissions to disabled state.
*
* @param[in] disk Pointer to the gendisk structure representing the block device
* @param[in] mode File mode flags indicating how the device should be opened
* @return 0 on success
*
* @pre
* - Device must be initialized
* - Disk structure must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
#if defined(NV_BLOCK_DEVICE_OPERATIONS_OPEN_HAS_GENDISK_ARG) #if defined(NV_BLOCK_DEVICE_OPERATIONS_OPEN_HAS_GENDISK_ARG)
static int vblk_ffu_open(struct gendisk *disk, fmode_t mode) static int vblk_ffu_open(struct gendisk *disk, fmode_t mode)
{ {
@@ -887,6 +1053,33 @@ static int vblk_ffu_open(struct block_device *device, fmode_t mode)
return 0; return 0;
} }
/**
* @brief Releases a virtual block device after IOCTL operations
*
* This function is called when closing the IOCTL-specific device node.
* It safely decrements the IOCTL user count using overflow checking to prevent underflow.
* The IOCTL interface remains available for other users if count is non-zero.
*
* @param[in] disk Pointer to the gendisk structure representing the block device
* @param[in] mode File mode flags indicating how the device was opened
*
* @pre
* - Device must be initialized
* - Disk structure must be valid
* - Device must have been previously opened for IOCTL operations
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: Yes
*/
#if defined(NV_BLOCK_DEVICE_OPERATIONS_RELEASE_HAS_NO_MODE_ARG) /* Linux v6.5 */ #if defined(NV_BLOCK_DEVICE_OPERATIONS_RELEASE_HAS_NO_MODE_ARG) /* Linux v6.5 */
static void vblk_ioctl_release(struct gendisk *disk) static void vblk_ioctl_release(struct gendisk *disk)
#else #else
@@ -906,6 +1099,33 @@ static void vblk_ioctl_release(struct gendisk *disk, fmode_t mode)
spin_unlock(&vblkdev->lock); spin_unlock(&vblkdev->lock);
} }
/**
* @brief Releases a virtual block device after firmware update operations
*
* This function is called when closing the FFU-specific device node.
* It safely decrements the FFU user count using overflow checking to prevent underflow.
* The FFU interface remains available for other users if count is non-zero.
*
* @param[in] disk Pointer to the gendisk structure representing the block device
* @param[in] mode File mode flags indicating how the device was opened
*
* @pre
* - Device must be initialized
* - Disk structure must be valid
* - Device must have been previously opened for FFU operations
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: Yes
*/
#if defined(NV_BLOCK_DEVICE_OPERATIONS_RELEASE_HAS_NO_MODE_ARG) #if defined(NV_BLOCK_DEVICE_OPERATIONS_RELEASE_HAS_NO_MODE_ARG)
static void vblk_ffu_release(struct gendisk *disk) static void vblk_ffu_release(struct gendisk *disk)
#else #else
@@ -921,7 +1141,33 @@ static void vblk_ffu_release(struct gendisk *disk, fmode_t mode)
spin_unlock(&vblkdev->lock); spin_unlock(&vblkdev->lock);
} }
/**
* @brief Releases a virtual block device after normal I/O operations
*
* This function is called when a block device is closed after I/O operations.
* It safely decrements the user count using overflow checking to prevent underflow.
* The device remains operational for other users if the count is non-zero.
*
* @param[in] disk Pointer to the gendisk structure representing the block device
* @param[in] mode File mode flags indicating how the device was opened
*
* @pre
* - Device must be initialized
* - Disk structure must be valid
* - Device must have been previously opened
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: Yes
*/
#if defined(NV_BLOCK_DEVICE_OPERATIONS_RELEASE_HAS_NO_MODE_ARG) /* Linux v6.5 */ #if defined(NV_BLOCK_DEVICE_OPERATIONS_RELEASE_HAS_NO_MODE_ARG) /* Linux v6.5 */
static void vblk_release(struct gendisk *disk) static void vblk_release(struct gendisk *disk)
#else #else
@@ -941,6 +1187,33 @@ static void vblk_release(struct gendisk *disk, fmode_t mode)
spin_unlock(&vblkdev->lock); spin_unlock(&vblkdev->lock);
} }
/**
* @brief Gets the geometry information for the virtual block device
*
* This function returns the logical geometry (heads, sectors, cylinders) for the block device.
* It uses fixed values for heads and sectors, and calculates cylinders based on device capacity.
* This information is used by legacy tools that expect disk geometry information.
*
* @param[in] device Pointer to the block device structure
* @param[out] geo Pointer to hd_geometry structure to fill with geometry information
* @return 0 on success
*
* @pre
* - Device must be initialized
* - Block device structure must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int vblk_getgeo(struct block_device *device, struct hd_geometry *geo) static int vblk_getgeo(struct block_device *device, struct hd_geometry *geo)
{ {
geo->heads = VS_LOG_HEADS; geo->heads = VS_LOG_HEADS;
@@ -950,6 +1223,7 @@ static int vblk_getgeo(struct block_device *device, struct hd_geometry *geo)
return 0; return 0;
} }
/** @} */
/* The device operations structure. */ /* The device operations structure. */
static const struct block_device_operations vblk_ops_no_ioctl = { static const struct block_device_operations vblk_ops_no_ioctl = {
@@ -978,8 +1252,44 @@ static const struct block_device_operations vblk_ops_ffu = {
.ioctl = vblk_ffu_ioctl .ioctl = vblk_ffu_ioctl
}; };
static ssize_t /**
vblk_phys_dev_show(struct device *dev, struct device_attribute *attr, * @defgroup vscd_sysfs LinuxVSCD::Sysfs
*
* @ingroup vscd_sysfs
* @{
*/
/**
* @brief Shows the physical device type (EMMC/UFS) backing this virtual block device
*
* This function implements the sysfs show function for the "phys_dev" attribute.
* It reads the physical device type from the vblk device configuration and returns
* a string indicating whether the underlying storage device is EMMC or UFS.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to the device attribute structure
* @param[out] buf Buffer to store the attribute value string
*
* @return Number of bytes written to buffer on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Configuration must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_phys_dev_show(struct device *dev,
struct device_attribute *attr,
char *buf) char *buf)
{ {
struct gendisk *disk = dev_to_disk(dev); struct gendisk *disk = dev_to_disk(dev);
@@ -993,8 +1303,38 @@ vblk_phys_dev_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, 16, "Unknown\n"); return snprintf(buf, 16, "Unknown\n");
} }
static ssize_t /**
vblk_phys_base_show(struct device *dev, struct device_attribute *attr, * @brief Shows the physical base address of the storage device
*
* This function implements the sysfs show function for the "phys_base" attribute.
* It reads the physical base address from the vblk device configuration and formats
* it as a hexadecimal string. This represents the base address of the physical
* storage device in memory.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to the device attribute structure
* @param[out] buf Buffer to store the attribute value string
*
* @return Number of bytes written to buffer on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Configuration must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_phys_base_show(struct device *dev,
struct device_attribute *attr,
char *buf) char *buf)
{ {
struct gendisk *disk = dev_to_disk(dev); struct gendisk *disk = dev_to_disk(dev);
@@ -1003,8 +1343,38 @@ vblk_phys_base_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, 16, "0x%llx\n", vblk->config.phys_base); return snprintf(buf, 16, "0x%llx\n", vblk->config.phys_base);
} }
static ssize_t /**
vblk_storage_type_show(struct device *dev, struct device_attribute *attr, * @brief Shows the storage type/partition (RPMB, BOOT, LUNx) of this virtual block device
*
* This function implements the sysfs show function for the "storage_type" attribute.
* It reads the storage type from the vblk device configuration and returns a string
* indicating the type of storage partition this device represents (e.g. RPMB, BOOT,
* or one of the logical unit numbers LUN0-LUN7).
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to the device attribute structure
* @param[out] buf Buffer to store the attribute value string
*
* @return Number of bytes written to buffer on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Configuration must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_storage_type_show(struct device *dev,
struct device_attribute *attr,
char *buf) char *buf)
{ {
struct gendisk *disk = dev_to_disk(dev); struct gendisk *disk = dev_to_disk(dev);
@@ -1038,8 +1408,38 @@ vblk_storage_type_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, 16, "Unknown\n"); return snprintf(buf, 16, "Unknown\n");
} }
static ssize_t /**
vblk_speed_mode_show(struct device *dev, struct device_attribute *attr, * @brief Shows the speed mode configuration of the storage device
*
* This function implements the sysfs show function for the "speed_mode" attribute.
* It reads the speed mode string from the vblk device configuration and returns it.
* The speed mode indicates the operating speed configuration of the underlying
* physical storage device (e.g. HS400, HS200 for eMMC or HS-G3 for UFS).
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to the device attribute structure
* @param[out] buf Buffer to store the attribute value string
*
* @return Number of bytes written to buffer on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Configuration must be valid
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vblk_speed_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf) char *buf)
{ {
struct gendisk *disk = dev_to_disk(dev); struct gendisk *disk = dev_to_disk(dev);
@@ -1068,6 +1468,7 @@ static const struct blk_mq_ops vblk_mq_ops = {
.queue_rq = vblk_request, .queue_rq = vblk_request,
}; };
/** @} */
#if (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ)) #if (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ))
/* Error report injection test support is included */ /* Error report injection test support is included */
@@ -1874,6 +2275,51 @@ static void vblk_init_device(struct work_struct *ws)
mutex_unlock(&vblkdev->ivc_lock); mutex_unlock(&vblkdev->ivc_lock);
} }
/**
* @defgroup vscd_irq_timer LinuxVSCD::IRQ/Timer
*
* @ingroup vscd_irq_timer
* @{
*/
/**
* @brief Interrupt handler for IVC (Inter-VM Communication) events
*
* This function handles interrupts from the IVC channel for the virtual block device.
* It performs two key tasks depending on device initialization state:
*
* 1. For initialized devices:
* - Wakes up the worker thread by completing the worker completion
* - Worker thread then processes any pending IVC messages/requests
*
* 2. For uninitialized devices:
* - Schedules initialization work on appropriate CPU
* - CPU selection based on schedulable_vcpu_number if specified, else uses default CPU
* - Initialization includes setting up device config, queues and other resources
*
* The handler ensures proper synchronization between interrupt context and worker thread
* for processing IVC messages.
*
* @param[in] irq Interrupt number
* @param[in] data Pointer to the virtual block device structure (struct vblk_dev)
* @return IRQ_HANDLED to indicate interrupt was processed
*
* @pre
* - IVC channel must be configured
* - Device structure must be allocated and basic init done
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: Yes
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Async
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static irqreturn_t ivc_irq_handler(int irq, void *data) static irqreturn_t ivc_irq_handler(int irq, void *data)
{ {
struct vblk_dev *vblkdev = (struct vblk_dev *)data; struct vblk_dev *vblkdev = (struct vblk_dev *)data;
@@ -1889,6 +2335,49 @@ static irqreturn_t ivc_irq_handler(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/**
* @brief Timer callback function to handle block I/O request timeouts
*
* This function is called when a block I/O request timer expires, indicating that
* the request has taken longer than expected to complete. It handles timeout
* conditions by:
* 1. Converting the timer structure back to the request structure using container_of
* 2. Logging an error message with:
* - The request ID that timed out
* - Current system counter value
* - Original request schedule time
* This helps identify stuck or slow I/O requests for debugging purposes.
*
* @param[in] timer Pointer to the timer_list structure that expired
*
* @pre
* - Timer must be initialized and started for a valid vsc_request
* - vblkdev device structure must be valid
* - Request must be in-flight when timer expires
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: Yes
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Async
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static void bio_request_timeout_callback(struct timer_list *timer)
{
struct vsc_request *req = from_timer(req, timer, timer);
dev_err(req->vblkdev->device, "Request id %d timed out. curr ctr: %llu sched ctr: %llu\n",
req->id, _arch_counter_get_cntvct(), req->time);
}
/** @} */
static void vblk_request_config(struct work_struct *ws) static void vblk_request_config(struct work_struct *ws)
{ {
struct vblk_dev *vblkdev = container_of(ws, struct vblk_dev, rq_cfg); struct vblk_dev *vblkdev = container_of(ws, struct vblk_dev, rq_cfg);
@@ -1922,15 +2411,6 @@ free_ivc:
tegra_hv_ivc_unreserve(vblkdev->ivck); tegra_hv_ivc_unreserve(vblkdev->ivck);
} }
static void bio_request_timeout_callback(struct timer_list *timer)
{
struct vsc_request *req = from_timer(req, timer, timer);
dev_err(req->vblkdev->device, "Request id %d timed out. curr ctr: %llu sched ctr: %llu\n",
req->id, _arch_counter_get_cntvct(), req->time);
}
static void tegra_create_timers(struct vblk_dev *vblkdev) static void tegra_create_timers(struct vblk_dev *vblkdev)
{ {
uint32_t i; uint32_t i;
@@ -1940,6 +2420,45 @@ static void tegra_create_timers(struct vblk_dev *vblkdev)
} }
/**
* @defgroup vscd_probe_remove LinuxVSCD::Probe/Remove
*
* @ingroup vscd_probe_remove
* @{
*/
/**
* @brief Probe function to initialize and setup virtual block device driver
*
* This function initializes a virtual block device that provides virtualized storage access
* in a hypervisor environment. It performs the following key steps:
* 1. Allocates and initializes the virtual block device structure
* 2. Sets up IVC (Inter-VM Communication) channel for storage commands
* 3. Configures the block device parameters like size, operations etc.
* 4. Creates block device nodes:
* - Main block device for regular I/O
* - IOCTL device node for control operations
* - FFU (Firmware Field Update) device node
* 5. Initializes request queues and work queues
* 6. Sets up device attributes
*
* @param[in] dev Platform device pointer from device tree
* @return 0 on success, negative errno on failure
*
* @pre Platform must be running in virtualized environment with hypervisor
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: Yes
* - Runtime: No
* - De-Init: No
*/
static int tegra_hv_vblk_probe(struct platform_device *pdev) static int tegra_hv_vblk_probe(struct platform_device *pdev)
{ {
static struct device_node *vblk_node; static struct device_node *vblk_node;
@@ -2034,6 +2553,34 @@ fail:
return ret; return ret;
} }
/**
* @brief Remove function to cleanup and remove virtual block device driver
*
* This function performs cleanup when the virtual block device is removed.
* Key cleanup steps include:
* 1. Waits for pending requests to complete
* 2. Removes block device nodes (main, ioctl and ffu)
* 3. Cleans up request queues
* 4. Frees IVC channels and shared memory
* 5. Frees device structures and resources
*
* @param[in] dev Platform device pointer for the device being removed
* @return 0 on success, negative errno on failure
*
* @pre Device must have been previously probed successfully
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: No
* - De-Init: Yes
*/
static int tegra_hv_vblk_remove(struct platform_device *pdev) static int tegra_hv_vblk_remove(struct platform_device *pdev)
{ {
struct vblk_dev *vblkdev = platform_get_drvdata(pdev); struct vblk_dev *vblkdev = platform_get_drvdata(pdev);
@@ -2079,7 +2626,50 @@ static int tegra_hv_vblk_remove(struct platform_device *pdev)
return 0; return 0;
} }
/** @} */
/**
* @defgroup vscd_suspend_resume LinuxVSCD::Suspend/Resume
*
* @ingroup vscd_suspend_resume
* @{
*/
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/**
* @brief Suspends the virtual block device driver
*
* This function handles the suspend operation for the virtual block device by:
* 1. Stopping hardware request queues for both regular I/O and IOCTL operations
* 2. Setting queue state to suspended
* 3. Waiting for any inflight requests to complete
* 4. Disabling IVC interrupts once queue is empty
*
* The function ensures clean suspension by:
* - Using spinlocks to safely stop queues
* - Tracking inflight requests via completion mechanism
* - Properly handling both regular and IOCTL queues
* - Disabling interrupts only after all requests complete
*
* @param[in] dev Pointer to device structure
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Driver must be in active state
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int tegra_hv_vblk_suspend(struct device *dev) static int tegra_hv_vblk_suspend(struct device *dev)
{ {
struct vblk_dev *vblkdev = dev_get_drvdata(dev); struct vblk_dev *vblkdev = dev_get_drvdata(dev);
@@ -2111,6 +2701,41 @@ static int tegra_hv_vblk_suspend(struct device *dev)
return 0; return 0;
} }
/**
* @brief Resumes the virtual block device driver
*
* This function handles the resume operation for the virtual block device by:
* 1. Setting queue state back to active
* 2. Reinitializing completion tracking
* 3. Re-enabling IVC interrupts
* 4. Restarting hardware request queues for both regular I/O and IOCTL operations
* 5. Waking up worker thread to process any pending requests
*
* The function ensures clean resume by:
* - Using spinlocks to safely restart queues
* - Properly handling both regular and IOCTL queues
* - Re-enabling interrupts before processing requests
* - Signaling worker thread to check for pending work
*
* @param[in] dev Pointer to device structure
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - Driver must be in suspended state
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int tegra_hv_vblk_resume(struct device *dev) static int tegra_hv_vblk_resume(struct device *dev)
{ {
struct vblk_dev *vblkdev = dev_get_drvdata(dev); struct vblk_dev *vblkdev = dev_get_drvdata(dev);
@@ -2141,6 +2766,8 @@ static int tegra_hv_vblk_resume(struct device *dev)
return 0; return 0;
} }
/** @} */
static const struct dev_pm_ops tegra_hv_vblk_pm_ops = { static const struct dev_pm_ops tegra_hv_vblk_pm_ops = {
.suspend = tegra_hv_vblk_suspend, .suspend = tegra_hv_vblk_suspend,
.resume = tegra_hv_vblk_resume, .resume = tegra_hv_vblk_resume,
@@ -2180,6 +2807,40 @@ static struct platform_driver tegra_hv_vblk_driver = {
}, },
}; };
/**
* @defgroup vscd_module_init_exit LinuxVSCD::Module Init/Exit
*
* @ingroup vscd_module_init_exit
* @{
*/
/**
* @brief Initialize the Tegra Hypervisor Virtual Block Device driver
*
* This function initializes the virtual block device driver that enables storage
* virtualization in Tegra Hypervisor environments. It performs the following:
* - Registers the block device driver with the kernel
* - Allocates major number for block devices
* - Initializes the virtual block device framework
* - Sets up IVC (Inter-VM Communication) channels for storage operations
* - Creates sysfs entries for device attributes
*
* @return 0 on success, negative errno on failure
*
* @pre None
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: Yes
* - Runtime: No
* - De-Init: No
*/
static int __init tegra_hv_vblk_driver_init(void) static int __init tegra_hv_vblk_driver_init(void)
{ {
vblk_major = 0; vblk_major = 0;
@@ -2193,12 +2854,42 @@ static int __init tegra_hv_vblk_driver_init(void)
} }
module_init(tegra_hv_vblk_driver_init); module_init(tegra_hv_vblk_driver_init);
/**
* @brief Cleanup and remove the Tegra Hypervisor Virtual Block Device driver
*
* This function performs cleanup when the virtual block device driver is removed.
* It handles:
* - Unregistering the block device driver
* - Freeing allocated major number
* - Cleaning up IVC channels
* - Removing sysfs entries
* - Freeing allocated resources
*
* @return None
*
* @pre Driver must have been initialized via tegra_hv_vblk_driver_init()
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: No
* - De-Init: Yes
*/
static void __exit tegra_hv_vblk_driver_exit(void) static void __exit tegra_hv_vblk_driver_exit(void)
{ {
unregister_blkdev(vblk_major, "vblk"); unregister_blkdev(vblk_major, "vblk");
platform_driver_unregister(&tegra_hv_vblk_driver); platform_driver_unregister(&tegra_hv_vblk_driver);
} }
module_exit(tegra_hv_vblk_driver_exit); module_exit(tegra_hv_vblk_driver_exit);
/**
* @}
*/
MODULE_AUTHOR("Dilan Lee <dilee@nvidia.com>"); MODULE_AUTHOR("Dilan Lee <dilee@nvidia.com>");
MODULE_DESCRIPTION("Virtual storage device over Tegra Hypervisor IVC channel"); MODULE_DESCRIPTION("Virtual storage device over Tegra Hypervisor IVC channel");

View File

@@ -69,6 +69,46 @@ static inline struct vmtd_dev *mtd_to_vmtd(struct mtd_info *mtd)
return container_of(mtd, struct vmtd_dev, mtd); return container_of(mtd, struct vmtd_dev, mtd);
} }
/**
* @defgroup vscd_mtd_irq_timer LinuxMtdVSCD::IRQ/Timer
*
* @ingroup vscd_mtd_irq_timer
* @{
*/
/**
* @brief Interrupt handler for IVC (Inter-VM Communication) channel
*
* This function serves as the interrupt service routine for the IVC channel.
* When an IVC interrupt occurs, it:
* 1. Signals completion of an IVC transaction by calling complete()
* 2. Wakes up any threads waiting on IVC communication
* 3. Enables further IVC communication to proceed
*
* The handler is essential for the asynchronous nature of IVC communication,
* allowing the driver to efficiently handle command/response sequences without
* busy waiting.
*
* @param[in] irq The interrupt number being handled
* @param[in] data Pointer to the vmtd_dev structure (passed as void*)
* @return IRQ_HANDLED indicating successful handling of the interrupt
*
* @pre
* - IVC channel must be properly initialized
* - Completion structure must be initialized
* - IRQ must be properly registered with this handler
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: Yes
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: Yes
* - Runtime: Yes
* - De-Init: Yes
*/
static irqreturn_t ivc_irq_handler(int irq, void *data) static irqreturn_t ivc_irq_handler(int irq, void *data)
{ {
struct vmtd_dev *vmtddev = (struct vmtd_dev *)data; struct vmtd_dev *vmtddev = (struct vmtd_dev *)data;
@@ -76,6 +116,9 @@ static irqreturn_t ivc_irq_handler(int irq, void *data)
complete(&vmtddev->msg_complete); complete(&vmtddev->msg_complete);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/**
* @}
*/
static int vmtd_send_cmd(struct vmtd_dev *vmtddev, struct vs_request *vs_req) static int vmtd_send_cmd(struct vmtd_dev *vmtddev, struct vs_request *vs_req)
{ {
@@ -206,9 +249,48 @@ static int vmtd_get_configinfo(struct vmtd_dev *vmtddev,
return 0; return 0;
} }
/* /**
* Read an address range from the flash chip. The address range * @defgroup vscd_mtd_request_handler LinuxMtdVSCD::Request Handler
* may be any size provided it is within the physical boundaries. *
* @ingroup vscd_mtd_request_handler
* @{
*/
/**
* @brief Reads data from the virtual MTD device
*
* This function reads data from the virtual MTD device by:
* 1. Validating read boundaries against device size
* 2. Breaking down large reads into smaller chunks based on max_read_bytes_per_io
* 3. For each chunk:
* - Preparing VS_MTD_READ request
* - Sending request through IVC channel
* - Waiting for response from physical device
* - Copying data from shared buffer to user buffer
* 4. Maintaining thread safety through mutex locking
*
* @param[in] mtd Pointer to MTD device information structure
* @param[in] from Starting offset in the device to read from
* @param[in] len Number of bytes to read
* @param[out] retlen Pointer to store number of bytes actually read
* @param[out] buf Buffer to store read data
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - IVC channel must be operational
* - Shared memory buffer must be mapped
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/ */
static int vmtd_read(struct mtd_info *mtd, loff_t from, size_t len, static int vmtd_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
@@ -264,9 +346,44 @@ fail:
return ret; return ret;
} }
/* /**
* Write an address range from the flash chip. The address range * @brief Writes data to the virtual MTD device
* may be any size provided it is within the physical boundaries. *
* This function writes data to the virtual MTD device by:
* 1. Validating write boundaries against device size
* 2. Breaking down large writes into smaller chunks based on max_write_bytes_per_io
* 3. For each chunk:
* - Copying data from user buffer to shared memory
* - Preparing VS_MTD_WRITE request
* - Sending request through IVC channel
* - Waiting for write confirmation
* 4. Maintaining thread safety through mutex locking
* 5. Handling write failures and partial writes
*
* @param[in] mtd Pointer to MTD device information structure
* @param[in] to Starting offset in the device to write to
* @param[in] len Number of bytes to write
* @param[out] retlen Pointer to store number of bytes actually written
* @param[in] buf Buffer containing data to write
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - IVC channel must be operational
* - Shared memory buffer must be mapped
* - Device must not be in read-only mode
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/ */
static int vmtd_write(struct mtd_info *mtd, loff_t to, size_t len, static int vmtd_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf) size_t *retlen, const u_char *buf)
@@ -323,9 +440,42 @@ fail:
return ret; return ret;
} }
/* /**
* Erase an address range from the flash chip. The address range * @brief Erases a region of the virtual MTD device
* may be any size provided it is within the physical boundaries. *
* This function erases a specified region of the virtual MTD device by:
* 1. Validating erase boundaries against device size
* 2. Preparing VS_MTD_ERASE request with:
* - Starting address (instr->addr)
* - Length of region to erase (instr->len)
* 3. Sending single erase command through IVC channel
* 4. Waiting for erase completion confirmation
* 5. Maintaining thread safety through mutex locking
* 6. Handling erase failures
*
* @param[in] mtd Pointer to MTD device information structure
* @param[in] instr Erase instruction structure containing:
* - Address to start erasing from
* - Length of region to erase
* @return 0 on success, negative errno on failure
*
* @pre
* - Device must be initialized
* - IVC channel must be operational
* - Device must not be in read-only mode
* - Erase region must be aligned to erase block size
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/ */
static int vmtd_erase(struct mtd_info *mtd, struct erase_info *instr) static int vmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
@@ -366,8 +516,51 @@ static int vmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
fail: fail:
return ret; return ret;
} }
/**
* @}
*/
/**
* @defgroup vscd_mtd_suspend_resume LinuxMtdVSCD::Suspend/Resume
*
* @ingroup vscd_mtd_suspend_resume
* @{
*/
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/**
* @brief Suspends the virtual MTD device operations
*
* This function performs the following operations during system suspend:
* 1. Checks if the device is properly set up
* 2. Acquires the device mutex to prevent concurrent access
* 3. Disables the IVC interrupt to prevent further communications
* 4. Resets the IVC channel to ensure clean state during suspend
*
* The function ensures that all ongoing MTD operations are properly halted
* and the communication channel with the hypervisor is safely suspended.
*
* @param[in] dev Pointer to the device structure
* @return 0 on success
*
* @pre
* - Device must be initialized
* - System must be in suspend transition
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int tegra_virt_mtd_suspend(struct device *dev) static int tegra_virt_mtd_suspend(struct device *dev)
{ {
struct vmtd_dev *vmtddev = dev_get_drvdata(dev); struct vmtd_dev *vmtddev = dev_get_drvdata(dev);
@@ -379,6 +572,37 @@ static int tegra_virt_mtd_suspend(struct device *dev)
return 0; return 0;
} }
/**
* @brief Resumes the virtual MTD device operations
*
* This function performs the following operations during system resume:
* 1. Checks if the device was properly set up before suspend
* 2. Re-enables the IVC interrupt to restore communication
* 3. Releases the device mutex to allow MTD operations
*
* The function restores the device to operational state after system resume,
* re-establishing the communication channel with the hypervisor and
* allowing MTD operations to proceed.
*
* @param[in] dev Pointer to the device structure
* @return 0 on success
*
* @pre
* - Device must have been previously suspended
* - System must be in resume transition
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static int tegra_virt_mtd_resume(struct device *dev) static int tegra_virt_mtd_resume(struct device *dev)
{ {
struct vmtd_dev *vmtddev = dev_get_drvdata(dev); struct vmtd_dev *vmtddev = dev_get_drvdata(dev);
@@ -397,6 +621,10 @@ static const struct dev_pm_ops tegra_hv_vmtd_pm_ops = {
}; };
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
/**
* @}
*/
static int vmtd_setup_device(struct vmtd_dev *vmtddev) static int vmtd_setup_device(struct vmtd_dev *vmtddev)
{ {
mutex_init(&vmtddev->lock); mutex_init(&vmtddev->lock);
@@ -449,6 +677,41 @@ static int vmtd_setup_device(struct vmtd_dev *vmtddev)
NULL, 0); NULL, 0);
} }
/**
* @defgroup vscd_mtd_sysfs LinuxMtdVSCD::Sysfs
*
* @ingroup vscd_mtd_sysfs
* @{
*/
/**
* @brief Shows the physical device type of the virtual MTD device
*
* This function retrieves and displays the physical device type that underlies
* the virtual MTD device. Currently, it checks if the device is a QSPI device
* and returns the appropriate string representation. If the device type is not
* recognized, it returns "unknown!".
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the device type string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - Configuration must be properly set up
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vmtd_phys_dev_show(struct device *dev, static ssize_t vmtd_phys_dev_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -461,6 +724,34 @@ static ssize_t vmtd_phys_dev_show(struct device *dev,
} }
static DEVICE_ATTR(phys_dev, 0444, vmtd_phys_dev_show, NULL); static DEVICE_ATTR(phys_dev, 0444, vmtd_phys_dev_show, NULL);
/**
* @brief Shows the physical base address of the virtual MTD device
*
* This function retrieves and displays the physical base address of the underlying
* MTD device in hexadecimal format. This address represents the starting memory
* location of the physical flash device in the system's memory map.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the base address string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - Configuration must be properly set up
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t vmtd_phys_base_show(struct device *dev, static ssize_t vmtd_phys_base_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -470,6 +761,34 @@ static ssize_t vmtd_phys_base_show(struct device *dev,
} }
static DEVICE_ATTR(phys_base, 0444, vmtd_phys_base_show, NULL); static DEVICE_ATTR(phys_base, 0444, vmtd_phys_base_show, NULL);
/**
* @brief Shows the manufacturer ID of the MTD device
*
* This function retrieves and displays the manufacturer ID of the flash device
* in hexadecimal format. The manufacturer ID is a unique identifier that
* indicates the company that manufactured the flash memory chip.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the manufacturer ID string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - Configuration must be properly set up
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t manufacturer_id_show(struct device *dev, static ssize_t manufacturer_id_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -479,6 +798,34 @@ static ssize_t manufacturer_id_show(struct device *dev,
} }
static DEVICE_ATTR_RO(manufacturer_id); static DEVICE_ATTR_RO(manufacturer_id);
/**
* @brief Shows the device ID of the MTD device
*
* This function retrieves and displays the device ID of the flash device
* in hexadecimal format. The device ID is a unique identifier that
* specifies the particular model or variant of the flash memory chip.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the device ID string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - Configuration must be properly set up
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t device_id_show(struct device *dev, static ssize_t device_id_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -488,6 +835,34 @@ static ssize_t device_id_show(struct device *dev,
} }
static DEVICE_ATTR_RO(device_id); static DEVICE_ATTR_RO(device_id);
/**
* @brief Shows the QSPI device size in bytes
*
* This function retrieves and displays the total size of the QSPI flash device
* in bytes. This represents the total storage capacity of the physical flash
* memory device that is being virtualized.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the device size string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - Configuration must be properly set up
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t qspi_device_size_bytes_show(struct device *dev, static ssize_t qspi_device_size_bytes_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -497,6 +872,39 @@ static ssize_t qspi_device_size_bytes_show(struct device *dev,
} }
static DEVICE_ATTR_RO(qspi_device_size_bytes); static DEVICE_ATTR_RO(qspi_device_size_bytes);
/**
* @brief Shows the ECC (Error Correction Code) status of the MTD device
*
* This function performs an ECC status check and returns the current ECC state.
* It sends a VS_MTD_ECC request to the physical device and interprets the response.
* Possible status values are:
* - ECC_NO_ERROR: No errors detected
* - ECC_ONE_BIT_CORRECTED: Single-bit error detected and corrected
* - ECC_TWO_BIT_ERROR: Double-bit error detected (uncorrectable)
* - ECC_DISABLED: ECC functionality is disabled
* - ECC_REQUEST_FAILED: Failed to get ECC status
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the ECC status string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - IVC channel must be operational
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t ecc_status_show(struct device *dev, static ssize_t ecc_status_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -538,6 +946,35 @@ static ssize_t ecc_status_show(struct device *dev,
} }
static DEVICE_ATTR_RO(ecc_status); static DEVICE_ATTR_RO(ecc_status);
/**
* @brief Shows the address of the chunk where ECC failure occurred
*
* This function retrieves and displays the memory address of the chunk where
* the last ECC error was detected. The address is displayed in hexadecimal format.
* After reading the address, it resets the stored address to 0 to prepare for
* the next ECC error detection.
*
* @param[in] dev Pointer to the device structure
* @param[in] attr Pointer to device attribute structure
* @param[out] buf Buffer to store the failure chunk address string
* @return Number of characters written to the buffer
*
* @pre
* - Device must be initialized
* - ECC status should have been checked previously
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: Yes
* - API Group
* - Init: No
* - Runtime: Yes
* - De-Init: No
*/
static ssize_t failure_chunk_addr_show(struct device *dev, static ssize_t failure_chunk_addr_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
@@ -551,6 +988,9 @@ static ssize_t failure_chunk_addr_show(struct device *dev,
} }
static DEVICE_ATTR_RO(failure_chunk_addr); static DEVICE_ATTR_RO(failure_chunk_addr);
/**
* @}
*/
static const struct attribute *vmtd_storage_attrs[] = { static const struct attribute *vmtd_storage_attrs[] = {
&dev_attr_phys_dev.attr, &dev_attr_phys_dev.attr,
@@ -685,6 +1125,46 @@ static int32_t vmtd_init_device(struct vmtd_dev *vmtddev)
return ret; return ret;
} }
/**
* @defgroup vscd_mtd_probe_remove LinuxMtdVSCD::Probe/Remove
*
* @ingroup vscd_mtd_probe_remove
* @{
*/
/**
* @brief Probes and initializes the virtual MTD device driver
*
* This function performs the following initialization steps:
* 1. Verifies hypervisor mode and device tree node
* 2. Allocates and initializes vmtd device structure
* 3. Reserves IVC channel for command/response communication
* 4. Reserves IVM memory pool for data transfer
* 5. Maps shared memory buffer for data transfer
* 6. Sets up interrupt handling for IVC communication
* 7. Initializes the virtual MTD device with configuration from physical device
* 8. Creates sysfs entries for device attributes
* 9. Registers error injection callbacks if enabled
*
* @param[in] pdev Platform device structure containing device information
* @return 0 on success, negative errno on failure
*
* @pre
* - System must be running in Tegra hypervisor mode
* - Valid device tree node must be present
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: Yes
* - Runtime: No
* - De-Init: No
*/
static int tegra_virt_mtd_probe(struct platform_device *pdev) static int tegra_virt_mtd_probe(struct platform_device *pdev)
{ {
struct device_node __maybe_unused *np; struct device_node __maybe_unused *np;
@@ -816,6 +1296,37 @@ static struct of_device_id tegra_virt_mtd_match[] = {
MODULE_DEVICE_TABLE(of, tegra_virt_mtd_match); MODULE_DEVICE_TABLE(of, tegra_virt_mtd_match);
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
/**
* @brief Removes and cleans up the virtual MTD device driver
*
* This function performs the following cleanup steps:
* 1. Unreserves the IVC channel used for command/response communication
* 2. Unreserves the IVM memory pool used for data transfer
* 3. Deregisters error injection callbacks if enabled
* 4. Frees allocated resources
*
* The function exists in two variants based on kernel version:
* - Returns void for Linux v6.11 and later
* - Returns int for earlier versions
*
* @param[in] pdev Platform device structure containing device information
* @return void or 0 depending on kernel version
*
* @pre
* - Driver must be successfully probed and initialized
*
* @usage
* - Allowed context for the API call
* - Interrupt handler: No
* - Signal handler: No
* - Thread-safe: Yes
* - Async/Sync: Sync
* - Re-entrant: No
* - API Group
* - Init: No
* - Runtime: No
* - De-Init: Yes
*/
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */ #if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
static void tegra_virt_mtd_remove_wrapper(struct platform_device *pdev) static void tegra_virt_mtd_remove_wrapper(struct platform_device *pdev)
{ {
@@ -827,6 +1338,9 @@ static int tegra_virt_mtd_remove_wrapper(struct platform_device *pdev)
return tegra_virt_mtd_remove(pdev); return tegra_virt_mtd_remove(pdev);
} }
#endif #endif
/**
* @}
*/
static struct platform_driver tegra_virt_mtd_driver = { static struct platform_driver tegra_virt_mtd_driver = {
.probe = tegra_virt_mtd_probe, .probe = tegra_virt_mtd_probe,