hwmon: (occ) Prevent power cap command overwriting poll response
authorEddie James <eajames@linux.ibm.com>
Tue, 28 Jun 2022 20:30:29 +0000 (15:30 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 7 Jul 2022 15:53:35 +0000 (17:53 +0200)
[ Upstream commit 1bbb2809040a1f9c7c53c9f06c21aa83275ed27b ]

Currently, the response to the power cap command overwrites the
first eight bytes of the poll response, since the commands use
the same buffer. This means that user's get the wrong data between
the time of sending the power cap and the next poll response update.
Fix this by specifying a different buffer for the power cap command
response.

Fixes: 5b5513b88002 ("hwmon: Add On-Chip Controller (OCC) hwmon driver")
Signed-off-by: Eddie James <eajames@linux.ibm.com>
Link: https://lore.kernel.org/r/20220628203029.51747-1-eajames@linux.ibm.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/hwmon/occ/common.c
drivers/hwmon/occ/common.h
drivers/hwmon/occ/p8_i2c.c
drivers/hwmon/occ/p9_sbe.c

index 0cb4a0a6cbc10dd4a0bde6e1dcf065231409cc0e..bbe5e4ef4113cb71bfd191cab1e26b3f40a40adf 100644 (file)
@@ -145,7 +145,7 @@ static int occ_poll(struct occ *occ)
        cmd[6] = 0;                     /* checksum lsb */
 
        /* mutex should already be locked if necessary */
-       rc = occ->send_cmd(occ, cmd, sizeof(cmd));
+       rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp));
        if (rc) {
                occ->last_error = rc;
                if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
@@ -182,6 +182,7 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
 {
        int rc;
        u8 cmd[8];
+       u8 resp[8];
        __be16 user_power_cap_be = cpu_to_be16(user_power_cap);
 
        cmd[0] = 0;     /* sequence number */
@@ -198,7 +199,7 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
        if (rc)
                return rc;
 
-       rc = occ->send_cmd(occ, cmd, sizeof(cmd));
+       rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp));
 
        mutex_unlock(&occ->lock);
 
index 5020117be740f3af58e2eb6136ca2fea09a8e684..7abf191020628e229f2ab5f63020ea3220b81ee8 100644 (file)
@@ -96,7 +96,8 @@ struct occ {
 
        int powr_sample_time_us;        /* average power sample time */
        u8 poll_cmd_data;               /* to perform OCC poll command */
-       int (*send_cmd)(struct occ *occ, u8 *cmd, size_t len);
+       int (*send_cmd)(struct occ *occ, u8 *cmd, size_t len, void *resp,
+                       size_t resp_len);
 
        unsigned long next_update;
        struct mutex lock;              /* lock OCC access */
index 9e61e1fb5142cfeb398ebb1dd5d634cc76d39a1e..c35c07964d856cb12cec63a4ee411e1ec4c57641 100644 (file)
@@ -111,7 +111,8 @@ static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
                                      be32_to_cpu(data1));
 }
 
-static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
+static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
+                              void *resp, size_t resp_len)
 {
        int i, rc;
        unsigned long start;
@@ -120,7 +121,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
        const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
        struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ);
        struct i2c_client *client = ctx->client;
-       struct occ_response *resp = &occ->resp;
+       struct occ_response *or = (struct occ_response *)resp;
 
        start = jiffies;
 
@@ -151,7 +152,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
                        return rc;
 
                /* wait for OCC */
-               if (resp->return_status == OCC_RESP_CMD_IN_PRG) {
+               if (or->return_status == OCC_RESP_CMD_IN_PRG) {
                        rc = -EALREADY;
 
                        if (time_after(jiffies, start + timeout))
@@ -163,7 +164,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
        } while (rc);
 
        /* check the OCC response */
-       switch (resp->return_status) {
+       switch (or->return_status) {
        case OCC_RESP_CMD_IN_PRG:
                rc = -ETIMEDOUT;
                break;
@@ -192,8 +193,8 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
        if (rc < 0)
                return rc;
 
-       data_length = get_unaligned_be16(&resp->data_length);
-       if (data_length > OCC_RESP_DATA_BYTES)
+       data_length = get_unaligned_be16(&or->data_length);
+       if ((data_length + 7) > resp_len)
                return -EMSGSIZE;
 
        /* fetch the rest of the response data */
index 9709f2b9c052a3ad9521be0e71565b16fff2d8db..14923e78e1f324ada95467ebf50f27be7dd51a31 100644 (file)
@@ -16,18 +16,17 @@ struct p9_sbe_occ {
 
 #define to_p9_sbe_occ(x)       container_of((x), struct p9_sbe_occ, occ)
 
-static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
+static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
+                              void *resp, size_t resp_len)
 {
-       struct occ_response *resp = &occ->resp;
        struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
-       size_t resp_len = sizeof(*resp);
        int rc;
 
        rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
        if (rc < 0)
                return rc;
 
-       switch (resp->return_status) {
+       switch (((struct occ_response *)resp)->return_status) {
        case OCC_RESP_CMD_IN_PRG:
                rc = -ETIMEDOUT;
                break;