通过KBUILD_MODNAME让dev_err等函数打印模块的name

本文解析了Linux内核中dev_err等函数的实现细节,介绍了如何通过KBUILD_MODNAME来定制错误信息前缀,帮助开发者更好地理解这些常用日志打印函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在drivers/base/core.c 中有dev_err 等的实现
2808    #define define_dev_printk_level(func, kern_level)        \
2809    void func(const struct device *dev, const char *fmt, ...)    \
2810    {                                \
2811        struct va_format vaf;                    \
2812        va_list args;                        \
2813                                    \
2814        va_start(args, fmt);                    \
2815                                    \
2816        vaf.fmt = fmt;                        \
2817        vaf.va = &args;                        \
2818                                    \
2819        __dev_printk(kern_level, dev, &vaf);            \
2820                                    \
2821        va_end(args);                        \
2822    }                                \
2823    EXPORT_SYMBOL(func);
2824    
2825    define_dev_printk_level(dev_emerg, KERN_EMERG);
2826    define_dev_printk_level(dev_alert, KERN_ALERT);
2827    define_dev_printk_level(dev_crit, KERN_CRIT);
2828    define_dev_printk_level(dev_err, KERN_ERR);
注意在dev_err 等的函数实现define_dev_printk_level 中的fmt,这样我们在自己的驱动中可以通过定义
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
这样在驱动中通过dev_err(dev, "can't read time\n"); 打印error log的话,实际效果如下:
rtc-efi rtc-efi: can't read time

这里的关键就是KBUILD_MODNAME,

obj-$(CONFIG_RTC_DRV_EFI)    += rtc-efi.o
从makefile中就可以看到这个.o的KBUILD_MODNAME 就是rtc-efi.



// SPDX-License-Identifier: GPL-2.0-only // // Copyright (c) 2024 GoldenRiver Inc. // Copyright (c) 2024 MediaTek Inc. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/idr.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/kthread.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/uaccess.h> #include <linux/mutex.h> #include <linux/delay.h> #include <linux/completion.h> #include <linux/spinlock.h> #include <linux/arm-smccc.h> #include <linux/nebula/hvcall.h> #include "rpmsg_virtio.h" #include "nebula_rproc_remote.h" enum { RPROC_REG_SET_READY = 0x0, RPROC_REG_CLEAR_READY = 0x4, RPROC_REG_SHM_BASE_LOW = 0x8, RPROC_REG_SHM_BASE_HIGH = 0xc, RPROC_REG_STATE = 0x10, RPROC_REG_KICK = 0x14, RPROC_REG_PEER_VMID = 0x18, RPROC_REG_PEER_ONLINE = 0x1c, }; enum { STATE_NOT_READY, STATE_READY, }; enum { IDLE, CREATING_VDEV, VDEV_CREATED, DESTROYING_VDEV, }; char *state_string[] = { __stringify(IDLE), __stringify(CREATING_VDEV), __stringify(VDEV_CREATED), __stringify(DESTROYING_VDEV), }; #define MAX_VIRTIO_DEVICES 20 #define ALL_HOSTS -2 struct rproc_resource_table { u32 ver; u32 num; u32 reserved[2]; u32 offset[MAX_VIRTIO_DEVICES]; } __packed; static void signal_irq(uint16_t irq) { struct arm_smccc_res res; unsigned long r7 = SMC_HYP_SECURE_ID << 16; arm_smccc_smc(SMC_FC_NBL_VHM_REQ, 0, irq, 0, 0, 0, 0, r7, &res); } struct irq_info { int local_virq; int remote_hwirq; struct virtio_vring_info *vring_info; }; struct virtio_device_info { struct virtio_device *dev; struct nebula_rproc_vdev_ops *ops; void *user_priv; }; struct rproc_remote_priv { void *__iomem reg_base; void *shm_base; size_t shm_size; phys_addr_t host_shm_phys; struct metal_io_region shm_io; struct device *dev; int num_queues; struct irq_info *irq_info; bool notify_with_phys_irq; struct mutex rsc_table_mutex; struct virtio_device_info vdevs[MAX_VIRTIO_DEVICES]; void *rsc_table; size_t rsc_table_offset; size_t num_vdevs; struct task_struct *thread; struct completion compl; bool need_release; int state; bool ready; spinlock_t state_lock; struct dentry *dbgfs; bool auto_restart; struct list_head node; bool force_offline; }; static void set_state(struct rproc_remote_priv *priv, int state) { unsigned long flags; int prev_state; spin_lock_irqsave(&priv->state_lock, flags); prev_state = priv->state; priv->state = state; spin_unlock_irqrestore(&priv->state_lock, flags); dev_info(priv->dev, "%s -> %s\n", state_string[prev_state], state_string[state]); } static int get_host(struct rproc_remote_priv *priv) { if (!priv) return -EINVAL; return readl_relaxed(priv->reg_base + RPROC_REG_PEER_VMID); } static int get_state(struct rproc_remote_priv *priv) { unsigned long flags; int state; spin_lock_irqsave(&priv->state_lock, flags); state = priv->state; spin_unlock_irqrestore(&priv->state_lock, flags); return state; } LIST_HEAD(rproc_devices); DEFINE_MUTEX(rproc_devices_lock); int nebula_rproc_register_device_for_host( const void *rsc, size_t rsc_size, struct nebula_rproc_vdev_ops *vdev_ops, int vmid, void *user_priv) { struct rproc_remote_priv *priv; struct rproc_resource_table *rsc_table; int vdev_idx, host_vmid; if (!vdev_ops) return -EINVAL; mutex_lock(&rproc_devices_lock); if (list_empty(&rproc_devices)) { mutex_unlock(&rproc_devices_lock); return -ENODEV; } list_for_each_entry(priv, &rproc_devices, node) { mutex_lock(&priv->rsc_table_mutex); host_vmid = get_host(priv); if(vmid != ALL_HOSTS && host_vmid != vmid){ mutex_unlock(&priv->rsc_table_mutex); continue; } vdev_idx = priv->num_vdevs; if (vdev_idx < 0 || vdev_idx >= MAX_VIRTIO_DEVICES) { mutex_unlock(&priv->rsc_table_mutex); mutex_unlock(&rproc_devices_lock); return -EINVAL; } pr_debug("register vdev%d, rsc_offset %lx, rsc_size %lx\n", vdev_idx, priv->rsc_table_offset, rsc_size); rsc_table = priv->rsc_table; rsc_table->offset[vdev_idx] = priv->rsc_table_offset; priv->vdevs[vdev_idx].ops = vdev_ops; priv->vdevs[vdev_idx].user_priv = user_priv; BUG_ON(priv->rsc_table_offset + rsc_size >= PAGE_SIZE); memcpy(priv->rsc_table + priv->rsc_table_offset, rsc, rsc_size); priv->num_vdevs++; priv->rsc_table_offset += rsc_size; rsc_table->num = priv->num_vdevs; mutex_unlock(&priv->rsc_table_mutex); } mutex_unlock(&rproc_devices_lock); return 0; } EXPORT_SYMBOL(nebula_rproc_register_device_for_host); int nebula_rproc_register_device(const void *rsc, size_t rsc_size, struct nebula_rproc_vdev_ops *vdev_ops, void *user_priv) { int ret; ret = nebula_rproc_register_device_for_host(rsc, rsc_size, vdev_ops, ALL_HOSTS, user_priv); if (ret) { pr_err("failed to register devices for host(%d)\n", ret); return ret; } return 0; } EXPORT_SYMBOL(nebula_rproc_register_device); static inline bool is_shm_paddr(struct rproc_remote_priv *priv, phys_addr_t phys) { return phys > priv->host_shm_phys; } static inline bool is_shm_vaddr(struct rproc_remote_priv *priv, void *vaddr) { u64 start = (u64)priv->shm_base; u64 end = (u64)(priv->shm_base + priv->shm_size); u64 target = (u64)vaddr; return (target > start) && (target < end); } static void *rproc_paddr_to_vaddr(struct virtqueue *vq, phys_addr_t phys) { struct remoteproc_virtio *rpvdev = container_of(vq->vq_dev, struct remoteproc_virtio, vdev); struct rproc_remote_priv *priv = rpvdev->priv; void *virt; if (is_shm_paddr(priv, phys)) { u32 off = phys - priv->host_shm_phys; virt = priv->shm_base + off; } else { virt = phys_to_virt(phys); } pr_debug("paddr_to_vaddr: phys: %llx, virt: %px\n", phys, virt); return virt; } static phys_addr_t rproc_vaddr_to_paddr(struct virtqueue *vq, void *vaddr) { struct remoteproc_virtio *rpvdev = container_of(vq->vq_dev, struct remoteproc_virtio, vdev); struct rproc_remote_priv *priv = rpvdev->priv; phys_addr_t phys; if (is_shm_vaddr(priv, vaddr)) { u32 off = (vaddr - priv->shm_base); phys = priv->host_shm_phys + off; } else { phys = virt_to_phys(vaddr); } pr_debug("vaddr_to_paddr: phys: %llx, virt: %px\n", phys, vaddr); return phys; } static int rproc_vdev_notify(void *data, uint32_t notifyid) { struct rproc_remote_priv *priv = data; if (priv->notify_with_phys_irq) { struct irq_info *info = &priv->irq_info[notifyid]; signal_irq(info->remote_hwirq); } else { writel_relaxed(notifyid, priv->reg_base + RPROC_REG_KICK); } return 0; } static irqreturn_t rproc_notify_irq_handler(int irq, void *data) { struct irq_info *info = data; BUG_ON(info->vring_info == NULL || info->vring_info->vq == NULL); virtqueue_notification(info->vring_info->vq); return IRQ_HANDLED; } static void dump_rsc_table(struct device *dev, void *table) { struct rproc_resource_table *rsc_table = table; int i; dev_dbg(dev, "dump_rsc_table\n"); dev_dbg(dev, "ver=%d\n", rsc_table->ver); dev_dbg(dev, "num=%d\n", rsc_table->num); for (i = 0; i < rsc_table->num; i++) { struct fw_rsc_vdev *vdev_desc; u32 offset = rsc_table->offset[i]; vdev_desc = table + offset; dev_dbg(dev, "vdev%d, offset=%x\n", i, rsc_table->offset[i]); dev_dbg(dev, "vdev%d, type=%x", i, vdev_desc->type); dev_dbg(dev, "vdev%d, num_vrings=%d", i, vdev_desc->num_of_vrings); } } static void rproc_vdev_destroy_single(struct rproc_remote_priv *priv, int vdev_idx) { struct virtio_device_info *vdev = &priv->vdevs[vdev_idx]; struct remoteproc_virtio *rpvdev; struct rproc_resource_table *rsc_table = priv->rsc_table; struct fw_rsc_vdev *rsc = priv->rsc_table + rsc_table->offset[vdev_idx]; int i; // reset virtio device rsc->status = 0; if (!vdev->dev) return; rpvdev = container_of(vdev->dev, struct remoteproc_virtio, vdev); for (i = 0; i < vdev->dev->vrings_num; i++) { struct irq_info *irq_info; int notifyid = vdev->dev->vrings_info[i].notifyid; irq_info = &priv->irq_info[notifyid]; if (irq_info->vring_info) { irq_set_affinity_hint(irq_info->local_virq, NULL); devm_free_irq(priv->dev, irq_info->local_virq, irq_info); irq_info->vring_info = NULL; } } BUG_ON(vdev->ops->on_destroy == NULL); vdev->ops->on_destroy(vdev->dev); dev_err(priv->dev, "destroy vdev%d\n", vdev_idx); rproc_virtio_remove_vdev(vdev->dev); vdev->dev = NULL; } static bool vdev_wait_remote_ready(struct virtio_device *vdev) { uint8_t status; struct remoteproc_virtio *rpvdev; struct rproc_remote_priv *priv; int ret, retry = 0; rpvdev = container_of(vdev, struct remoteproc_virtio, vdev); priv = rpvdev->priv; while (1) { if (priv->need_release) { pr_info("virtio-%d is not alive!!!!! just quit\n", vdev->id.device); return false; } ret = virtio_get_status(vdev, &status); if (ret) { pr_info("virtio-%d: can't get status\n", vdev->id.device); return false; } if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) return true; msleep(100); retry++; if (retry == 50) { pr_info("virtio-%d still waiting for remote ready\n", vdev->id.device); retry = 0; } } } static int create_rproc_vdev(struct rproc_remote_priv *priv) { int ret, host_vmid; unsigned int num_vrings, i, vdev_idx; struct fw_rsc_vdev *vdev_rsc; struct virtio_device *vdev; struct rproc_resource_table *rsc_table = priv->rsc_table; struct nebula_rproc_vdev_ops *vdev_ops; cpumask_t mask; // bind rproc irq to vcpu4 cpumask_clear(&mask); cpumask_set_cpu(4, &mask); priv->need_release = false; dump_rsc_table(priv->dev, priv->rsc_table); for (vdev_idx = 0; vdev_idx < rsc_table->num; vdev_idx++) { dev_dbg(priv->dev, "rproc vdev%d start init\n", vdev_idx); vdev_ops = priv->vdevs[vdev_idx].ops; vdev_rsc = priv->rsc_table + rsc_table->offset[vdev_idx]; BUG_ON(vdev_rsc->type != RSC_VDEV); vdev = rproc_virtio_create_vdev(VIRTIO_DEV_DEVICE, vdev_idx, (void *)vdev_rsc, /*rsc_io=*/NULL, priv, rproc_vdev_notify, vdev_ops->on_reset); if (!vdev) { dev_info(priv->dev, "failed to create virtio vdev"); ret = -ENOMEM; goto err_free_vdev; } ret = vdev_wait_remote_ready(vdev); if (!ret) { ret = -ENODEV; rproc_virtio_remove_vdev(vdev); goto err_free_vdev; } /* The dfeatures has been updated after remote driver is ready, * we should apply it to local vdev->features */ vdev->features = vdev_rsc->dfeatures; /* set the notification id for vrings */ num_vrings = vdev_rsc->num_of_vrings; for (i = 0; i < num_vrings; i++) { const struct fw_rsc_vdev_vring *vring_rsc; phys_addr_t da; unsigned int num_descs, align; struct metal_io_region *io = NULL; void *va; size_t size; uint32_t off; struct irq_info *irq_info; int notifyid; vring_rsc = &vdev_rsc->vring[i]; notifyid = vring_rsc->notifyid; da = vring_rsc->da; dev_dbg(priv->dev, "vdev%d vring%d da=%llx\n", vdev_idx, notifyid, da); num_descs = vring_rsc->num; align = vring_rsc->align; size = vring_size(num_descs, align); off = da - (priv->host_shm_phys & 0xffffffff); va = priv->shm_base + off; ret = rproc_virtio_init_vring(vdev, i, notifyid, va, io, num_descs, align); if (ret) { dev_err(priv->dev, "vdev%d: failed to init vring, ret=%d\n", vdev_idx, ret); rproc_virtio_remove_vdev(vdev); goto err_free_vdev; } BUG_ON(notifyid >= priv->num_queues); irq_info = &priv->irq_info[notifyid]; irq_info->vring_info = &vdev->vrings_info[i]; } dev_info(priv->dev, "creating vdev%d (id:%d)\n", vdev_idx, vdev->id.device); host_vmid = get_host(priv); BUG_ON(vdev_ops->on_create == NULL); ret = vdev_ops->on_create(vdev, &priv->shm_io, host_vmid, priv->vdevs[vdev_idx].user_priv); if (ret) { dev_info(priv->dev, "failed to create rproc vdev%d, ret=%d\n", vdev_idx, ret); rproc_virtio_remove_vdev(vdev); continue; } dev_info(priv->dev, "created vdev%d (id:%d)\n", vdev_idx, vdev->id.device); priv->vdevs[vdev_idx].dev = vdev; // bind rproc irq to vcpu4 cpumask_clear(&mask); cpumask_set_cpu(4, &mask); /* The virtqueue should be created in vdev's on_create() callback, and * we should request irq only after virtqueue is created. */ for (i = 0; i < num_vrings; i++) { int notifyid = vdev_rsc->vring[i].notifyid; struct irq_info *irq_info = &priv->irq_info[notifyid]; ret = devm_request_threaded_irq(priv->dev, irq_info->local_virq, NULL, rproc_notify_irq_handler, IRQF_ONESHOT, dev_name(priv->dev), irq_info); if (ret) { dev_err(priv->dev, "vdev%d: failed to request irq, ret=%d\n", vdev_idx, ret); irq_info->vring_info = NULL; goto err_free_vdev; } irq_set_affinity_hint(irq_info->local_virq, &mask); } dev_dbg(priv->dev, "rproc vdev%d init done\n", vdev_idx); } return 0; err_free_vdev: for (i = 0; i <= vdev_idx; i++) { rproc_vdev_destroy_single(priv, i); } return ret; } static void rproc_vdev_destroy(struct rproc_remote_priv *priv) { int i, j; dev_info(priv->dev, "rproc vdev destroy start\n"); for (i = 0; i < priv->num_vdevs; i++) { struct virtio_device_info *vdev = &priv->vdevs[i]; struct remoteproc_virtio *rpvdev; struct rproc_resource_table *rsc_table = priv->rsc_table; struct fw_rsc_vdev *rsc = priv->rsc_table + rsc_table->offset[i]; // reset virtio device rsc->status = 0; dev_info(priv->dev, "destroying vdev%d\n", i); if (!vdev->dev) { dev_info(priv->dev, "vdev%d is not created, skipped\n", i); continue; } rpvdev = container_of(vdev->dev, struct remoteproc_virtio, vdev); for (j = 0; j < vdev->dev->vrings_num; j++) { struct irq_info *irq_info; int notifyid = vdev->dev->vrings_info[j].notifyid; irq_info = &priv->irq_info[notifyid]; if (irq_info->vring_info) { irq_set_affinity_hint(irq_info->local_virq, NULL); devm_free_irq(priv->dev, irq_info->local_virq, irq_info); irq_info->vring_info = NULL; } } BUG_ON(vdev->ops->on_destroy == NULL); vdev->ops->on_destroy(vdev->dev); dev_err(priv->dev, "destroyed vdev%d start\n", i); rproc_virtio_remove_vdev(vdev->dev); dev_err(priv->dev, "destroyed vdev%d end\n", i); vdev->dev = NULL; } dev_info(priv->dev, "rproc vdev destroy done\n"); } static irqreturn_t rproc_ctrl_irq_handler(int irq, void *data) { struct rproc_remote_priv *priv = (struct rproc_remote_priv *)data; u32 peer_online = readl_relaxed(priv->reg_base + RPROC_REG_PEER_ONLINE); if (!peer_online) priv->need_release = true; complete(&priv->compl); return IRQ_HANDLED; } static void get_host_shm_base_addr(struct rproc_remote_priv *priv) { priv->host_shm_phys = readl_relaxed(priv->reg_base + RPROC_REG_SHM_BASE_LOW); priv->host_shm_phys |= (u64)readl_relaxed(priv->reg_base + RPROC_REG_SHM_BASE_HIGH) << 32; } static int handle_idle(struct rproc_remote_priv *priv) { u32 peer_online = readl_relaxed(priv->reg_base + RPROC_REG_PEER_ONLINE); if (peer_online && priv->ready) { get_host_shm_base_addr(priv); writel_relaxed(0, priv->reg_base + RPROC_REG_SET_READY); complete(&priv->compl); return CREATING_VDEV; } return IDLE; } static int handle_creating_vdev(struct rproc_remote_priv *priv) { int ret = create_rproc_vdev(priv); if (ret == 0) { return VDEV_CREATED; } else { return IDLE; } } static int handle_vdev_created(struct rproc_remote_priv *priv) { u32 peer_online = readl_relaxed(priv->reg_base + RPROC_REG_PEER_ONLINE); if (priv->force_offline) peer_online = false; if (!peer_online || !priv->ready) { writel_relaxed(0, priv->reg_base + RPROC_REG_CLEAR_READY); complete(&priv->compl); return DESTROYING_VDEV; } return VDEV_CREATED; } static int handle_destroying_vdev(struct rproc_remote_priv *priv) { rproc_vdev_destroy(priv); return IDLE; } static int create_vdev(void *args) { struct rproc_remote_priv *priv = (struct rproc_remote_priv *)args; int state, next_state; while (!kthread_should_stop()) { wait_for_completion_interruptible(&priv->compl); state = get_state(priv); switch (state) { case IDLE: next_state = handle_idle(priv); break; case CREATING_VDEV: next_state = handle_creating_vdev(priv); break; case VDEV_CREATED: next_state = handle_vdev_created(priv); break; case DESTROYING_VDEV: pr_err("create_vdev DESTROYING_VDEV \n"); next_state = handle_destroying_vdev(priv); break; default: BUG(); } set_state(priv, next_state); } return 0; } static ssize_t ready_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); return sprintf(buf, "%u\n", priv->ready); } static ssize_t ready_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); bool ready; int ret; ret = strtobool(buf, &ready); if (ret < 0) return ret; priv->ready = ready; complete(&priv->compl); return ret == 0 ? size : ret; } static DEVICE_ATTR_RW(ready); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); return sprintf(buf, "%s\n", state_string[get_state(priv)]); } static DEVICE_ATTR_RO(state); static ssize_t peer_vmid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); return sprintf(buf, "%d\n", readl_relaxed(priv->reg_base + RPROC_REG_PEER_VMID)); } static DEVICE_ATTR_RO(peer_vmid); static ssize_t peer_online_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); return sprintf(buf, "%d\n", readl_relaxed(priv->reg_base + RPROC_REG_PEER_ONLINE)); } static DEVICE_ATTR_RO(peer_online); static ssize_t resource_table_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); int len, i, j; struct rproc_resource_table *rsc_table = priv->shm_base; len = snprintf(buf, PAGE_SIZE, "ver=0x%x, num=%u\n", rsc_table->ver, rsc_table->num); if ((len >= PAGE_SIZE) || (len < 0)) return -ENOSPC; for (i = 0; i < rsc_table->num; i++) { struct fw_rsc_vdev *vdev = (void *)rsc_table + rsc_table->offset[i]; len += snprintf( buf + len, PAGE_SIZE - len, " id=0x%x, features=0x%x, status=0x%x, num_vrings=%d\n", vdev->id, vdev->gfeatures, vdev->status, vdev->num_of_vrings); if (len >= PAGE_SIZE) return -ENOSPC; for (j = 0; j < vdev->num_of_vrings; j++) { struct fw_rsc_vdev_vring *vring = &vdev->vring[j]; len += snprintf(buf + len, PAGE_SIZE - len, " vring%d, notifyid=%d, num=%d\n", j, vring->notifyid, vring->num); if (len >= PAGE_SIZE) return -ENOSPC; } } return len; } static DEVICE_ATTR_RO(resource_table); static ssize_t force_offline_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct rproc_remote_priv *priv = dev_get_drvdata(dev); int ret = 0; bool force_offline; ret = strtobool(buf, &force_offline); if (ret < 0) return ret; priv->force_offline = force_offline; complete(&priv->compl); return ret == 0 ? size : ret; } static DEVICE_ATTR_WO(force_offline); static struct attribute *rproc_dev_attrs[] = { &dev_attr_ready.attr, &dev_attr_state.attr, &dev_attr_peer_vmid.attr, &dev_attr_peer_online.attr, &dev_attr_resource_table.attr, &dev_attr_force_offline.attr, NULL, }; static struct attribute_group rproc_dev_group = { .attrs = rproc_dev_attrs, }; static int rproc_remote_probe(struct platform_device *pdev) { struct resource *mem; struct device *dev = &pdev->dev; struct rproc_remote_priv *priv; int ret; void *__iomem reg_base; void *shm_base; size_t shm_size; int i, irq_count, remote_irq_count, virq; struct rproc_resource_table *rsc_table; cpumask_t mask; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { ret = -EINVAL; goto err_free_priv; } reg_base = devm_ioremap_resource(dev, mem); if (IS_ERR(reg_base)) { ret = PTR_ERR(reg_base); goto err_free_priv; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!mem) { ret = -EINVAL; goto err_unmap_reg; } shm_size = resource_size(mem); shm_base = devm_memremap(dev, mem->start, shm_size, MEMREMAP_WB); if (!shm_base) { ret = -EINVAL; goto err_unmap_reg; } dev_info(dev, "shared memory @ %px, size %lx\n", shm_base, shm_size); priv->reg_base = reg_base; priv->shm_base = shm_base; priv->shm_size = shm_size; priv->rsc_table = priv->shm_base; priv->dev = &pdev->dev; priv->shm_io.paddr_to_vaddr = rproc_paddr_to_vaddr; priv->shm_io.vaddr_to_paddr = rproc_vaddr_to_paddr; mutex_init(&priv->rsc_table_mutex); platform_set_drvdata(pdev, priv); irq_count = platform_irq_count(pdev); ret = of_property_read_u32(dev->of_node, "remote_irq_count", &remote_irq_count); BUG_ON(ret != 0); if (remote_irq_count) { dev_info(dev, "notify using physical interrupt\n"); priv->notify_with_phys_irq = true; BUG_ON(remote_irq_count != irq_count - 1); } // bind rproc irq to vcpu4 cpumask_clear(&mask); cpumask_set_cpu(4, &mask); init_completion(&priv->compl); virq = platform_get_irq(pdev, 0); ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, rproc_ctrl_irq_handler, IRQF_ONESHOT, dev_name(&pdev->dev), priv); BUG_ON(ret < 0); irq_set_affinity_hint(virq, &mask); priv->num_queues = irq_count - 1; priv->irq_info = kzalloc(irq_count * sizeof(struct irq_info), GFP_KERNEL); if (!priv->irq_info) goto err_unmap_reg; for (i = 0; i < priv->num_queues; i++) { struct irq_info *irq_info = &priv->irq_info[i]; virq = platform_get_irq(pdev, i + 1); BUG_ON(virq < 0); irq_info->local_virq = virq; if (priv->notify_with_phys_irq) { int hwirq; ret = of_property_read_u32_index( dev->of_node, "remote_irqs", i, &hwirq); BUG_ON(ret < 0); irq_info->remote_hwirq = hwirq; } } rsc_table = priv->shm_base; rsc_table->ver = 1; priv->rsc_table_offset = sizeof(struct rproc_resource_table); INIT_LIST_HEAD(&priv->node); priv->state = IDLE; spin_lock_init(&priv->state_lock); ret = sysfs_create_group(&dev->kobj, &rproc_dev_group); WARN_ON(ret != 0); priv->thread = kthread_run(create_vdev, priv, "rproc_vdev_create"); if (IS_ERR(priv->thread)) { dev_err(priv->dev, "ERROR: failed to start rproc_vdev_create\n"); ret = PTR_ERR(priv->thread); goto err_unmap_reg; } mutex_lock(&rproc_devices_lock); list_add(&priv->node, &rproc_devices); mutex_unlock(&rproc_devices_lock); return 0; err_unmap_reg: devm_iounmap(dev, reg_base); err_free_priv: kfree(priv); return ret; } static const struct of_device_id rproc_remote_of_match[] = { { .compatible = "grt,rproc-remote", }, {}, }; static struct platform_driver nebula_rproc_remote = { .probe = rproc_remote_probe, .driver = { .name = "nebula-rproc-remote", .owner = THIS_MODULE, .of_match_table = rproc_remote_of_match, }, }; static int __init nebula_rproc_remote_init(void) { return platform_driver_register(&nebula_rproc_remote); } module_init(nebula_rproc_remote_init); MODULE_LICENSE("Dual BSD/GPL");
最新发布
08-03
// SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2024 MediaTek Inc. /* * virtio transport for vsock * * Copyright (C) 2013-2015 Red Hat, Inc. * Author: Asias He <asias@redhat.com> * Stefan Hajnoczi <stefanha@redhat.com> * * Some of the code is take from Gerd Hoffmann <kraxel@redhat.com>'s * early virtio-vsock proof-of-concept bits. */ #include <linux/spinlock.h> #include <linux/module.h> #include <linux/list.h> #include <linux/atomic.h> #include <linux/virtio.h> #include <linux/virtio_ids.h> #include <linux/virtio_config.h> #include <linux/virtio_vsock.h> #include <linux/of_irq.h> #include <net/sock.h> #include <linux/mutex.h> #include <net/af_vsock.h> #include <linux/types.h> #include <uapi/linux/sched/types.h> #include <linux/sched.h> #include <linux/kthread.h> static struct workqueue_struct *virtio_vsock_workqueue; static struct virtio_vsock __rcu *the_virtio_vsock; static DEFINE_MUTEX(the_virtio_vsock_mutex); /* protects the_virtio_vsock */ static struct virtio_transport virtio_transport; /* forward declaration */ struct virtio_vsock { struct virtio_device *vdev; struct virtqueue *vqs[VSOCK_VQ_MAX]; /* Virtqueue processing is deferred to a workqueue */ struct work_struct tx_work; struct work_struct rx_work; struct work_struct event_work; /* The following fields are protected by tx_lock. vqs[VSOCK_VQ_TX] * must be accessed with tx_lock held. */ struct mutex tx_lock; bool tx_run; struct work_struct send_pkt_work; spinlock_t send_pkt_list_lock; struct list_head send_pkt_list; atomic_t queued_replies; /* The following fields are protected by rx_lock. vqs[VSOCK_VQ_RX] * must be accessed with rx_lock held. */ struct mutex rx_lock; bool rx_run; int rx_buf_nr; int rx_buf_max_nr; /* The following fields are protected by event_lock. * vqs[VSOCK_VQ_EVENT] must be accessed with event_lock held. */ struct mutex event_lock; bool event_run; struct virtio_vsock_event event_list[8]; u32 guest_cid; bool seqpacket_allow; int num_hwirq_vq; int kick_irq[VSOCK_VQ_MAX]; int notify_irq[VSOCK_VQ_MAX]; }; #include <linux/arm-smccc.h> #define SMC_FC_NBL_VHM_REQ 0xB4000100 #define SMC_HYP_SECURE_ID 1 /* SMC call for hypervisor */ static void virtio_vsock_hw_irq_notify(uint32_t irq) { struct arm_smccc_res res; unsigned long r7 = SMC_HYP_SECURE_ID << 16; arm_smccc_smc(SMC_FC_NBL_VHM_REQ, 0, irq, 0, 0, 0, 0, r7, &res); } static void virtio_vsock_notify(struct virtio_vsock *vsock, int qidx) { struct virtqueue *vq = vsock->vqs[qidx]; int notify_irq = vsock->notify_irq[qidx]; if (notify_irq) { if (virtqueue_kick_prepare(vq)) virtio_vsock_hw_irq_notify(notify_irq); } else { virtqueue_kick(vq); } } static u32 virtio_transport_get_local_cid(void) { struct virtio_vsock *vsock; u32 ret; rcu_read_lock(); vsock = rcu_dereference(the_virtio_vsock); if (!vsock) { ret = VMADDR_CID_ANY; goto out_rcu; } ret = vsock->guest_cid; out_rcu: rcu_read_unlock(); return ret; } static void virtio_transport_send_pkt_work(struct work_struct *work) { struct virtio_vsock *vsock = container_of(work, struct virtio_vsock, send_pkt_work); struct virtqueue *vq; bool added = false; bool restart_rx = false; mutex_lock(&vsock->tx_lock); if (!vsock->tx_run) goto out; vq = vsock->vqs[VSOCK_VQ_TX]; for (;;) { struct virtio_vsock_pkt *pkt; struct scatterlist hdr, buf, *sgs[2]; int ret, in_sg = 0, out_sg = 0; bool reply; spin_lock_bh(&vsock->send_pkt_list_lock); if (list_empty(&vsock->send_pkt_list)) { spin_unlock_bh(&vsock->send_pkt_list_lock); break; } pkt = list_first_entry(&vsock->send_pkt_list, struct virtio_vsock_pkt, list); list_del_init(&pkt->list); spin_unlock_bh(&vsock->send_pkt_list_lock); virtio_transport_deliver_tap_pkt(pkt); reply = pkt->reply; sg_init_one(&hdr, &pkt->hdr, sizeof(pkt->hdr)); sgs[out_sg++] = &hdr; if (pkt->buf) { sg_init_one(&buf, pkt->buf, pkt->len); sgs[out_sg++] = &buf; } ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, pkt, GFP_KERNEL); /* Usually this means that there is no more space available in * the vq */ if (ret < 0) { spin_lock_bh(&vsock->send_pkt_list_lock); list_add(&pkt->list, &vsock->send_pkt_list); spin_unlock_bh(&vsock->send_pkt_list_lock); break; } if (reply) { struct virtqueue *rx_vq = vsock->vqs[VSOCK_VQ_RX]; int val; val = atomic_dec_return(&vsock->queued_replies); /* Do we now have resources to resume rx processing? */ if (val + 1 == virtqueue_get_vring_size(rx_vq)) restart_rx = true; } added = true; } if (added) virtio_vsock_notify(vsock, VSOCK_VQ_TX); out: mutex_unlock(&vsock->tx_lock); if (restart_rx) queue_work(virtio_vsock_workqueue, &vsock->rx_work); } static int virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) { struct virtio_vsock *vsock; int len = pkt->len; rcu_read_lock(); vsock = rcu_dereference(the_virtio_vsock); if (!vsock) { virtio_transport_free_pkt(pkt); len = -ENODEV; goto out_rcu; } if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) { virtio_transport_free_pkt(pkt); len = -ENODEV; goto out_rcu; } if (pkt->reply) atomic_inc(&vsock->queued_replies); spin_lock_bh(&vsock->send_pkt_list_lock); list_add_tail(&pkt->list, &vsock->send_pkt_list); spin_unlock_bh(&vsock->send_pkt_list_lock); queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); out_rcu: rcu_read_unlock(); return len; } static int virtio_transport_cancel_pkt(struct vsock_sock *vsk) { struct virtio_vsock *vsock; struct virtio_vsock_pkt *pkt, *n; int cnt = 0, ret; LIST_HEAD(freeme); rcu_read_lock(); vsock = rcu_dereference(the_virtio_vsock); if (!vsock) { ret = -ENODEV; goto out_rcu; } spin_lock_bh(&vsock->send_pkt_list_lock); list_for_each_entry_safe(pkt, n, &vsock->send_pkt_list, list) { if (pkt->vsk != vsk) continue; list_move(&pkt->list, &freeme); } spin_unlock_bh(&vsock->send_pkt_list_lock); list_for_each_entry_safe(pkt, n, &freeme, list) { if (pkt->reply) cnt++; list_del(&pkt->list); virtio_transport_free_pkt(pkt); } if (cnt) { struct virtqueue *rx_vq = vsock->vqs[VSOCK_VQ_RX]; int new_cnt; new_cnt = atomic_sub_return(cnt, &vsock->queued_replies); if (new_cnt + cnt >= virtqueue_get_vring_size(rx_vq) && new_cnt < virtqueue_get_vring_size(rx_vq)) queue_work(virtio_vsock_workqueue, &vsock->rx_work); } ret = 0; out_rcu: rcu_read_unlock(); return ret; } static void virtio_vsock_rx_fill(struct virtio_vsock *vsock) { int buf_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE; struct virtio_vsock_pkt *pkt; struct scatterlist hdr, buf, *sgs[2]; struct virtqueue *vq; int ret; vq = vsock->vqs[VSOCK_VQ_RX]; do { pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); if (!pkt) break; pkt->buf = kmalloc(buf_len, GFP_KERNEL); if (!pkt->buf) { virtio_transport_free_pkt(pkt); break; } pkt->buf_len = buf_len; pkt->len = buf_len; sg_init_one(&hdr, &pkt->hdr, sizeof(pkt->hdr)); sgs[0] = &hdr; sg_init_one(&buf, pkt->buf, buf_len); sgs[1] = &buf; ret = virtqueue_add_sgs(vq, sgs, 0, 2, pkt, GFP_KERNEL); if (ret) { virtio_transport_free_pkt(pkt); break; } vsock->rx_buf_nr++; } while (vq->num_free); if (vsock->rx_buf_nr > vsock->rx_buf_max_nr) vsock->rx_buf_max_nr = vsock->rx_buf_nr; virtio_vsock_notify(vsock, VSOCK_VQ_RX); } static void virtio_transport_tx_work(struct work_struct *work) { struct virtio_vsock *vsock = container_of(work, struct virtio_vsock, tx_work); struct virtqueue *vq; bool added = false; vq = vsock->vqs[VSOCK_VQ_TX]; mutex_lock(&vsock->tx_lock); if (!vsock->tx_run) goto out; do { struct virtio_vsock_pkt *pkt; unsigned int len; virtqueue_disable_cb(vq); while ((pkt = virtqueue_get_buf(vq, &len)) != NULL) { virtio_transport_free_pkt(pkt); added = true; } } while (!virtqueue_enable_cb(vq)); out: mutex_unlock(&vsock->tx_lock); if (added) queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); } /* Is there space left for replies to rx packets? */ static bool virtio_transport_more_replies(struct virtio_vsock *vsock) { struct virtqueue *vq = vsock->vqs[VSOCK_VQ_RX]; int val; smp_rmb(); /* paired with atomic_inc() and atomic_dec_return() */ val = atomic_read(&vsock->queued_replies); return val < virtqueue_get_vring_size(vq); } /* event_lock must be held */ static int virtio_vsock_event_fill_one(struct virtio_vsock *vsock, struct virtio_vsock_event *event) { struct scatterlist sg; struct virtqueue *vq; vq = vsock->vqs[VSOCK_VQ_EVENT]; sg_init_one(&sg, event, sizeof(*event)); return virtqueue_add_inbuf(vq, &sg, 1, event, GFP_KERNEL); } /* event_lock must be held */ static void virtio_vsock_event_fill(struct virtio_vsock *vsock) { size_t i; for (i = 0; i < ARRAY_SIZE(vsock->event_list); i++) { struct virtio_vsock_event *event = &vsock->event_list[i]; virtio_vsock_event_fill_one(vsock, event); } virtio_vsock_notify(vsock, VSOCK_VQ_EVENT); } static void virtio_vsock_reset_sock(struct sock *sk) { /* vmci_transport.c doesn't take sk_lock here either. At least we're * under vsock_table_lock so the sock cannot disappear while we're * executing. */ sk->sk_state = TCP_CLOSE; sk->sk_err = ECONNRESET; sk_error_report(sk); } static void virtio_vsock_update_guest_cid(struct virtio_vsock *vsock) { struct virtio_device *vdev = vsock->vdev; __le64 guest_cid; vdev->config->get(vdev, offsetof(struct virtio_vsock_config, guest_cid), &guest_cid, sizeof(guest_cid)); vsock->guest_cid = le64_to_cpu(guest_cid); } /* event_lock must be held */ static void virtio_vsock_event_handle(struct virtio_vsock *vsock, struct virtio_vsock_event *event) { switch (le32_to_cpu(event->id)) { case VIRTIO_VSOCK_EVENT_TRANSPORT_RESET: virtio_vsock_update_guest_cid(vsock); vsock_for_each_connected_socket(&virtio_transport.transport, virtio_vsock_reset_sock); break; } } static void virtio_transport_event_work(struct work_struct *work) { struct virtio_vsock *vsock = container_of(work, struct virtio_vsock, event_work); struct virtqueue *vq; vq = vsock->vqs[VSOCK_VQ_EVENT]; mutex_lock(&vsock->event_lock); if (!vsock->event_run) goto out; do { struct virtio_vsock_event *event; unsigned int len; virtqueue_disable_cb(vq); while ((event = virtqueue_get_buf(vq, &len)) != NULL) { if (len == sizeof(*event)) virtio_vsock_event_handle(vsock, event); virtio_vsock_event_fill_one(vsock, event); } } while (!virtqueue_enable_cb(vq)); virtio_vsock_notify(vsock, VSOCK_VQ_EVENT); out: mutex_unlock(&vsock->event_lock); } static void virtio_vsock_event_done(struct virtqueue *vq) { struct virtio_vsock *vsock = vq->vdev->priv; if (!vsock) return; queue_work(virtio_vsock_workqueue, &vsock->event_work); } static void virtio_vsock_tx_done(struct virtqueue *vq) { struct virtio_vsock *vsock = vq->vdev->priv; if (!vsock) return; queue_work(virtio_vsock_workqueue, &vsock->tx_work); } static void virtio_vsock_rx_done(struct virtqueue *vq) { struct virtio_vsock *vsock = vq->vdev->priv; if (!vsock) return; queue_work(virtio_vsock_workqueue, &vsock->rx_work); } static bool virtio_transport_seqpacket_allow(u32 remote_cid); static struct virtio_transport virtio_transport = { .transport = { .module = THIS_MODULE, .get_local_cid = virtio_transport_get_local_cid, .init = virtio_transport_do_socket_init, .destruct = virtio_transport_destruct, .release = virtio_transport_release, .connect = virtio_transport_connect, .shutdown = virtio_transport_shutdown, .cancel_pkt = virtio_transport_cancel_pkt, .dgram_bind = virtio_transport_dgram_bind, .dgram_dequeue = virtio_transport_dgram_dequeue, .dgram_enqueue = virtio_transport_dgram_enqueue, .dgram_allow = virtio_transport_dgram_allow, .stream_dequeue = virtio_transport_stream_dequeue, .stream_enqueue = virtio_transport_stream_enqueue, .stream_has_data = virtio_transport_stream_has_data, .stream_has_space = virtio_transport_stream_has_space, .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, .stream_is_active = virtio_transport_stream_is_active, .stream_allow = virtio_transport_stream_allow, .seqpacket_dequeue = virtio_transport_seqpacket_dequeue, .seqpacket_enqueue = virtio_transport_seqpacket_enqueue, .seqpacket_allow = virtio_transport_seqpacket_allow, .seqpacket_has_data = virtio_transport_seqpacket_has_data, .notify_poll_in = virtio_transport_notify_poll_in, .notify_poll_out = virtio_transport_notify_poll_out, .notify_recv_init = virtio_transport_notify_recv_init, .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, .notify_send_init = virtio_transport_notify_send_init, .notify_send_pre_block = virtio_transport_notify_send_pre_block, .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, .notify_buffer_size = virtio_transport_notify_buffer_size, }, .send_pkt = virtio_transport_send_pkt, }; static bool virtio_transport_seqpacket_allow(u32 remote_cid) { struct virtio_vsock *vsock; bool seqpacket_allow; seqpacket_allow = false; rcu_read_lock(); vsock = rcu_dereference(the_virtio_vsock); if (vsock) seqpacket_allow = vsock->seqpacket_allow; rcu_read_unlock(); return seqpacket_allow; } static void virtio_transport_rx_work(struct work_struct *work) { struct virtio_vsock *vsock = container_of(work, struct virtio_vsock, rx_work); struct virtqueue *vq; vq = vsock->vqs[VSOCK_VQ_RX]; mutex_lock(&vsock->rx_lock); if (!vsock->rx_run) goto out; do { virtqueue_disable_cb(vq); for (;;) { struct virtio_vsock_pkt *pkt; unsigned int len; if (!virtio_transport_more_replies(vsock)) { /* Stop rx until the device processes already * pending replies. Leave rx virtqueue * callbacks disabled. */ goto out; } pkt = virtqueue_get_buf(vq, &len); if (!pkt) { break; } vsock->rx_buf_nr--; /* Drop short/long packets */ if (unlikely(len < sizeof(pkt->hdr) || len > sizeof(pkt->hdr) + pkt->len)) { virtio_transport_free_pkt(pkt); continue; } pkt->len = len - sizeof(pkt->hdr); virtio_transport_deliver_tap_pkt(pkt); virtio_transport_recv_pkt(&virtio_transport, pkt); } } while (!virtqueue_enable_cb(vq)); out: if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2) virtio_vsock_rx_fill(vsock); mutex_unlock(&vsock->rx_lock); } typedef irqreturn_t (*irq_handler_t)(int irq, void *data); static irqreturn_t virtio_vsock_tx_irq_handler(int irq, void *data) { struct virtio_vsock *vsock = data; queue_work(virtio_vsock_workqueue, &vsock->tx_work); return IRQ_HANDLED; } static irqreturn_t virtio_vsock_rx_irq_handler(int irq, void *data) { struct virtio_vsock *vsock = data; queue_work(virtio_vsock_workqueue, &vsock->rx_work); return IRQ_HANDLED; } static irqreturn_t virtio_vsock_event_irq_handler(int irq, void *data) { struct virtio_vsock *vsock = data; queue_work(virtio_vsock_workqueue, &vsock->event_work); return IRQ_HANDLED; } static void virtio_vsock_init_hw_irq(struct virtio_vsock *vsock, irq_handler_t handlers[], size_t num_vq) { struct device_node *irq_node; struct irq_desc *desc; int irq; int ret; int vq_idx; cpumask_t mask; cpumask_clear(&mask); cpumask_set_cpu(5, &mask); irq_node = of_find_compatible_node(NULL, NULL, "nbl,virtio_vsock_irq"); if (irq_node == NULL) { return; } vsock->num_hwirq_vq = num_vq; for (vq_idx = 0; vq_idx < num_vq; vq_idx++) { irq = of_irq_get(irq_node, vq_idx * 2 + 1); BUG_ON(irq == 0); ret = request_irq(irq, handlers[vq_idx], IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "vritio-vsock", vsock); BUG_ON(ret != 0); vsock->kick_irq[vq_idx] = ret; irq_set_affinity_hint(irq, &mask); irq = of_irq_get(irq_node, vq_idx * 2); BUG_ON(irq == 0); desc = irq_to_desc(irq); BUG_ON(desc == NULL); vsock->notify_irq[vq_idx] = desc->irq_data.hwirq; } } static void virtio_vsock_release_hw_irq(struct virtio_vsock *vsock) { int vq_idx; for (vq_idx = 0; vq_idx < vsock->num_hwirq_vq; vq_idx++) { free_irq(vsock->kick_irq[vq_idx], vsock); } } static int virtio_vsock_vqs_init(struct virtio_vsock *vsock) { struct virtio_device *vdev = vsock->vdev; static const char * const names[] = { "rx", "tx", "event", }; vq_callback_t *callbacks[] = { virtio_vsock_rx_done, virtio_vsock_tx_done, virtio_vsock_event_done, }; irq_handler_t hwirq_vq_handlers[] = { virtio_vsock_rx_irq_handler, virtio_vsock_tx_irq_handler, virtio_vsock_event_irq_handler, }; int ret; ret = virtio_find_vqs(vdev, VSOCK_VQ_MAX, vsock->vqs, callbacks, names, NULL); if (ret < 0) return ret; virtio_vsock_update_guest_cid(vsock); virtio_vsock_init_hw_irq(vsock, hwirq_vq_handlers, /*num_vq=*/2); virtio_device_ready(vdev); return 0; } static void virtio_vsock_vqs_start(struct virtio_vsock *vsock) { mutex_lock(&vsock->tx_lock); vsock->tx_run = true; mutex_unlock(&vsock->tx_lock); mutex_lock(&vsock->rx_lock); virtio_vsock_rx_fill(vsock); vsock->rx_run = true; mutex_unlock(&vsock->rx_lock); mutex_lock(&vsock->event_lock); virtio_vsock_event_fill(vsock); vsock->event_run = true; mutex_unlock(&vsock->event_lock); /* virtio_transport_send_pkt() can queue packets once * the_virtio_vsock is set, but they won't be processed until * vsock->tx_run is set to true. We queue vsock->send_pkt_work * when initialization finishes to send those packets queued * earlier. * We don't need to queue the other workers (rx, event) because * as long as we don't fill the queues with empty buffers, the * host can't send us any notification. */ queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); } static void virtio_vsock_vqs_del(struct virtio_vsock *vsock) { struct virtio_device *vdev = vsock->vdev; struct virtio_vsock_pkt *pkt; /* Reset all connected sockets when the VQs disappear */ vsock_for_each_connected_socket(&virtio_transport.transport, virtio_vsock_reset_sock); /* Stop all work handlers to make sure no one is accessing the device, * so we can safely call virtio_reset_device(). */ mutex_lock(&vsock->rx_lock); vsock->rx_run = false; mutex_unlock(&vsock->rx_lock); mutex_lock(&vsock->tx_lock); vsock->tx_run = false; mutex_unlock(&vsock->tx_lock); mutex_lock(&vsock->event_lock); vsock->event_run = false; mutex_unlock(&vsock->event_lock); /* Flush all device writes and interrupts, device will not use any * more buffers. */ virtio_reset_device(vdev); mutex_lock(&vsock->rx_lock); while ((pkt = virtqueue_detach_unused_buf(vsock->vqs[VSOCK_VQ_RX]))) virtio_transport_free_pkt(pkt); mutex_unlock(&vsock->rx_lock); mutex_lock(&vsock->tx_lock); while ((pkt = virtqueue_detach_unused_buf(vsock->vqs[VSOCK_VQ_TX]))) virtio_transport_free_pkt(pkt); mutex_unlock(&vsock->tx_lock); spin_lock_bh(&vsock->send_pkt_list_lock); while (!list_empty(&vsock->send_pkt_list)) { pkt = list_first_entry(&vsock->send_pkt_list, struct virtio_vsock_pkt, list); list_del(&pkt->list); virtio_transport_free_pkt(pkt); } spin_unlock_bh(&vsock->send_pkt_list_lock); virtio_vsock_release_hw_irq(vsock); /* Delete virtqueues and flush outstanding callbacks if any */ vdev->config->del_vqs(vdev); } static int virtio_vsock_probe(struct virtio_device *vdev) { struct virtio_vsock *vsock = NULL; int ret; ret = mutex_lock_interruptible(&the_virtio_vsock_mutex); if (ret) return ret; /* Only one virtio-vsock device per guest is supported */ if (rcu_dereference_protected(the_virtio_vsock, lockdep_is_held(&the_virtio_vsock_mutex))) { ret = -EBUSY; goto out; } vsock = kzalloc(sizeof(*vsock), GFP_KERNEL); if (!vsock) { ret = -ENOMEM; goto out; } vsock->vdev = vdev; vsock->rx_buf_nr = 0; vsock->rx_buf_max_nr = 0; atomic_set(&vsock->queued_replies, 0); mutex_init(&vsock->tx_lock); mutex_init(&vsock->rx_lock); mutex_init(&vsock->event_lock); spin_lock_init(&vsock->send_pkt_list_lock); INIT_LIST_HEAD(&vsock->send_pkt_list); INIT_WORK(&vsock->rx_work, virtio_transport_rx_work); INIT_WORK(&vsock->tx_work, virtio_transport_tx_work); INIT_WORK(&vsock->event_work, virtio_transport_event_work); INIT_WORK(&vsock->send_pkt_work, virtio_transport_send_pkt_work); if (virtio_has_feature(vdev, VIRTIO_VSOCK_F_SEQPACKET)) vsock->seqpacket_allow = true; vdev->priv = vsock; ret = virtio_vsock_vqs_init(vsock); if (ret < 0) goto out; rcu_assign_pointer(the_virtio_vsock, vsock); virtio_vsock_vqs_start(vsock); mutex_unlock(&the_virtio_vsock_mutex); return 0; out: kfree(vsock); mutex_unlock(&the_virtio_vsock_mutex); return ret; } static void virtio_vsock_remove(struct virtio_device *vdev) { struct virtio_vsock *vsock = vdev->priv; mutex_lock(&the_virtio_vsock_mutex); vdev->priv = NULL; rcu_assign_pointer(the_virtio_vsock, NULL); synchronize_rcu(); virtio_vsock_vqs_del(vsock); /* Other works can be queued before 'config->del_vqs()', so we flush * all works before to free the vsock object to avoid use after free. */ flush_work(&vsock->rx_work); flush_work(&vsock->tx_work); flush_work(&vsock->event_work); flush_work(&vsock->send_pkt_work); mutex_unlock(&the_virtio_vsock_mutex); kfree(vsock); } #define CONFIG_PM_SLEEP 1 #ifdef CONFIG_PM_SLEEP static int virtio_vsock_freeze(struct virtio_device *vdev) { struct virtio_vsock *vsock = vdev->priv; mutex_lock(&the_virtio_vsock_mutex); rcu_assign_pointer(the_virtio_vsock, NULL); synchronize_rcu(); virtio_vsock_vqs_del(vsock); //add by konggc struct virtio_vsock *vsock = virtio_get_drvdata(vdev); struct virtio_vsock_pkt *pkt, *tmp; unsigned long flags; vsock->suspended = true; cancel_work_sync(&vsock->rx_fill_work); // 取消未执行的工作 // 释放所有pkt和缓冲区 spin_lock_irqsave(&vsock->rx_lock, flags); list_for_each_entry_safe(pkt, tmp, &vsock->rx_pkts, list) { list_del(&pkt->list); virtqueue_detach_unused_buf(vsock->vqs[VSOCK_VQ_RX], pkt); kfree(pkt->buf); kfree(pkt); } vsock->rx_buf_nr = 0; spin_unlock_irqrestore(&vsock->rx_lock, flags); //end of add by konggc mutex_unlock(&the_virtio_vsock_mutex); return 0; } static int virtio_vsock_restore(struct virtio_device *vdev) { struct virtio_vsock *vsock = vdev->priv; int ret; mutex_lock(&the_virtio_vsock_mutex); /* Only one virtio-vsock device per guest is supported */ if (rcu_dereference_protected(the_virtio_vsock, lockdep_is_held(&the_virtio_vsock_mutex))) { ret = -EBUSY; goto out; } ret = virtio_vsock_vqs_init(vsock); if (ret < 0) goto out; rcu_assign_pointer(the_virtio_vsock, vsock); virtio_vsock_vqs_start(vsock); out: mutex_unlock(&the_virtio_vsock_mutex); return ret; } #endif /* CONFIG_PM_SLEEP */ static struct virtio_device_id id_table[] = { { VIRTIO_ID_VSOCK, VIRTIO_DEV_ANY_ID }, { 0 }, }; static unsigned int features[] = { VIRTIO_VSOCK_F_SEQPACKET }; static struct virtio_driver virtio_vsock_driver = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, .probe = virtio_vsock_probe, .remove = virtio_vsock_remove, #ifdef CONFIG_PM_SLEEP .freeze = virtio_vsock_freeze, .restore = virtio_vsock_restore, #endif }; static int __init virtio_vsock_init(void) { int ret; virtio_vsock_workqueue = alloc_workqueue("virtio_vsock", 0, 0); if (!virtio_vsock_workqueue) return -ENOMEM; ret = vsock_core_register(&virtio_transport.transport, VSOCK_TRANSPORT_F_G2H); if (ret) goto out_wq; ret = register_virtio_driver(&virtio_vsock_driver); if (ret) goto out_vci; return 0; out_vci: vsock_core_unregister(&virtio_transport.transport); out_wq: destroy_workqueue(virtio_vsock_workqueue); return ret; } static void __exit virtio_vsock_exit(void) { unregister_virtio_driver(&virtio_vsock_driver); vsock_core_unregister(&virtio_transport.transport); destroy_workqueue(virtio_vsock_workqueue); } module_init(virtio_vsock_init); module_exit(virtio_vsock_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Asias He"); MODULE_DESCRIPTION("virtio transport for vsock"); MODULE_DEVICE_TABLE(virtio, id_table); 上述是原来的代码,下面是做了优先通过工作队列将分配逻辑延迟到进程上下文,保留GFP_KERNEL的高效分配;强化休眠 / 唤醒阶段的资源管理(提前释放、延迟分配),降低内存竞争风险的相关代码#include <linux/virtio.h> #include <linux/virtio_vsock.h> #include <linux/workqueue.h> #include <linux/spinlock.h> #include <linux/list.h> #include <linux/pm.h> // 扩展VSOCK数据包结构体,添加链表成员用于资源跟踪 struct virtio_vsock_pkt { struct virtio_vsock_hdr hdr; void *buf; size_t buf_len; size_t len; struct list_head list; // 用于链表跟踪所有分配的pkt }; // 扩展VSOCK设备结构体,添加工作队列和资源管理成员 struct virtio_vsock { struct virtqueue *vqs[VSOCK_VQ_NUM]; unsigned int rx_buf_nr; unsigned int rx_buf_max_nr; struct list_head rx_pkts; // 跟踪所有分配的pkt spinlock_t rx_lock; // 保护rx_pkts的自旋锁 struct work_struct rx_fill_work; // 工作队列项(延迟分配) bool suspended; // 标记设备是否处于休眠状态 }; // 工作队列处理函数(运行在进程上下文) static void virtio_vsock_rx_fill_work(struct work_struct *work) { struct virtio_vsock *vsock = container_of(work, struct virtio_vsock, rx_fill_work); // 仅在非休眠状态下执行分配 if (!vsock->suspended) { virtio_vsock_rx_fill(vsock); } } // 改进后的接收缓冲区填充函数(在进程上下文执行) static void virtio_vsock_rx_fill(struct virtio_vsock *vsock) { int buf_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE; struct virtio_vsock_pkt *pkt; struct scatterlist hdr, buf, *sgs[2]; struct virtqueue *vq; int ret; vq = vsock->vqs[VSOCK_VQ_RX]; if (!vq) return; do { // 1. 分配pkt结构体(进程上下文,安全使用GFP_KERNEL) pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); if (!pkt) break; // 2. 分配数据缓冲区 pkt->buf = kmalloc(buf_len, GFP_KERNEL); if (!pkt->buf) { kfree(pkt); break; } // 3. 初始化pkt元信息 pkt->buf_len = buf_len; pkt->len = buf_len; INIT_LIST_HEAD(&pkt->list); // 4. 初始化scatterlist并添加到虚拟队列 sg_init_one(&hdr, &pkt->hdr, sizeof(pkt->hdr)); sgs[0] = &hdr; sg_init_one(&buf, pkt->buf, buf_len); sgs[1] = &buf; ret = virtqueue_add_sgs(vq, sgs, 0, 2, pkt, GFP_KERNEL); if (ret) { kfree(pkt->buf); kfree(pkt); break; } // 5. 将pkt加入跟踪链表(带锁保护) spin_lock(&vsock->rx_lock); list_add(&pkt->list, &vsock->rx_pkts); vsock->rx_buf_nr++; spin_unlock(&vsock->rx_lock); } while (vq->num_free > 0); // 更新最大缓冲区计数 if (vsock->rx_buf_nr > vsock->rx_buf_max_nr) vsock->rx_buf_max_nr = vsock->rx_buf_nr; // 通知设备接收队列已准备好 virtio_vsock_notify(vsock, VSOCK_VQ_RX); } // 休眠回调:释放所有资源 static int virtio_vsock_suspend(struct device *dev) { struct virtio_device *vdev = container_of(dev, struct virtio_device, dev); struct virtio_vsock *vsock = virtio_get_drvdata(vdev); struct virtio_vsock_pkt *pkt, *tmp; unsigned long flags; vsock->suspended = true; cancel_work_sync(&vsock->rx_fill_work); // 取消未执行的工作 // 释放所有pkt和缓冲区 spin_lock_irqsave(&vsock->rx_lock, flags); list_for_each_entry_safe(pkt, tmp, &vsock->rx_pkts, list) { list_del(&pkt->list); virtqueue_detach_unused_buf(vsock->vqs[VSOCK_VQ_RX], pkt); kfree(pkt->buf); kfree(pkt); } vsock->rx_buf_nr = 0; spin_unlock_irqrestore(&vsock->rx_lock, flags); return 0; } // 唤醒回调:调度工作队列重建缓冲区 static int virtio_vsock_resume(struct device *dev) { struct virtio_device *vdev = container_of(dev, struct virtio_device, dev); struct virtio_vsock *vsock = virtio_get_drvdata(vdev); vsock->suspended = false; // 调度工作队列在进程上下文重建缓冲区 if (!work_pending(&vsock->rx_fill_work)) { queue_work(system_wq, &vsock->rx_fill_work); } return 0; } // 电源管理操作结构体 static const struct dev_pm_ops virtio_vsock_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(virtio_vsock_suspend, virtio_vsock_resume) }; // 设备初始化函数 static int virtio_vsock_probe(struct virtio_device *vdev) { struct virtio_vsock *vsock; int ret; vsock = kzalloc(sizeof(*vsock), GFP_KERNEL); if (!vsock) return -ENOMEM; // 初始化虚拟队列(原有逻辑) ret = virtio_vsock_init_vqs(vdev, vsock); if (ret) { kfree(vsock); return ret; } // 初始化资源跟踪与工作队列 INIT_LIST_HEAD(&vsock->rx_pkts); spin_lock_init(&vsock->rx_lock); INIT_WORK(&vsock->rx_fill_work, virtio_vsock_rx_fill_work); vsock->suspended = false; virtio_set_drvdata(vdev, vsock); return 0; } // 设备移除函数 static void virtio_vsock_remove(struct virtio_device *vdev) { struct virtio_vsock *vsock = virtio_get_drvdata(vdev); // 清理逻辑(释放资源等) cancel_work_sync(&vsock->rx_fill_work); virtio_vsock_cleanup_vqs(vsock); kfree(vsock); } // VSOCK设备驱动结构体 static struct virtio_driver virtio_vsock_driver = { .driver = { .name = "virtio-vsock", .owner = THIS_MODULE, .pm = &virtio_vsock_pm_ops, }, .probe = virtio_vsock_probe, .remove = virtio_vsock_remove, // 其他驱动回调(如feature_table等) }; module_virtio_driver(virtio_vsock_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("VirtIO VSOCK driver with suspend/resume optimization"); 现在需要将两份代码进行合并
07-24
### KBUILD_MODNAME 的定义与含义 `KBUILD_MODNAME` 是 Linux 内核构建系统中的一个重要变量,用于指定当前正在编译的内核模块名称。该变量通常由 Kbuild 自动设置,在模块编译过程中起到关键作用。 #### 变量的作用 `KBUILD_MODNAME` 主要用于标识模块的名字,这个名字会被用来生成最终的 `.ko` 文件名[^3]。例如,如果 `KBUILD_MODNAME` 被设置为 `example_module`,那么最终生成的目标文件将是 `example_module.ko`。 此外,`KBUILD_MODNAME` 还可以被模块开发者在源码中引用,以便动态获取模块名称并将其嵌入到日志或其他运行时信息中。这有助于调试和跟踪模块的行为。 #### 自动生成机制 Kbuild 系统会自动推导出 `KBUILD_MODNAME` 的值。具体来说,它是基于 Makefile 中目标文件的基础名称来设定的。例如: ```Makefile obj-m := my_module.o ``` 在这个例子中,`my_module.o` 将触发 Kbuild 设置 `KBUILD_MODNAME=my_module`。 #### 使用场景 以下是几个常见的使用场景: 1. **模块初始化函数**:开发人员可以在模块加载时打印模块名称作为调试信息。 ```c static int __init example_init(void) { printk(KERN_INFO "Loading module %s\n", KBUILD_MODNAME); return 0; } ``` 2. **错误处理**:当发生异常时,可以通过 `KBUILD_MODNAME` 提供上下文信息。 ```c pr_err("Error occurred in module %s\n", KBUILD_MODNAME); ``` 3. **参数注册**:某些情况下,模块可能需要将自己的名称传递给其他子系统。 ```c struct module_param_desc param = { .name = "debug_level", .module = THIS_MODULE }; module_param_call(debug_level, NULL, param_set_int, &param.value, S_IRUGO | S_IWUSR); ``` 需要注意的是,虽然可以直接访问 `KBUILD_MODNAME`,但在实际编程中更推荐使用宏 `THIS_MODULE->name` 来获得模块名称,因为后者更加灵活且兼容性强。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值