// SPDX-License-Identifier: GPL-2.0-or-later /* Module to test a mailbox with heavy load on two concurrent channels. * Sends the value of the void pointer as the message, so no dereferencing * of the pointers are done here, or can be done by the mailbox driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct mbox_loadtest_device { struct device *dev; struct dentry *root_debugfs_dir; struct mbox_client ping_client; struct mbox_chan *ping_channel; struct completion completion; struct mutex ping_lock; u32 ping_rsp; struct mbox_client pong_client; struct mbox_chan *pong_channel; struct tasklet_struct pong_tasklet; u32 pong_rsp; }; static void mbox_loadtest_receive_ping_message(struct mbox_client *client, void *message) { struct mbox_loadtest_device *tdev = container_of(client, struct mbox_loadtest_device, ping_client); tdev->ping_rsp = (u32)(unsigned long)message; complete(&tdev->completion); } static void mbox_loadtest_pong_tasklet(unsigned long data) { struct mbox_loadtest_device *tdev = (struct mbox_loadtest_device *)data; mbox_send_message(tdev->pong_channel, (void *)(unsigned long)tdev->pong_rsp); } static void mbox_loadtest_receive_pong_message(struct mbox_client *client, void *message) { struct mbox_loadtest_device *tdev = container_of(client, struct mbox_loadtest_device, pong_client); tdev->pong_rsp = ((u32)(unsigned long)message) + 1; tasklet_init(&tdev->pong_tasklet, mbox_loadtest_pong_tasklet, (unsigned long)tdev); tasklet_hi_schedule(&tdev->pong_tasklet); } static int mbox_loadtest_send_ping_message(struct mbox_loadtest_device *tdev, u32 message) { int compleated; u32 rsp; mutex_lock(&tdev->ping_lock); reinit_completion(&tdev->completion); mbox_send_message(tdev->ping_channel, (void *)(unsigned long)message); compleated = wait_for_completion_timeout(&tdev->completion, msecs_to_jiffies(20)); rsp = tdev->ping_rsp; mutex_unlock(&tdev->ping_lock); if (!compleated) { dev_err(tdev->dev, "Timeout\n"); return -EFAULT; } if (rsp != message+1) { dev_err(tdev->dev, "Wrong ans %i != %i\n", rsp, message); return -EFAULT; } return 0; } static ssize_t mbox_loadtest_ping_write(struct file *filp, const char __user *userbuf, size_t count, loff_t *ppos) { int ret; struct mbox_loadtest_device *tdev = filp->private_data; ret = mbox_loadtest_send_ping_message(tdev, 0x42); return ret < 0 ? ret : count; } static const struct file_operations mbox_loadtest_ping_ops = { .write = mbox_loadtest_ping_write, .open = simple_open, }; static int mbox_loadtest_probe(struct platform_device *pdev) { struct mbox_loadtest_device *tdev; struct device_node *np = pdev->dev.of_node; tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); if (!tdev) return -ENOMEM; if (of_property_match_string(np, "mbox-names", "ping") >= 0) { tdev->ping_client.dev = &pdev->dev; tdev->ping_client.rx_callback = mbox_loadtest_receive_ping_message; tdev->ping_client.tx_done = NULL; tdev->ping_client.tx_block = false; tdev->ping_client.knows_txdone = false; tdev->ping_client.tx_tout = 500; tdev->ping_channel = mbox_request_channel_byname(&tdev->ping_client, "ping"); if (IS_ERR(tdev->ping_channel)) { return -EPROBE_DEFER; } mutex_init(&tdev->ping_lock); if (debugfs_initialized()) { tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL); debugfs_create_file("ping", 0600, tdev->root_debugfs_dir, tdev, &mbox_loadtest_ping_ops); } } if (of_property_match_string(np, "mbox-names", "pong") >= 0) { tdev->pong_client.dev = &pdev->dev; tdev->pong_client.rx_callback = mbox_loadtest_receive_pong_message; tdev->pong_client.tx_done = NULL; tdev->pong_client.tx_block = false; tdev->pong_client.knows_txdone = false; tdev->pong_client.tx_tout = 500; tdev->pong_channel = mbox_request_channel_byname(&tdev->pong_client, "pong"); if (IS_ERR(tdev->pong_channel)) { return -EPROBE_DEFER; } } init_completion(&tdev->completion); tdev->dev = &pdev->dev; platform_set_drvdata(pdev, tdev); return 0; } static int mbox_loadtest_remove(struct platform_device *pdev) { struct mbox_loadtest_device *tdev = platform_get_drvdata(pdev); debugfs_remove_recursive(tdev->root_debugfs_dir); if (tdev->ping_channel) mbox_free_channel(tdev->ping_channel); if (tdev->pong_channel) mbox_free_channel(tdev->pong_channel); return 0; } static const struct of_device_id mbox_loadtest_match[] = { { .compatible = "mailbox-loadtest" }, {}, }; MODULE_DEVICE_TABLE(of, mbox_loadtest_match); static struct platform_driver mbox_loadtest_driver = { .driver = { .name = "mailbox_loadtest", .of_match_table = mbox_loadtest_match, }, .probe = mbox_loadtest_probe, .remove = mbox_loadtest_remove, }; module_platform_driver(mbox_loadtest_driver); MODULE_DESCRIPTION("Mailbox Load Testing Facility"); MODULE_LICENSE("GPL v2");