FSI changes for v6.1

* Fix a OCC hwmon userspace compatibility regression that was
    introduced in v5.19
 
  * Device tree bindings for the OCC
 
  * A bunch of janitor type fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+nHMAt9PCBDH63wBa3ZZB4FHcJ4FAmM0M4UACgkQa3ZZB4FH
 cJ5kixAAhrKwdGgy/Y0icswgKdixjY7ZkNlVQilKEykMD4KcfarFoRf/6BnyZXMG
 w09nCUjMLmwzYh3ebAEisVv++yNB/FuctCdUjd0UyITqhMkzuVru54ifZuTrcWMw
 8MPRjVZDkbsyDT/gaVRdZgj9FHBybuuQJiGluuw4FgXt78/KBC1dpb/U7zJqsJQt
 eWebyu2giZ/bNm9U4rqZrx0Ld5xOXC/Ry623J3XBbdR9lOgzyAIjbWu6H54sI7eV
 DGsiOLotEPLX4ZZk2VphwaN+WTgV8/ZRfY5VIzH6ZncllJCg8ULCzKLnvF6V3ecr
 j9GLzo9lo0qMcHfN0fiT11jLPWvs6Ji7WjoEc9mGU8NZtVzG+qgJ7z0MQs9IcT1a
 IcrOueCpYfOJjuYey9hWGCIOmm7Fql5f+UPwEkXU6cMFUP0EMtwVp/4ZT51uoZZJ
 JZ4GURMeJnlFLZstCDUhKTg4HiPymzHQPeUjoIsnXAtO4Q45xgaFii2KerIgiTsQ
 5NB2lPqaIbdI2XqoJ/eiTsfEoGppyYKw5bjDcQeICO3nWSNUUjGrdbpvb60XWEBd
 mLDeoTpMBDdDYl6/v1JIvd3jHSsMz2l9X3VshIj6U11TsIsfyvmanY3ac9NljNQF
 Z4W6YHRgGeGM4+jwTsGNXVvel6ZuBO4Y9ld4NOUZWP3x8taYtt0=
 =ZFpc
 -----END PGP SIGNATURE-----

Merge tag 'fsi-for-v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi into char-misc-next

Joel writes:
  "FSI changes for v6.1
    * Fix a OCC hwmon userspace compatibility regression that was
      introduced in v5.19
    * Device tree bindings for the OCC
    * A bunch of janitor type fixes"

* tag 'fsi-for-v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi:
  fsi: core: Check error number after calling ida_simple_get
  hwmon: (occ) Check for device property for setting OCC active during probe
  fsi: occ: Support probing the hwmon child device from dts node
  dt-bindings: hwmon: Add IBM OCC bindings
  fsi: master-ast-cf: Fix missing of_node_put in fsi_master_acf_probe
  fsi: sbefifo: Add detailed debugging information
  fsi: cleanup extern usage in function definition
  fsi: occ: Prevent use after free
  hwmon (occ): Retry for checksum failure
  fsi: occ: Fix checksum failure mode
  fsi: Fix typo in comment
This commit is contained in:
Greg Kroah-Hartman 2022-09-30 14:02:47 +02:00
commit f5e536af48
8 changed files with 143 additions and 29 deletions

View file

@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ibm,occ-hwmon.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IBM On-Chip Controller (OCC) accessed from a service processor
maintainers:
- Eddie James <eajames@linux.ibm.com>
description: |
The POWER processor On-Chip Controller (OCC) helps manage power and
thermals for the system. A service processor or baseboard management
controller can query the OCC for it's power and thermal data to report
through hwmon.
properties:
compatible:
enum:
- ibm,p9-occ-hwmon
- ibm,p10-occ-hwmon
ibm,no-poll-on-init:
description: This property describes whether or not the OCC should
be polled during driver initialization.
type: boolean
required:
- compatible
additionalProperties: false
examples:
- |
hwmon {
compatible = "ibm,p10-occ-hwmon";
ibm,no-poll-on-init;
};

View file

@ -392,8 +392,8 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
}
EXPORT_SYMBOL_GPL(fsi_slave_write);
extern int fsi_slave_claim_range(struct fsi_slave *slave,
uint32_t addr, uint32_t size)
int fsi_slave_claim_range(struct fsi_slave *slave,
uint32_t addr, uint32_t size)
{
if (addr + size < addr)
return -EINVAL;
@ -406,8 +406,8 @@ extern int fsi_slave_claim_range(struct fsi_slave *slave,
}
EXPORT_SYMBOL_GPL(fsi_slave_claim_range);
extern void fsi_slave_release_range(struct fsi_slave *slave,
uint32_t addr, uint32_t size)
void fsi_slave_release_range(struct fsi_slave *slave,
uint32_t addr, uint32_t size)
{
}
EXPORT_SYMBOL_GPL(fsi_slave_release_range);
@ -1314,6 +1314,9 @@ int fsi_master_register(struct fsi_master *master)
mutex_init(&master->scan_lock);
master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
if (master->idx < 0)
return master->idx;
dev_set_name(&master->dev, "fsi%d", master->idx);
master->dev.class = &fsi_master_class;

View file

@ -1324,12 +1324,14 @@ static int fsi_master_acf_probe(struct platform_device *pdev)
}
master->cvic = devm_of_iomap(&pdev->dev, np, 0, NULL);
if (IS_ERR(master->cvic)) {
of_node_put(np);
rc = PTR_ERR(master->cvic);
dev_err(&pdev->dev, "Error %d mapping CVIC\n", rc);
goto err_free;
}
rc = of_property_read_u32(np, "copro-sw-interrupts",
&master->cvic_sw_irq);
of_node_put(np);
if (rc) {
dev_err(&pdev->dev, "Can't find coprocessor SW interrupt\n");
goto err_free;

View file

@ -51,7 +51,7 @@
#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
/* MRESB: Reset brindge */
/* MRESB: Reset bridge */
#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */

View file

@ -44,6 +44,7 @@ struct occ {
struct device *sbefifo;
char name[32];
int idx;
bool platform_hwmon;
u8 sequence_number;
void *buffer;
void *client_buffer;
@ -94,6 +95,7 @@ static int occ_open(struct inode *inode, struct file *file)
client->occ = occ;
mutex_init(&client->lock);
file->private_data = client;
get_device(occ->dev);
/* We allocate a 1-page buffer, make sure it all fits */
BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
@ -197,6 +199,7 @@ static int occ_release(struct inode *inode, struct file *file)
{
struct occ_client *client = file->private_data;
put_device(client->occ->dev);
free_page((unsigned long)client->buffer);
kfree(client);
@ -246,7 +249,7 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
if (checksum != checksum_resp) {
dev_err(occ->dev, "Bad checksum: %04x!=%04x\n", checksum,
checksum_resp);
return -EBADMSG;
return -EBADE;
}
return 0;
@ -493,12 +496,19 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
for (i = 1; i < req_len - 2; ++i)
checksum += byte_request[i];
mutex_lock(&occ->occ_lock);
rc = mutex_lock_interruptible(&occ->occ_lock);
if (rc)
return rc;
occ->client_buffer = response;
occ->client_buffer_size = user_resp_len;
occ->client_response_size = 0;
if (!occ->buffer) {
rc = -ENOENT;
goto done;
}
/*
* Get a sequence number and update the counter. Avoid a sequence
* number of 0 which would pass the response check below even if the
@ -575,8 +585,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n",
resp->return_status, resp_data_length);
occ->client_response_size = resp_data_length + 7;
rc = occ_verify_checksum(occ, resp, resp_data_length);
if (rc)
goto done;
occ->client_response_size = resp_data_length + 7;
done:
*resp_len = occ->client_response_size;
@ -586,7 +599,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
}
EXPORT_SYMBOL_GPL(fsi_occ_submit);
static int occ_unregister_child(struct device *dev, void *data)
static int occ_unregister_platform_child(struct device *dev, void *data)
{
struct platform_device *hwmon_dev = to_platform_device(dev);
@ -595,12 +608,25 @@ static int occ_unregister_child(struct device *dev, void *data)
return 0;
}
static int occ_unregister_of_child(struct device *dev, void *data)
{
struct platform_device *hwmon_dev = to_platform_device(dev);
of_device_unregister(hwmon_dev);
if (dev->of_node)
of_node_clear_flag(dev->of_node, OF_POPULATED);
return 0;
}
static int occ_probe(struct platform_device *pdev)
{
int rc;
u32 reg;
char child_name[32];
struct occ *occ;
struct platform_device *hwmon_dev;
struct platform_device *hwmon_dev = NULL;
struct device_node *hwmon_node;
struct device *dev = &pdev->dev;
struct platform_device_info hwmon_dev_info = {
.parent = dev,
@ -659,10 +685,20 @@ static int occ_probe(struct platform_device *pdev)
return rc;
}
hwmon_dev_info.id = occ->idx;
hwmon_dev = platform_device_register_full(&hwmon_dev_info);
if (IS_ERR(hwmon_dev))
dev_warn(dev, "failed to create hwmon device\n");
hwmon_node = of_get_child_by_name(dev->of_node, hwmon_dev_info.name);
if (hwmon_node) {
snprintf(child_name, sizeof(child_name), "%s.%d", hwmon_dev_info.name, occ->idx);
hwmon_dev = of_platform_device_create(hwmon_node, child_name, dev);
of_node_put(hwmon_node);
}
if (!hwmon_dev) {
occ->platform_hwmon = true;
hwmon_dev_info.id = occ->idx;
hwmon_dev = platform_device_register_full(&hwmon_dev_info);
if (IS_ERR(hwmon_dev))
dev_warn(dev, "failed to create hwmon device\n");
}
return 0;
}
@ -671,11 +707,17 @@ static int occ_remove(struct platform_device *pdev)
{
struct occ *occ = platform_get_drvdata(pdev);
kvfree(occ->buffer);
misc_deregister(&occ->mdev);
device_for_each_child(&pdev->dev, NULL, occ_unregister_child);
mutex_lock(&occ->occ_lock);
kvfree(occ->buffer);
occ->buffer = NULL;
mutex_unlock(&occ->occ_lock);
if (occ->platform_hwmon)
device_for_each_child(&pdev->dev, NULL, occ_unregister_platform_child);
else
device_for_each_child(&pdev->dev, NULL, occ_unregister_of_child);
ida_simple_remove(&occ_ida, occ->idx);

View file

@ -477,7 +477,8 @@ static int sbefifo_wait(struct sbefifo *sbefifo, bool up,
if (!ready) {
sysfs_notify(&sbefifo->dev.kobj, NULL, dev_attr_timeout.attr.name);
sbefifo->timed_out = true;
dev_err(dev, "%s FIFO Timeout ! status=%08x\n", up ? "UP" : "DOWN", sts);
dev_err(dev, "%s FIFO Timeout (%u ms)! status=%08x\n",
up ? "UP" : "DOWN", jiffies_to_msecs(timeout), sts);
return -ETIMEDOUT;
}
dev_vdbg(dev, "End of wait status: %08x\n", sts);
@ -497,8 +498,8 @@ static int sbefifo_send_command(struct sbefifo *sbefifo,
u32 status;
int rc;
dev_vdbg(dev, "sending command (%zd words, cmd=%04x)\n",
cmd_len, be32_to_cpu(command[1]));
dev_dbg(dev, "sending command (%zd words, cmd=%04x)\n",
cmd_len, be32_to_cpu(command[1]));
/* As long as there's something to send */
timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_CMD);
@ -551,21 +552,23 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
size_t len;
int rc;
dev_vdbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response));
dev_dbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response));
timeout = msecs_to_jiffies(sbefifo->timeout_start_rsp_ms);
for (;;) {
/* Grab FIFO status (this will handle parity errors) */
rc = sbefifo_wait(sbefifo, false, &status, timeout);
if (rc < 0)
if (rc < 0) {
dev_dbg(dev, "timeout waiting (%u ms)\n", jiffies_to_msecs(timeout));
return rc;
}
timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_RSP);
/* Decode status */
len = sbefifo_populated(status);
eot_set = sbefifo_eot_set(status);
dev_vdbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set);
dev_dbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set);
/* Go through the chunk */
while(len--) {

View file

@ -10,6 +10,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/sysfs.h>
#include <asm/unaligned.h>
@ -1216,8 +1217,16 @@ int occ_setup(struct occ *occ)
occ->groups[0] = &occ->group;
rc = occ_setup_sysfs(occ);
if (rc)
if (rc) {
dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
return rc;
}
if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) {
rc = occ_active(occ, true);
if (rc)
occ_shutdown_sysfs(occ);
}
return rc;
}

View file

@ -7,6 +7,7 @@
#include <linux/fsi-occ.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/string.h>
@ -14,6 +15,8 @@
#include "common.h"
#define OCC_CHECKSUM_RETRIES 3
struct p9_sbe_occ {
struct occ occ;
bool sbe_error;
@ -80,18 +83,23 @@ static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
void *resp, size_t resp_len)
{
size_t original_resp_len = resp_len;
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
int rc;
int rc, i;
rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
if (rc < 0) {
for (i = 0; i < OCC_CHECKSUM_RETRIES; ++i) {
rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
if (rc >= 0)
break;
if (resp_len) {
if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len))
sysfs_notify(&occ->bus_dev->kobj, NULL,
bin_attr_ffdc.attr.name);
return rc;
}
return rc;
if (rc != -EBADE)
return rc;
resp_len = original_resp_len;
}
switch (((struct occ_response *)resp)->return_status) {
@ -174,9 +182,17 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
return 0;
}
static const struct of_device_id p9_sbe_occ_of_match[] = {
{ .compatible = "ibm,p9-occ-hwmon" },
{ .compatible = "ibm,p10-occ-hwmon" },
{}
};
MODULE_DEVICE_TABLE(of, p9_sbe_occ_of_match);
static struct platform_driver p9_sbe_occ_driver = {
.driver = {
.name = "occ-hwmon",
.of_match_table = p9_sbe_occ_of_match,
},
.probe = p9_sbe_occ_probe,
.remove = p9_sbe_occ_remove,