All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dan Carpenter <dan.carpenter@oracle.com>
To: "Czerwacki, Eial" <eial.czerwacki@sap.com>
Cc: "linux-staging@lists.linux.dev" <linux-staging@lists.linux.dev>,
	SAP vSMP Linux Maintainer <linux.vsmp@sap.com>
Subject: Re: [RFC] staging/vSMP: new driver
Date: Thu, 17 Mar 2022 13:19:43 +0300	[thread overview]
Message-ID: <20220317101942.GX3293@kadam> (raw)
In-Reply-To: <PAXPR02MB73106333FD8FA483FAA0DAC181119@PAXPR02MB7310.eurprd02.prod.outlook.com>

"new driver" is too vague.  What is the driver for?

You need a README or a TODO which explains how to move the driver out of
staging and into the main kernel.

Run the patch through scripts/checkpatch.pl --strict.  You don't have
to fix everything because this is staging but a bunch of the style
issues are easy to fix so we may as well just do it.

There are a number of bugs and style issues.
1) Find and replace every -1 with a define or a proper error code
2) Never do assignments inside of a condition
3) Do success handling instead of error handling.  Try to return errors
   as directly as possible.

Bad:
	ret = frob();
	if (!ret)
		printk("success!");
	return ret;

Good:
	ret = frob();
	if (ret)
		return ret;
	printk("success!");
	return 0;

Keep scrolling down for the details.

On Wed, Mar 16, 2022 at 06:13:04PM +0000, Czerwacki, Eial wrote:
> Introducing the vSMP guest driver which allows interaction with the vSMP control device when
> running a Linux OS atop of the vSMP hypervisor.
> vSMP is a resource aggregation hypervisor from SAP.
> 
> the driver comprises of 3 modules, vsmp which includes all the api needed to interact with the
> control driver, vsmp_logs which allows reading logs from the hypervisor and vsmp_common_info which
> allows reading generic information the hypervisor exposes, currently only the version is exposed.
> 
> Signed-off-by: Eial Czerwacki <eial.czerwacki@sap.com>
> ---
>  MAINTAINERS                             |   6 +
>  drivers/staging/Kconfig                 |   2 +
>  drivers/staging/Makefile                |   1 +
>  drivers/staging/vsmp/Kconfig            |  14 +
>  drivers/staging/vsmp/Makefile           |   7 +
>  drivers/staging/vsmp/api.c              | 402 ++++++++++++++++++++++++
>  drivers/staging/vsmp/api.h              |  61 ++++
>  drivers/staging/vsmp/common/Kconfig     |  11 +
>  drivers/staging/vsmp/common/Makefile    |   7 +
>  drivers/staging/vsmp/common/common.c    |  64 ++++
>  drivers/staging/vsmp/common/common.h    |  27 ++
>  drivers/staging/vsmp/common/version.c   |  85 +++++
>  drivers/staging/vsmp/logs/Kconfig       |  10 +
>  drivers/staging/vsmp/logs/Makefile      |   7 +
>  drivers/staging/vsmp/logs/active_logs.c | 112 +++++++
>  drivers/staging/vsmp/registers.h        |  16 +
>  16 files changed, 832 insertions(+)
>  create mode 100644 drivers/staging/vsmp/Kconfig
>  create mode 100644 drivers/staging/vsmp/Makefile
>  create mode 100644 drivers/staging/vsmp/api.c
>  create mode 100644 drivers/staging/vsmp/api.h
>  create mode 100644 drivers/staging/vsmp/common/Kconfig
>  create mode 100644 drivers/staging/vsmp/common/Makefile
>  create mode 100644 drivers/staging/vsmp/common/common.c
>  create mode 100644 drivers/staging/vsmp/common/common.h
>  create mode 100644 drivers/staging/vsmp/common/version.c
>  create mode 100644 drivers/staging/vsmp/logs/Kconfig
>  create mode 100644 drivers/staging/vsmp/logs/Makefile
>  create mode 100644 drivers/staging/vsmp/logs/active_logs.c
>  create mode 100644 drivers/staging/vsmp/registers.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 68f21d46614c..2fc61b4d13e5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18381,6 +18381,12 @@ F:	Documentation/core-api/printk-formats.rst
>  F:	lib/test_printf.c
>  F:	lib/vsprintf.c
>  
> +VSMP GUEST DRIVER
> +M:     Eial Czerwacki <eial.czerwacki@sap.com>
> +M:     linux-vsmp@sap.com
> +S:     Maintained
> +F:     drivers/staging/vsmp-guest 
> +
>  VT1211 HARDWARE MONITOR DRIVER
>  M:	Juerg Haefliger <juergh@gmail.com>
>  L:	linux-hwmon@vger.kernel.org
> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
> index 4ec5528f89fa..f61778bccb0b 100644
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -114,6 +114,8 @@ source "drivers/staging/axis-fifo/Kconfig"
>  
>  source "drivers/staging/fieldbus/Kconfig"
>  
> +source "drivers/staging/vsmp/Kconfig"
> +
>  source "drivers/staging/kpc2000/Kconfig"
>  
>  source "drivers/staging/qlge/Kconfig"
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index 4d34198151b3..726e704ba8bc 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -47,6 +47,7 @@ obj-$(CONFIG_SOC_MT7621)	+= mt7621-dts/
>  obj-$(CONFIG_STAGING_GASKET_FRAMEWORK)	+= gasket/
>  obj-$(CONFIG_XIL_AXIS_FIFO)	+= axis-fifo/
>  obj-$(CONFIG_FIELDBUS_DEV)     += fieldbus/
> +obj-$(CONFIG_VSMP)       += vsmp/
>  obj-$(CONFIG_KPC2000)		+= kpc2000/
>  obj-$(CONFIG_QLGE)		+= qlge/
>  obj-$(CONFIG_WFX)		+= wfx/
> diff --git a/drivers/staging/vsmp/Kconfig b/drivers/staging/vsmp/Kconfig
> new file mode 100644
> index 000000000000..c57cfcb55033
> --- /dev/null
> +++ b/drivers/staging/vsmp/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +menuconfig VSMP
> +	tristate "vSMP Guest Support"
> +	depends on SYS_HYPERVISOR && X86_64 && PCI
> +	help
> +	  Support for vSMP Guest Driver.
> +
> +	  this drivers allows information gathering of data from the vSMP hypervisor when
> +	  running ontop of a vSMP based VM.
> +
> +	  If unsure, say no.
> +
> +source "drivers/staging/vsmp/common/Kconfig"
> +source "drivers/staging/vsmp/logs/Kconfig"
> diff --git a/drivers/staging/vsmp/Makefile b/drivers/staging/vsmp/Makefile
> new file mode 100644
> index 000000000000..88625cd3707d
> --- /dev/null
> +++ b/drivers/staging/vsmp/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for vSMP Guest drivers.
> +#
> +
> +obj-$(CONFIG_VSMP)	+= vsmp.o common/ logs/
> +vsmp-y			:= api.o
> diff --git a/drivers/staging/vsmp/api.c b/drivers/staging/vsmp/api.c
> new file mode 100644
> index 000000000000..b0d4b5a990d4
> --- /dev/null
> +++ b/drivers/staging/vsmp/api.c
> @@ -0,0 +1,402 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * vSMP api
> + * Copyright (C) 2022 SAP.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/pci_regs.h>
> +#include <linux/pci.h>
> +#include <linux/kobject.h>
> +#include <linux/kernel.h>
> +
> +#include <asm/io.h>
> +
> +#include "api.h"
> +
> +#define DRIVER_LICENSE "GPL"
> +#define DRIVER_AUTHOR "Eial Czerwacki <eial.czerwacki@sap.com>"
> +#define DRIVER_DESC "vSMP api driver"
> +#define DRIVER_VERSION "0.1"
> +
> +#define PCI_DEVICE_ID_SAP_FLX_VSMP_CTL 0x1011
> +#define VSMP_CTL_MAX_PCI_BARS 2
> +
> +/* modules info */
> +MODULE_LICENSE(DRIVER_LICENSE);
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_VERSION(DRIVER_VERSION);
> +
> +static unsigned int vsmp_bus = 0;
> +static unsigned short vsmp_dev = 0x1f;
> +static unsigned char vsmp_func = 0;
> +static bool vsmp_bdf_static = false;
> +static char *vsmp_ctrl_pci_addr = "";
> +module_param(vsmp_ctrl_pci_addr, charp, 0660);
> +
> +/* defines */
> +// copied from arch/x86/pci/direct.c
> +#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
> +	   (0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
> +	   | (devfn << 8) | (reg & 0xFC))

Put the operator on the first line.

> +
> +/* global vars */
> +void __iomem *cfg_addr = NULL;
> +static struct kobject *vsmp_sysfs_kobj;
> +static struct pci_dev *vsmp_dev_obj = NULL;
> +
> +static const struct pci_device_id pci_tbl[] = {
> +	{ PCI_VDEVICE(SCALEMP, PCI_DEVICE_ID_SAP_FLX_VSMP_CTL), 0, },
> +	{ 0, },			/* terminate list */
> +};
> +
> +static struct {
> +	uint64_t start;
> +	uint64_t len;
> +} mapped_bars[VSMP_CTL_MAX_PCI_BARS] = { 0 };
> +
> +/* internal functions */
> +
> +/**
> + * parses the pci address of a given address where the vSMP
> + * pci device is found
> + */
> +static void parse_vsmp_ctrl_pci_addr(void)
> +{
> +	if (strlen(vsmp_ctrl_pci_addr)) {

Reverse this condition.

	if (strlen(vsmp_ctrl_pci_addr) != 0)
		return;

> +		unsigned int bus;
> +		unsigned short dev;
> +		unsigned char func;
> +
> +		if (sscanf(vsmp_ctrl_pci_addr, "%04x:%02hx.%1hhx", &bus, &dev,
> +			   &func) == 3) {

Same:

	if (sscanf(vsmp_ctrl_pci_addr, "%04x:%02hx.%1hhx", &bus, &dev,
		   &func) != 3)
		return;


> +			vsmp_bus = bus;
> +			vsmp_dev = dev;
> +			vsmp_func = func;
> +			vsmp_bdf_static = true;
> +		}
> +	}
> +}
> +
> +/**
> + * queries as specfic device in order to determine if it is a vSMP pci device
> + */
> +static void query_pci_bus_for_vsmp_directly(unsigned int default_bus,
> +					    unsigned int devfn)
> +{
> +	printk(KERN_INFO
> +	       "vSMP pci controller not found in devices tree, trying directly at %x:%x.%x...\n",
> +	       vsmp_bus, vsmp_dev, vsmp_func);
> +
> +	outl(PCI_CONF1_ADDRESS(vsmp_bus, devfn, PCI_BASE_ADDRESS_0), 0xcf8);
> +	mapped_bars[0].start = inl(0xcfc);

Doesn't this need to set mapped_bars[0].len as well?

> +}
> +
> +/**
> + * scan all the devices on the system in order to locate the
> + * vSMP pic device for a given dev end func is provided
> + */
> +static void scan_pci_devs_list_for_vsmp(unsigned int devfn)

This function should return negatives if we do not find a device.
The current error hanlding is convoluted and relies on globals.

> +{
> +	int found, i;

"found" is never set to false.  Make it a bool.

> +	struct pci_dev *pdev = NULL;

Do not initialize "pdev" to a bogus value.

> +	const struct pci_device_id *ent;
> +
> +	for_each_pci_dev(pdev) {
> +		if (devfn != -1) {

Make this -1 a define instead of a magic number.

> +			if ((pdev->bus->number == vsmp_bus) &&
> +			    (devfn == pdev->devfn)) {
> +				found = 1;
> +				break;
> +			}
> +		} else {
> +			ent = pci_match_id(pci_tbl, pdev);
> +			if (ent) {
> +				found = 1;
> +				vsmp_bus = pdev->bus->number;
> +				vsmp_dev = PCI_SLOT(pdev->devfn);
> +				vsmp_func = PCI_FUNC(pdev->devfn);
> +				break;
> +			}
> +		}
> +	}

	if (!found)
		return -ENODEV;

> +
> +	for (i = 0; (i < ARRAY_SIZE(mapped_bars)) && found; i++) {
> +		mapped_bars[i].start = pci_resource_start(pdev, i);
> +		mapped_bars[i].len = pci_resource_len(pdev, i);
> +	}
> +	vsmp_dev_obj = pdev;
> +}
> +
> +/**
> + * open the cfg address space of the vSDP device
> + */
> +static int open_cfg_addr(void)
> +{
> +	unsigned int devfn = (vsmp_bdf_static) ? PCI_DEVFN(vsmp_dev, vsmp_func)
> +						: -1;
> +
> +	scan_pci_devs_list_for_vsmp(devfn);
> +	if (!mapped_bars[0].start)
> +		query_pci_bus_for_vsmp_directly(0, devfn);
> +
> +	if (!mapped_bars[0].len) {
> +		printk(KERN_INFO "vSMP pci controller not found, exiting.\n");
> +		return -1;

Return proper kernel error codes.  "return -EINVAL;"

> +	}
> +
> +	printk(KERN_INFO "mapping bar 0: [0x%llx,0x%llx]\n",
> +	       mapped_bars[0].start, mapped_bars[0].start + mapped_bars[0].len);
> +	cfg_addr = ioremap(mapped_bars[0].start, mapped_bars[0].len);
> +
> +	if (!cfg_addr) {

Delete the blank link between the call and the error checking.  They are
part of the same thing.

> +		printk(KERN_ERR
> +		       "failed to map vSMP pci controller at %x:%x.%x, exiting.\n",
> +		       vsmp_bus, vsmp_dev, vsmp_func);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* exported functions */
> +
> +/**
> + * read a value from a specfic register in the vSMP's device config space
> + */
> +unsigned long vsmp_read_reg_from_cfg(unsigned int reg, enum reg_size_type type)


Use u64 when 64 bit types are intended.

> +{
> +	unsigned long ret_val;


u64 ret_val;

> +
> +	switch (type) {
> +	case VSMP_CTL_REG_SIZE_8BIT:
> +		ret_val = readb(cfg_addr + reg);
> +		break;
> +
> +	case VSMP_CTL_REG_SIZE_16BIT:
> +		ret_val = readw(cfg_addr + reg);
> +		break;
> +
> +	case VSMP_CTL_REG_SIZE_32BIT:
> +		ret_val = readl(cfg_addr + reg);
> +		break;
> +
> +	case VSMP_CTL_REG_SIZE_64BIT:
> +		ret_val = readq(cfg_addr + reg);
> +		break;
> +
> +	default:
> +		printk(KERN_ERR "unsupported reg size type %d.\n", type);
> +		ret_val = (unsigned long)(-1);


64 bit types.  "ret_val = -1ULL;"


> +	}
> +
> +	dev_dbg(&vsmp_dev_obj->dev, "%s: read 0x%lx from reg 0x%x of %d bits\n",
> +		__func__, ret_val, reg, (type + 1) * 8);
> +	return ret_val;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_read_reg_from_cfg);
> +
> +/**
> + * write a value to a specfic register in the vSMP's device config space
> + */
> +void vsmp_write_reg_to_cfg(unsigned int reg, unsigned long value,
> +			   enum reg_size_type type)
> +{
> +	switch (type) {
> +	case VSMP_CTL_REG_SIZE_8BIT:
> +		writeb((unsigned char)value, cfg_addr + reg);
> +		break;
> +
> +	case VSMP_CTL_REG_SIZE_16BIT:
> +		writew((unsigned short)value, cfg_addr + reg);
> +		break;
> +
> +	case VSMP_CTL_REG_SIZE_32BIT:
> +		writel((unsigned int)value, cfg_addr + reg);
> +		break;
> +
> +	case VSMP_CTL_REG_SIZE_64BIT:
> +		writeq(value, cfg_addr + reg);
> +		break;
> +
> +	default:
> +		printk(KERN_ERR "unsupported reg size type %d.\n", type);
> +	}
> +
> +	dev_dbg(&vsmp_dev_obj->dev, "%s: wrote 0x%x to reg 0x%x of %u bits\n",
> +		__func__, reg, reg, (type + 1) * 8);
> +}
> +EXPORT_SYMBOL_GPL(vsmp_write_reg_to_cfg);
> +
> +/**
> + * reag a buffer from a specific offset in a specific bar, maxed to a predefined len size-wise from the vSMP device
> + */
> +unsigned int vsmp_read_buff_from_bar(unsigned int bar, unsigned int offset,

This is unsigned int but it returns zero or negative error codes.

Create a separate function for "halt_on_null".  It will be cleaner that
way.

> +				     char *out, unsigned int len,
> +				     bool halt_on_null)
> +{
> +	unsigned char __iomem *buff;
> +
> +	BUG_ON(!mapped_bars[bar].start);
> +	BUG_ON(ARRAY_SIZE(mapped_bars) <= bar);
> +	BUG_ON((offset + len) > mapped_bars[bar].len);
> +
> +	buff = ioremap(mapped_bars[bar].start + offset, len);
> +	if (!buff)
> +		return -ENOMEM;
> +
> +	if (halt_on_null) {
> +		int i;
> +		unsigned char c;
> +
> +		for (i = 0; i < len; i++) {
> +			c = ioread8(&buff[i]);
> +			if (!c)
> +				break;
> +			out[i] = c;

Copy the NUL terminator to out[i] before the break.

> +		}
> +	} else
> +		memcpy_fromio(out, buff, len);
> +
> +	iounmap(buff);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_read_buff_from_bar);
> +
> +/**
> + * register the vSMP sysfs object for user space interaction
> + */
> +int vsmp_register_sysfs_group(const struct bin_attribute *bin_attr)
> +{
> +	int error = -1;
> +
> +	if (vsmp_sysfs_kobj && bin_attr) {
> +		if ((error = sysfs_create_bin_file(vsmp_sysfs_kobj, bin_attr)))
> +			printk(KERN_CRIT "%s returned error %d\n", __func__,
> +			       error);
> +	}
> +
> +	return error;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_register_sysfs_group);
> +
> +/**
> + * deergister the vSMP sysfs object for user space interaction
> + */
> +void vsmp_deregister_sysfs_group(const struct bin_attribute *bin_attr)
> +{
> +	if (vsmp_sysfs_kobj && bin_attr)
> +		sysfs_remove_bin_file(vsmp_sysfs_kobj, bin_attr);
> +}
> +EXPORT_SYMBOL_GPL(vsmp_deregister_sysfs_group);
> +
> +/**
> + * generic function to read from the bar
> + */
> +ssize_t vsmp_generic_buff_read(struct fw_ops *op, unsigned bar, unsigned reg,
> +			       char *buf, loff_t off, size_t count)
> +{
> +	ssize_t ret_val = 0;
> +
> +	if (op->buff_offset >= op->hwi_block_size) {	// preform H/W op
> +		vsmp_reset_op(op);
> +
> +		if ((ret_val = vsmp_read_buff_from_bar(bar, reg, op->buff,
> +						       op->hwi_block_size, false))) {
> +			printk(KERN_ERR "%s operation failed\n",
> +			       (op->action == FW_READ) ? "read" : "write");
> +		}
> +		op->buff_offset = 0;
> +	}
> +
> +	if (!ret_val) {

Flip this around.  Do error handling.  Don't do success handling.

> +		ret_val = memory_read_from_buffer(buf, count, &op->buff_offset,
> +						  op->buff, op->hwi_block_size);
> +	}
> +
> +	return ret_val;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_generic_buff_read);
> +
> +void vsmp_reset_op(struct fw_ops *op)
> +{
> +	memset(op->buff, 0, op->hwi_block_size);
> +	op->buff_offset = op->hwi_block_size;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_reset_op);
> +
> +int vsmp_init_op(struct fw_ops *op, ssize_t max_size,
> +		  enum vsmp_fw_action action)
> +{
> +	op->hwi_block_size = max_size;
> +	op->action = action;
> +	op->buff_offset = op->hwi_block_size;
> +
> +	if (!(op->buff = kzalloc(op->hwi_block_size, GFP_KERNEL)))
> +		return -ENOMEM;
> +
> +	vsmp_reset_op(op);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_init_op);
> +
> +void vsmp_release_op(struct fw_ops *op)
> +{
> +	BUG_ON(!op->buff);
> +	kfree(op->buff);
> +	memset(op, 0, sizeof(*op));
> +}
> +EXPORT_SYMBOL_GPL(vsmp_release_op);
> +
> +bool vsmp_op_buffer_depleted(struct fw_ops *op)
> +{
> +	return op->buff_offset >= op->hwi_block_size;
> +}
> +EXPORT_SYMBOL_GPL(vsmp_op_buffer_depleted);
> +
> +/* module functions */
> +
> +/**
> + * init the module
> + */
> +static int __init vsmp_api_init(void)
> +{
> +	int ret_val;
> +
> +	parse_vsmp_ctrl_pci_addr();
> +
> +	if (!(ret_val = open_cfg_addr())) {

Error handling not success handling.

> +		printk(KERN_INFO "%s [%02x:%02x.%x] up and running\n",
> +		       DRIVER_DESC, vsmp_bus, vsmp_dev, vsmp_func);
> +
> +		if (!(vsmp_sysfs_kobj = kobject_create_and_add("vsmp",
> +								hypervisor_kobj))) {

Reverse.

> +			printk(KERN_ERR
> +			       "failed to create sysfs entry for vSMP pci controller at %x:%x.%x, exiting.\n",
> +			       vsmp_bus, vsmp_dev, vsmp_func);
> +			ret_val = -1;
> +		}
> +	}
> +
> +	return ret_val;
> +}
> +
> +/**
> + * cleanup after the module upon exit
> + */
> +static void __exit vsmp_api_exit(void)
> +{
> +	if (cfg_addr)
> +		iounmap(cfg_addr);
> +
> +	if (vsmp_sysfs_kobj)
> +		kobject_put(vsmp_sysfs_kobj);
> +}
> +
> +module_init(vsmp_api_init);
> +module_exit(vsmp_api_exit);
> diff --git a/drivers/staging/vsmp/api.h b/drivers/staging/vsmp/api.h
> new file mode 100644
> index 000000000000..88d81b80bd81
> --- /dev/null
> +++ b/drivers/staging/vsmp/api.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * vSMP api header
> + * Copyright (C) 2022 SAP.
> + */
> +
> +#ifndef VSMP_API_H
> +#define VSMP_API_H
> +
> +#include <linux/sysfs.h>
> +
> +enum reg_size_type {
> +	VSMP_CTL_REG_SIZE_8BIT = 0,
> +	VSMP_CTL_REG_SIZE_16BIT,
> +	VSMP_CTL_REG_SIZE_32BIT,
> +	VSMP_CTL_REG_SIZE_64BIT
> +};
> +
> +enum vsmp_fw_action {
> +	FW_READ = 0,
> +	FW_WRITE = 1
> +};
> +
> +struct fw_ops {
> +	enum vsmp_fw_action action;
> +	ssize_t hwi_block_size;
> +	unsigned char *buff;
> +	loff_t buff_offset;
> +	bool in_progress;
> +};
> +
> +#define FILE_PREM (S_IRUSR | S_IRGRP | S_IROTH)
> +
> +#define VSMP_ATTR_RO(_name) static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
> +#define vsmp_read_reg8_from_cfg(_reg_) ((unsigned char) vsmp_read_reg_from_cfg((_reg_), VSMP_CTL_REG_SIZE_8BIT))
> +#define vsmp_read_reg16_from_cfg(_reg_) ((unsigned short) vsmp_read_reg_from_cfg((_reg_), VSMP_CTL_REG_SIZE_16BIT))
> +#define vsmp_read_reg32_from_cfg(_reg_) ((unsigned int) vsmp_read_reg_from_cfg((_reg_), VSMP_CTL_REG_SIZE_32BIT))
> +#define vsmp_read_reg64_from_cfg(_reg_) ((unsigned long) vsmp_read_reg_from_cfg((_reg_), VSMP_CTL_REG_SIZE_64BIT))
> +#define vsmp_write_reg8_to_cfg(_reg_, _val_) vsmp_write_reg_to_cfg((_reg_),(_val_), VSMP_CTL_REG_SIZE_8BIT)
> +#define vsmp_write_reg16_to_cfg(_reg_, _val_) vsmp_write_reg_to_cfg((_reg_), (_val_), VSMP_CTL_REG_SIZE_16BIT)
> +#define vsmp_write_reg32_to_cfg(_reg_, _val_) vsmp_write_reg_to_cfg((_reg_), (_val_), VSMP_CTL_REG_SIZE_32BIT)
> +#define vsmp_write_reg64_to_cfg(_reg_, _val_) vsmp_write_reg_to_cfg((_reg_), (_val_), VSMP_CTL_REG_SIZE_64BIT)
> +
> +unsigned long vsmp_read_reg_from_cfg(unsigned int reg, enum reg_size_type type);
> +void vsmp_write_reg_to_cfg(unsigned int reg, unsigned long value,
> +			   enum reg_size_type type);
> +unsigned int vsmp_read_buff_from_bar(unsigned int bar, unsigned int offset,
> +				     char *out, unsigned int len,
> +				     bool halt_on_null);
> +int vsmp_register_sysfs_group(const struct bin_attribute *bin_attr);
> +void vsmp_deregister_sysfs_group(const struct bin_attribute *bin_attr);
> +
> +ssize_t vsmp_generic_buff_read(struct fw_ops *op, unsigned bar, unsigned reg,
> +			       char *buf, loff_t off, size_t count);
> +void vsmp_reset_op(struct fw_ops *op);
> +int vsmp_init_op(struct fw_ops *op, ssize_t max_size,
> +		 enum vsmp_fw_action action);
> +void vsmp_release_op(struct fw_ops *op);
> +bool vsmp_op_buffer_depleted(struct fw_ops *op);
> +
> +#endif // VSMP_API_H
> diff --git a/drivers/staging/vsmp/common/Kconfig b/drivers/staging/vsmp/common/Kconfig
> new file mode 100644
> index 000000000000..fe0cfeaca1bb
> --- /dev/null
> +++ b/drivers/staging/vsmp/common/Kconfig
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config VSMP_COMMON
> +	tristate "vSMP Guest driver common Support"
> +	depends on VSMP
> +	help
> +	  select this feature in order to be able to retrive generic data from the vSMP
> +	  VM.
> +	  currently old version is supported.
> +	  the data can be found under /sys/hypervisor/vsmp
> +
> +	  If unsure, say N.
> diff --git a/drivers/staging/vsmp/common/Makefile b/drivers/staging/vsmp/common/Makefile
> new file mode 100644
> index 000000000000..e6f6aa1d73df
> --- /dev/null
> +++ b/drivers/staging/vsmp/common/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for vSMP Guest common feature.
> +#
> +
> +obj-$(CONFIG_VSMP_COMMON)	+= common_info.o 
> +common_info-y		+= common.o version.o
> diff --git a/drivers/staging/vsmp/common/common.c b/drivers/staging/vsmp/common/common.c
> new file mode 100644
> index 000000000000..31e6551f2b7f
> --- /dev/null
> +++ b/drivers/staging/vsmp/common/common.c
> @@ -0,0 +1,64 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * vSMP common
> + * Copyright (C) 2022 SAP.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci_regs.h>
> +#include <linux/pci.h>
> +#include <linux/kobject.h>
> +#include <linux/kernel.h>
> +
> +#include <asm/io.h>
> +
> +#include "../api.h"
> +#include "common.h"
> +
> +#define DRIVER_LICENSE "GPL"
> +#define DRIVER_AUTHOR "Eial Czerwacki <eial.czerwacki@sap.com>"
> +#define DRIVER_DESC "vSMP common info driver"
> +#define DRIVER_VERSION "0.1"
> +
> +/* modules info */
> +MODULE_LICENSE(DRIVER_LICENSE);
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_VERSION(DRIVER_VERSION);
> +
> +static struct sysfs_entry_cbs cbs_arr[] = {
> +	create_entry(version),
> +};
> +
> +/* module functions */
> +
> +/**
> + * init the common module
> + */
> +static int __init vsmp_common_info_init(void)
> +{
> +	int ret_val = 0, i;
> +
> +	for (i = 0; (i < ARRAY_SIZE(cbs_arr) && !ret_val); i++) {
> +		if ((ret_val = cbs_arr[i].reg_cb()))
> +			printk(KERN_ERR "failed to init sysfs (%d), exiting.\n",
> +			       ret_val);
> +	}
> +
> +	return -1 * ret_val;

sysfs_register_version_cb() already returns negatives.  Why the "-1 *"?

> +}
> +
> +/**
> + * cleanup after the module
> + */
> +static void __exit vsmp_common_info_exit(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(cbs_arr); i++)
> +		cbs_arr[i].dereg_cb();
> +}
> +
> +module_init(vsmp_common_info_init);
> +module_exit(vsmp_common_info_exit);
> diff --git a/drivers/staging/vsmp/common/common.h b/drivers/staging/vsmp/common/common.h
> new file mode 100644
> index 000000000000..08a096628c95
> --- /dev/null
> +++ b/drivers/staging/vsmp/common/common.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * vSMP common header
> + * Copyright (C) 2022 SAP.
> + */
> +
> +#ifndef VSM_COMMON_H
> +#define VSM_COMMON_H
> +
> +#define create_entry(_label_) \
> +	{ \
> +		.reg_cb = sysfs_register_ ## _label_ ## _cb, \
> +		.dereg_cb = sysfs_deregister_ ## _label_ ## _cb, \
> +	}
> +
> +typedef int (*sysfs_register_cb)(void);
> +typedef void (*sysfs_deregister_cb)(void);
> +
> +struct sysfs_entry_cbs {
> +	sysfs_register_cb reg_cb;
> +	sysfs_deregister_cb dereg_cb;
> +};
> +
> +int sysfs_register_version_cb(void);
> +void sysfs_deregister_version_cb(void);
> +
> +#endif // !VSM_COMMON_H
> diff --git a/drivers/staging/vsmp/common/version.c b/drivers/staging/vsmp/common/version.c
> new file mode 100644
> index 000000000000..35b5a7785e9a
> --- /dev/null
> +++ b/drivers/staging/vsmp/common/version.c
> @@ -0,0 +1,85 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * vSMP version
> + * Copyright (C) 2022 SAP.
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/kobject.h>
> +
> +#include "../api.h"
> +#include "../registers.h"
> +
> +#define VERSION_MAX_LEN (1 << 19)
> +
> +static struct fw_ops op;
> +
> +static ssize_t version_read(struct file *filp, struct kobject *kobj,
> +			    struct bin_attribute *bin_attr,
> +			    char *buf, loff_t off, size_t count)
> +{
> +	ssize_t ret_val = vsmp_generic_buff_read(&op, 0,
> +						 vsmp_read_reg32_from_cfg(VSMP_VERSION_REG),
> +						 buf, off, count);

Don't put functions which can fail in the declaration block.

	ret_val = vsmp_generic_buff_read(&op, 0,
					 vsmp_read_reg32_from_cfg(VSMP_VERSION_REG),
					 buf, off, count);

> +
> +	if ((ret_val != -ENOMEM) && ((ret_val != -EINVAL)))

Don't test for specific error codes.

	if (ret_val < 0)
		return ret_val;


> +	    buf[ret_val++] = '\n';
> +
> +	return ret_val;
> +}
> +
> +struct bin_attribute version_raw_attr = __BIN_ATTR(version, FILE_PREM, version_read, NULL, VERSION_MAX_LEN);
> +
> +/**
> + * retrive str in order to determine the proper length.
> + * this is the best way to maintain backwards compatibility with all
> + * vSMP versions.
> + */
> +static int get_version_len(void)
> +{
> +	unsigned reg;
> +	char *version_str = kzalloc(VERSION_MAX_LEN, GFP_ATOMIC | __GFP_NOWARN);

VERSION_MAX_LEN is 524288 bytes.  That's too long.  Create a small
buffer and loop over it until you find the NUL terminator.  Why does
this need to be ATOMIC, are we holding a lock?  Hopefully if we can fix
the length the __GFP_NOWARN will be unnecessary.

> +
> +	if (!version_str)
> +		return -1;
> +
> +	reg = vsmp_read_reg32_from_cfg(VSMP_VERSION_REG);
> +	memset(version_str, 0, VERSION_MAX_LEN);
> +
> +	if (vsmp_read_buff_from_bar(0, reg, version_str, VERSION_MAX_LEN, true)) {
> +		printk(KERN_ERR "failed to read buffer from bar\n");
> +		return -1;
> +	}
> +
> +	version_raw_attr.size = strlen(version_str);

get_version_len() should return the length instead of setting a global.

> +	kfree(version_str);
> +
> +	return 0;
> +}
> +
> +/**
> + * register the version sysfs entry
> + */
> +int sysfs_register_version_cb(void)
> +{
> +	if (get_version_len()) {
> +		printk(KERN_ERR "failed to init vSMP version module\n");
> +		return -1;
> +	}
> +
> +	if (!vsmp_init_op(&op, version_raw_attr.size, FW_READ)) {

This if statement is reversed so this code can never work.

> +		printk(KERN_ERR "failed to init vSMP version op\n");
> +		return -1;
> +	}
> +
> +	return vsmp_register_sysfs_group(&version_raw_attr);
> +}
> +
> +/**
> + * deregister the version sysfs entry
> + */
> +void sysfs_deregister_version_cb(void)
> +{
> +	vsmp_deregister_sysfs_group(&version_raw_attr);
> +	vsmp_release_op(&op);
> +}
> diff --git a/drivers/staging/vsmp/logs/Kconfig b/drivers/staging/vsmp/logs/Kconfig
> new file mode 100644
> index 000000000000..d41491472bab
> --- /dev/null
> +++ b/drivers/staging/vsmp/logs/Kconfig
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config VSMP_LOGS
> +	tristate "vSMP Guest driver logs Support"
> +	depends on VSMP
> +	help
> +	  select this feature in order to be able to retrive logs from the vSMP
> +	  VM.
> +	  the data can be found under /sys/hypervisor/logs
> +
> +	  If unsure, say N.
> diff --git a/drivers/staging/vsmp/logs/Makefile b/drivers/staging/vsmp/logs/Makefile
> new file mode 100644
> index 000000000000..edc679803fe6
> --- /dev/null
> +++ b/drivers/staging/vsmp/logs/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for vSMP Guest common feature.
> +#
> +
> +obj-$(CONFIG_VSMP_LOGS)	+= logs.o 
> +logs-y		+= active_logs.o
> diff --git a/drivers/staging/vsmp/logs/active_logs.c b/drivers/staging/vsmp/logs/active_logs.c
> new file mode 100644
> index 000000000000..91492c72cc6f
> --- /dev/null
> +++ b/drivers/staging/vsmp/logs/active_logs.c
> @@ -0,0 +1,112 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * vSMP logs
> + * Copyright (C) 2022 SAP.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci_regs.h>
> +#include <linux/pci.h>
> +#include <linux/kobject.h>
> +#include <linux/kernel.h>
> +#include <linux/mutex.h>
> +
> +#include <asm/io.h>
> +
> +#include "../api.h"
> +#include "../registers.h"
> +
> +#define DRIVER_LICENSE "GPL"
> +#define DRIVER_AUTHOR "Eial Czerwacki <eial.czerwacki@sap.com>"
> +#define DRIVER_DESC "vSMP logs driver"
> +#define DRIVER_VERSION "0.1"
> +
> +#define LOGS_MASK (1 << 0)
> +
> +/* modules info */
> +MODULE_LICENSE(DRIVER_LICENSE);
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_VERSION(DRIVER_VERSION);

Just put:

MODULE_LICENSE("GPL v2");

Adding unnecessary indirection is against kernel style.  It says v2 at
the top.

> +
> +static struct fw_ops op;
> +static unsigned log_start;
> +DEFINE_MUTEX(log_mutex);
> +
> +/* module functions */
> +static ssize_t log_read(struct file *filp, struct kobject *kobj,
> +			struct bin_attribute *bin_attr,
> +			char *buf, loff_t off, size_t count)
> +{
> +	ssize_t ret_val = 0;
> +
> +	if (!op.in_progress) {
> +		if ((ret_val = mutex_lock_interruptible(&log_mutex))) {

Don't do assignments inside of an if condition.

> +			printk(KERN_ERR
> +			       "error in atemmpt to lock mutex (%lx)\n",
> +			       ret_val);
> +			return ret_val;
> +		}
> +
> +		vsmp_write_reg16_to_cfg(VSMP_LOGS_CTRL_REG,
> +					vsmp_read_reg16_from_cfg
> +					(VSMP_LOGS_CTRL_REG) | LOGS_MASK);
> +		op.in_progress = true;
> +		vsmp_write_reg64_to_cfg(VSMP_LOGS_STATUS_REG, 0);
> +	}
> +
> +	if (vsmp_op_buffer_depleted(&op)) {
> +		if (!(vsmp_read_reg16_from_cfg(VSMP_LOGS_CTRL_REG) & LOGS_MASK)) {
> +			vsmp_reset_op(&op);
> +			op.in_progress = 0;
> +			mutex_unlock(&log_mutex);
> +			return ret_val;

Return a literal when possible. "return 0;" is more clear than
"return ret_val;".

> +		}
> +	}
> +
> +	ret_val = vsmp_generic_buff_read(&op, 1, log_start, buf, off, count);
> +	if (!ret_val || (ret_val == -ENOMEM) || (ret_val == -EINVAL)) {
> +		printk(KERN_ERR "error reading from buffer\n");
> +		mutex_unlock(&log_mutex);
> +		return 0;
> +	}
> +
> +	if (vsmp_op_buffer_depleted(&op)) {
> +		vsmp_write_reg64_to_cfg(VSMP_LOGS_STATUS_REG,
> +					~0LL & ~(1LL << 0));
> +	}
> +
> +	return count;
> +}
> +
> +static struct bin_attribute log_raw_attr = __BIN_ATTR(log, FILE_PREM, log_read, NULL, 0);
> +
> +/**
> + * init the log module
> + */
> +static int __init vsmp_logs_init(void)
> +{
> +	if (!vsmp_init_op(&op, vsmp_read_reg32_from_cfg(VSMP_LOGS_LEN_REG),
> +							 FW_READ)) {
> +		printk(KERN_ERR "failed to init vSMP log op\n");
> +		return -1;
> +	}
> +
> +	log_start = vsmp_read_reg32_from_cfg(VSMP_LOGS_START_REG);
> +	mutex_init(&log_mutex);
> +
> +	return vsmp_register_sysfs_group(&log_raw_attr);

If vsmp_register_sysfs_group() fails then it needs to vsmp_release_op().

I have written a lot about how to write error handling code:
https://lore.kernel.org/all/20210831084735.GL12231@kadam/

regards,
dan carpenter


  parent reply	other threads:[~2022-03-17 10:20 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-16 18:13 [RFC] staging/vSMP: new driver Czerwacki, Eial
2022-03-16 18:31 ` Randy Dunlap
2022-03-16 18:57   ` Czerwacki, Eial
2022-03-17  7:23 ` Greg KH
2022-03-17  7:34   ` Czerwacki, Eial
2022-03-17  7:51     ` Greg KH
2022-03-17  8:17       ` Czerwacki, Eial
2022-03-17  8:35         ` Greg KH
2022-03-17  8:52           ` Czerwacki, Eial
2022-03-17  8:59             ` Greg KH
2022-03-17  9:04               ` Czerwacki, Eial
2022-04-20 11:18                 ` Czerwacki, Eial
2022-04-20 11:24                   ` Greg KH
2022-04-20 11:38                     ` Czerwacki, Eial
2022-04-20 11:42                       ` Greg KH
2022-04-20 11:57                         ` Czerwacki, Eial
2022-04-20 12:17                           ` Greg KH
2022-04-20 12:36                             ` Czerwacki, Eial
2022-04-20 14:24                               ` Greg KH
2022-03-17  7:24 ` Greg KH
2022-03-17  7:38   ` Czerwacki, Eial
2022-03-17  7:52     ` Greg KH
2022-03-17 10:19 ` Dan Carpenter [this message]
2022-03-17 10:27   ` Dan Carpenter
2022-03-17 13:41   ` Czerwacki, Eial
2022-03-17 13:56     ` Dan Carpenter
2022-03-17 14:05       ` Czerwacki, Eial

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220317101942.GX3293@kadam \
    --to=dan.carpenter@oracle.com \
    --cc=eial.czerwacki@sap.com \
    --cc=linux-staging@lists.linux.dev \
    --cc=linux.vsmp@sap.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.