diff --git a/drivers/block/tegra_oops_virt_storage/tegra_hv_vblk_oops.c b/drivers/block/tegra_oops_virt_storage/tegra_hv_vblk_oops.c index 408a1977..170f7f43 100644 --- a/drivers/block/tegra_oops_virt_storage/tegra_hv_vblk_oops.c +++ b/drivers/block/tegra_oops_virt_storage/tegra_hv_vblk_oops.c @@ -127,6 +127,42 @@ static int32_t wait_for_fops_completion(struct vblk_dev *vblkdev_oops, bool is_r 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) { struct vsc_request *vsc_req; @@ -221,6 +257,35 @@ fail: 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, loff_t pos) { @@ -241,7 +306,8 @@ static ssize_t vblk_oops_write(const char *buf, size_t bytes, */ if (in_atomic()) { 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; } @@ -334,6 +400,35 @@ fail: * - 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 */ + +/** + * @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, loff_t pos) { @@ -415,6 +510,7 @@ static ssize_t vblk_oops_panic_write(const char *buf, size_t bytes, */ return bytes; } +/** @} */ /* Set up virtual device. */ static void setup_device(struct vblk_dev *vblkdev) @@ -683,6 +779,39 @@ static int vblk_oops_get_configinfo(struct vblk_dev *vblkdev) 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) { 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 struct device_node *vblk_node; @@ -804,6 +967,8 @@ fail: return ret; } +/**@} */ + static int tegra_hv_vblk_oops_remove(struct platform_device *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; } +/** + * @defgroup oops_driver_suspend_resume OOPSDriver::Suspend/Resume + * + * @ingroup oops_driver_suspend_resume + * @{ + */ + #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) { /* Reset the channel */ @@ -826,6 +1020,28 @@ static int tegra_hv_vblk_oops_suspend(struct device *dev) 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) { int i = 0; @@ -841,12 +1057,15 @@ static int tegra_hv_vblk_oops_resume(struct device *dev) return 0; } + static const struct dev_pm_ops tegra_hv_vblk_oops_pm_ops = { .suspend_noirq = tegra_hv_vblk_oops_suspend, .resume_noirq = tegra_hv_vblk_oops_resume, }; #endif /* CONFIG_PM_SLEEP */ +/** @} */ + #ifdef CONFIG_OF static const struct of_device_id tegra_hv_vblk_oops_match[] = { { .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); #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 */ 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) { return platform_driver_register(&tegra_hv_vblk_oops_driver); } 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) { platform_driver_unregister(&tegra_hv_vblk_oops_driver); } module_exit(tegra_hv_vblk_driver_exit); +/** @} */ MODULE_AUTHOR("Haribabu Narayanan "); MODULE_DESCRIPTION("Virtual OOPS storage device over Tegra Hypervisor IVC channel"); MODULE_LICENSE("GPL"); diff --git a/drivers/block/tegra_virt_storage/tegra_hv_ioctl.c b/drivers/block/tegra_virt_storage/tegra_hv_ioctl.c index ce397290..3548c005 100644 --- a/drivers/block/tegra_virt_storage/tegra_hv_ioctl.c +++ b/drivers/block/tegra_virt_storage/tegra_hv_ioctl.c @@ -191,7 +191,42 @@ static int vblk_common_ioctl(struct vblk_dev *vblkdev, fmode_t mode, 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, unsigned int cmd, unsigned long arg) { @@ -207,7 +242,37 @@ int vblk_ffu_ioctl(struct block_device *bdev, fmode_t mode, 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, unsigned int cmd, unsigned long arg) { @@ -221,7 +286,35 @@ int vblk_ioctl(struct block_device *bdev, fmode_t mode, 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, unsigned int cmd, unsigned long arg) { @@ -245,3 +338,4 @@ int vblk_ioctl_not_supported(struct block_device *bdev, fmode_t mode, return ret; } +/** @} */ diff --git a/drivers/block/tegra_virt_storage/tegra_hv_vblk.c b/drivers/block/tegra_virt_storage/tegra_hv_vblk.c index b0c805b1..34ad5933 100644 --- a/drivers/block/tegra_virt_storage/tegra_hv_vblk.c +++ b/drivers/block/tegra_virt_storage/tegra_hv_vblk.c @@ -755,6 +755,47 @@ bio_exit: 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) { struct vblk_dev *vblkdev = (struct vblk_dev *)data; @@ -782,9 +823,45 @@ static int vblk_request_worker(void *data) 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, - const struct blk_mq_queue_data *bd) + const struct blk_mq_queue_data *bd) { struct req_entry *entry; struct request *req = bd->rq; @@ -813,8 +890,43 @@ static blk_status_t vblk_request(struct blk_mq_hw_ctx *hctx, return BLK_STS_OK; } +/** @} */ /* 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 */ 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; } +/** + * @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 */ 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; } +/** + * @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) 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; } +/** + * @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 */ static void vblk_ioctl_release(struct gendisk *disk) #else @@ -906,6 +1099,33 @@ static void vblk_ioctl_release(struct gendisk *disk, fmode_t mode) 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) static void vblk_ffu_release(struct gendisk *disk) #else @@ -921,7 +1141,33 @@ static void vblk_ffu_release(struct gendisk *disk, fmode_t mode) 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 */ static void vblk_release(struct gendisk *disk) #else @@ -941,6 +1187,33 @@ static void vblk_release(struct gendisk *disk, fmode_t mode) 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) { geo->heads = VS_LOG_HEADS; @@ -950,6 +1223,7 @@ static int vblk_getgeo(struct block_device *device, struct hd_geometry *geo) return 0; } +/** @} */ /* The device operations structure. */ static const struct block_device_operations vblk_ops_no_ioctl = { @@ -978,9 +1252,45 @@ static const struct block_device_operations vblk_ops_ffu = { .ioctl = vblk_ffu_ioctl }; -static ssize_t -vblk_phys_dev_show(struct device *dev, struct device_attribute *attr, - char *buf) +/** + * @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) { struct gendisk *disk = dev_to_disk(dev); struct vblk_dev *vblk = disk->private_data; @@ -993,9 +1303,39 @@ vblk_phys_dev_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, 16, "Unknown\n"); } -static ssize_t -vblk_phys_base_show(struct device *dev, struct device_attribute *attr, - char *buf) +/** + * @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) { struct gendisk *disk = dev_to_disk(dev); struct vblk_dev *vblk = disk->private_data; @@ -1003,9 +1343,39 @@ vblk_phys_base_show(struct device *dev, struct device_attribute *attr, 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, - char *buf) +/** + * @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) { struct gendisk *disk = dev_to_disk(dev); struct vblk_dev *vblk = disk->private_data; @@ -1038,9 +1408,39 @@ vblk_storage_type_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, 16, "Unknown\n"); } -static ssize_t -vblk_speed_mode_show(struct device *dev, struct device_attribute *attr, - char *buf) +/** + * @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) { struct gendisk *disk = dev_to_disk(dev); struct vblk_dev *vblk = disk->private_data; @@ -1068,6 +1468,7 @@ static const struct blk_mq_ops vblk_mq_ops = { .queue_rq = vblk_request, }; +/** @} */ #if (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ)) /* 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); } +/** + * @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) { 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; } +/** + * @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) { struct vblk_dev *vblkdev = container_of(ws, struct vblk_dev, rq_cfg); @@ -1922,15 +2411,6 @@ free_ivc: 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) { 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 struct device_node *vblk_node; @@ -2034,6 +2553,34 @@ fail: 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) { struct vblk_dev *vblkdev = platform_get_drvdata(pdev); @@ -2079,7 +2626,50 @@ static int tegra_hv_vblk_remove(struct platform_device *pdev) return 0; } +/** @} */ + +/** + * @defgroup vscd_suspend_resume LinuxVSCD::Suspend/Resume + * + * @ingroup vscd_suspend_resume + * @{ + */ + #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) { struct vblk_dev *vblkdev = dev_get_drvdata(dev); @@ -2111,6 +2701,41 @@ static int tegra_hv_vblk_suspend(struct device *dev) 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) { struct vblk_dev *vblkdev = dev_get_drvdata(dev); @@ -2141,6 +2766,8 @@ static int tegra_hv_vblk_resume(struct device *dev) return 0; } +/** @} */ + static const struct dev_pm_ops tegra_hv_vblk_pm_ops = { .suspend = tegra_hv_vblk_suspend, .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) { vblk_major = 0; @@ -2193,12 +2854,42 @@ static int __init tegra_hv_vblk_driver_init(void) } 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) { unregister_blkdev(vblk_major, "vblk"); platform_driver_unregister(&tegra_hv_vblk_driver); } module_exit(tegra_hv_vblk_driver_exit); +/** + * @} + */ MODULE_AUTHOR("Dilan Lee "); MODULE_DESCRIPTION("Virtual storage device over Tegra Hypervisor IVC channel"); diff --git a/drivers/mtd/devices/tegra_hv_mtd.c b/drivers/mtd/devices/tegra_hv_mtd.c index e3f7429c..81706c8a 100644 --- a/drivers/mtd/devices/tegra_hv_mtd.c +++ b/drivers/mtd/devices/tegra_hv_mtd.c @@ -69,6 +69,46 @@ static inline struct vmtd_dev *mtd_to_vmtd(struct mtd_info *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) { 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); return IRQ_HANDLED; } +/** + * @} + */ 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; } -/* - * Read an address range from the flash chip. The address range - * may be any size provided it is within the physical boundaries. +/** + * @defgroup vscd_mtd_request_handler LinuxMtdVSCD::Request Handler + * + * @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, size_t *retlen, u_char *buf) @@ -264,9 +346,44 @@ fail: return ret; } -/* - * Write an address range from the flash chip. The address range - * may be any size provided it is within the physical boundaries. +/** + * @brief Writes data to the virtual MTD device + * + * 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, size_t *retlen, const u_char *buf) @@ -323,9 +440,42 @@ fail: return ret; } -/* - * Erase an address range from the flash chip. The address range - * may be any size provided it is within the physical boundaries. +/** + * @brief Erases a region of the virtual MTD device + * + * 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) { @@ -366,8 +516,51 @@ static int vmtd_erase(struct mtd_info *mtd, struct erase_info *instr) fail: return ret; } +/** + * @} + */ + + +/** + * @defgroup vscd_mtd_suspend_resume LinuxMtdVSCD::Suspend/Resume + * + * @ingroup vscd_mtd_suspend_resume + * @{ + */ #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) { struct vmtd_dev *vmtddev = dev_get_drvdata(dev); @@ -379,6 +572,37 @@ static int tegra_virt_mtd_suspend(struct device *dev) 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) { 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 */ +/** + * @} + */ + static int vmtd_setup_device(struct vmtd_dev *vmtddev) { mutex_init(&vmtddev->lock); @@ -449,6 +677,41 @@ static int vmtd_setup_device(struct vmtd_dev *vmtddev) 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, 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); +/** + * @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, 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); +/** + * @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, 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); +/** + * @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, 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); +/** + * @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, 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); +/** + * @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, 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); +/** + * @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, 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 const struct attribute *vmtd_storage_attrs[] = { &dev_attr_phys_dev.attr, @@ -685,6 +1125,46 @@ static int32_t vmtd_init_device(struct vmtd_dev *vmtddev) 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) { struct device_node __maybe_unused *np; @@ -816,7 +1296,38 @@ static struct of_device_id tegra_virt_mtd_match[] = { MODULE_DEVICE_TABLE(of, tegra_virt_mtd_match); #endif /* CONFIG_OF */ -#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */ +/** + * @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 */ static void tegra_virt_mtd_remove_wrapper(struct platform_device *pdev) { tegra_virt_mtd_remove(pdev); @@ -827,6 +1338,9 @@ static int tegra_virt_mtd_remove_wrapper(struct platform_device *pdev) return tegra_virt_mtd_remove(pdev); } #endif +/** + * @} + */ static struct platform_driver tegra_virt_mtd_driver = { .probe = tegra_virt_mtd_probe,