Commit b3c0f877 authored by Mihai Carabas's avatar Mihai Carabas Committed by Greg Kroah-Hartman
Browse files

misc/pvpanic: probe multiple instances



Create the mecahism that allows multiple pvpanic instances to call
pvpanic_probe and receive panic events. A global list will retain all the
mapped addresses where to write panic events.

Signed-off-by: default avatarMihai Carabas <mihai.carabas@oracle.com>
Link: https://lore.kernel.org/r/1616597356-20696-3-git-send-email-mihai.carabas@oracle.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6861d27c
Loading
Loading
Loading
Loading
+26 −16
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/slab.h>

#include <uapi/misc/pvpanic.h>

@@ -23,25 +24,26 @@ MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
MODULE_DESCRIPTION("pvpanic-mmio device driver");
MODULE_LICENSE("GPL");

static void __iomem *base;
static unsigned int capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED;
static unsigned int events;

static ssize_t capability_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{
	return sysfs_emit(buf, "%x\n", capability);
	struct pvpanic_instance *pi = dev_get_drvdata(dev);

	return sysfs_emit(buf, "%x\n", pi->capability);
}
static DEVICE_ATTR_RO(capability);

static ssize_t events_show(struct device *dev,  struct device_attribute *attr, char *buf)
{
	return sysfs_emit(buf, "%x\n", events);
	struct pvpanic_instance *pi = dev_get_drvdata(dev);

	return sysfs_emit(buf, "%x\n", pi->events);
}

static ssize_t events_store(struct device *dev,  struct device_attribute *attr,
			    const char *buf, size_t count)
{
	struct pvpanic_instance *pi = dev_get_drvdata(dev);
	unsigned int tmp;
	int err;

@@ -49,15 +51,12 @@ static ssize_t events_store(struct device *dev, struct device_attribute *attr,
	if (err)
		return err;

	if ((tmp & capability) != tmp)
	if ((tmp & pi->capability) != tmp)
		return -EINVAL;

	events = tmp;

	pvpanic_set_events(events);
	pi->events = tmp;

	return count;

}
static DEVICE_ATTR_RW(events);

@@ -71,7 +70,9 @@ ATTRIBUTE_GROUPS(pvpanic_mmio_dev);
static int pvpanic_mmio_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct pvpanic_instance *pi;
	struct resource *res;
	void __iomem *base;

	res = platform_get_mem_or_io(pdev, 0);
	if (!res)
@@ -92,19 +93,28 @@ static int pvpanic_mmio_probe(struct platform_device *pdev)
		return -EINVAL;
	}

	pi = kmalloc(sizeof(*pi), GFP_ATOMIC);
	if (!pi)
		return -ENOMEM;

	pi->base = base;
	pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED;

	/* initlize capability by RDPT */
	capability &= ioread8(base);
	events = capability;
	pi->capability &= ioread8(base);
	pi->events = pi->capability;

	pvpanic_probe(base, capability);
	dev_set_drvdata(dev, pi);

	return 0;
	return pvpanic_probe(pi);
}

static int pvpanic_mmio_remove(struct platform_device *pdev)
{
	struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev);

	pvpanic_remove();
	pvpanic_remove(pi);
	kfree(pi);

	return 0;
}
+52 −20
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/list.h>

#include <uapi/misc/pvpanic.h>

@@ -23,15 +25,20 @@ MODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>");
MODULE_DESCRIPTION("pvpanic device driver ");
MODULE_LICENSE("GPL");

static void __iomem *base;
static unsigned int capability;
static unsigned int events;
struct list_head pvpanic_list;
spinlock_t pvpanic_lock;

static void
pvpanic_send_event(unsigned int event)
{
	if (event & capability & events)
		iowrite8(event, base);
	struct pvpanic_instance *pi_cur;

	spin_lock(&pvpanic_lock);
	list_for_each_entry(pi_cur, &pvpanic_list, list) {
		if (event & pi_cur->capability & pi_cur->events)
			iowrite8(event, pi_cur->base);
	}
	spin_unlock(&pvpanic_lock);
}

static int
@@ -53,29 +60,54 @@ static struct notifier_block pvpanic_panic_nb = {
	.priority = 1, /* let this called before broken drm_fb_helper */
};

void pvpanic_probe(void __iomem *pbase, unsigned int dev_cap)
int pvpanic_probe(struct pvpanic_instance *pi)
{
	base = pbase;
	capability = dev_cap;
	events = capability;
	if (!pi || !pi->base)
		return -EINVAL;

	if (capability)
		atomic_notifier_chain_register(&panic_notifier_list,
					       &pvpanic_panic_nb);
	spin_lock(&pvpanic_lock);
	list_add(&pi->list, &pvpanic_list);
	spin_unlock(&pvpanic_lock);

	return 0;
}
EXPORT_SYMBOL_GPL(pvpanic_probe);

void pvpanic_remove(void)
void pvpanic_remove(struct pvpanic_instance *pi)
{
	if (capability)
		atomic_notifier_chain_unregister(&panic_notifier_list,
						 &pvpanic_panic_nb);
	base = NULL;
	struct pvpanic_instance *pi_cur, *pi_next;

	if (!pi)
		return;

	spin_lock(&pvpanic_lock);
	list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) {
		if (pi_cur == pi) {
			list_del(&pi_cur->list);
			break;
		}
	}
	spin_unlock(&pvpanic_lock);
}
EXPORT_SYMBOL_GPL(pvpanic_remove);

void pvpanic_set_events(unsigned int dev_events)
static int pvpanic_init(void)
{
	events = dev_events;
	INIT_LIST_HEAD(&pvpanic_list);
	spin_lock_init(&pvpanic_lock);

	atomic_notifier_chain_register(&panic_notifier_list,
				       &pvpanic_panic_nb);

	return 0;
}
EXPORT_SYMBOL_GPL(pvpanic_set_events);

static void pvpanic_exit(void)
{
	atomic_notifier_chain_unregister(&panic_notifier_list,
					 &pvpanic_panic_nb);

}

module_init(pvpanic_init);
module_exit(pvpanic_exit);
+9 −3
Original line number Diff line number Diff line
@@ -8,8 +8,14 @@
#ifndef PVPANIC_H_
#define PVPANIC_H_

void pvpanic_probe(void __iomem *base, unsigned int dev_cap);
void pvpanic_remove(void);
void pvpanic_set_events(unsigned int dev_events);
struct pvpanic_instance {
	void __iomem *base;
	unsigned int capability;
	unsigned int events;
	struct list_head list;
};

int pvpanic_probe(struct pvpanic_instance *pi);
void pvpanic_remove(struct pvpanic_instance *pi);

#endif /* PVPANIC_H_ */