From patchwork Thu Sep 21 22:43:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eddie James X-Patchwork-Id: 817201 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3xysF250Fgz9t38 for ; Fri, 22 Sep 2017 08:47:18 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3xysF23j98zDrFJ for ; Fri, 22 Sep 2017 08:47:18 +1000 (AEST) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=linux.vnet.ibm.com (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=eajames@linux.vnet.ibm.com; receiver=) Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3xys9h3ln8zDqT0 for ; Fri, 22 Sep 2017 08:44:24 +1000 (AEST) Received: from pps.filterd (m0098396.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v8LMiKdN064324 for ; Thu, 21 Sep 2017 18:44:22 -0400 Received: from e19.ny.us.ibm.com (e19.ny.us.ibm.com [129.33.205.209]) by mx0a-001b2d01.pphosted.com with ESMTP id 2d4jnk4e2e-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 21 Sep 2017 18:44:21 -0400 Received: from localhost by e19.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 21 Sep 2017 18:44:05 -0400 Received: from b01cxnp22034.gho.pok.ibm.com (9.57.198.24) by e19.ny.us.ibm.com (146.89.104.206) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 21 Sep 2017 18:44:02 -0400 Received: from b01ledav001.gho.pok.ibm.com (b01ledav001.gho.pok.ibm.com [9.57.199.106]) by b01cxnp22034.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v8LMi2Is51970088; Thu, 21 Sep 2017 22:44:02 GMT Received: from b01ledav001.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id AE6C12803E; Thu, 21 Sep 2017 18:43:55 -0400 (EDT) Received: from oc3016140333.ibm.com (unknown [9.41.174.252]) by b01ledav001.gho.pok.ibm.com (Postfix) with ESMTP id 250692803F; Thu, 21 Sep 2017 18:43:55 -0400 (EDT) From: Eddie James To: openbmc@lists.ozlabs.org Subject: [PATCH linux dev-4.10 1/3] drivers/fsi/sbefifo: refactor to upstream list state Date: Thu, 21 Sep 2017 17:43:56 -0500 X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com> References: <1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17092122-0056-0000-0000-000003CD0551 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00007775; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000231; SDB=6.00920429; UDB=6.00462502; IPR=6.00700651; BA=6.00005601; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00017240; XFM=3.00000015; UTC=2017-09-21 22:44:03 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17092122-0057-0000-0000-000008040AB9 Message-Id: <1506033838-2078-2-git-send-email-eajames@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-09-21_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=4 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000 definitions=main-1709210305 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: andrew@aj.id.au, "Edward A. James" Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" From: "Edward A. James" Includes various fixes: - check for complete while waiting for data in read() - fix probe if probe fails - general cleanup - slightly safer (earlier) get_client() - reorder some of the remove() operations for safety Signed-off-by: Edward A. James --- drivers/fsi/fsi-sbefifo.c | 329 ++++++++++++++++++++++++-------------------- include/linux/fsi-sbefifo.h | 6 +- 2 files changed, 180 insertions(+), 155 deletions(-) diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index 1c37ff7..5d25ade 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -11,20 +11,27 @@ * GNU General Public License for more details. */ -#include +#include #include -#include +#include #include +#include +#include +#include +#include #include #include #include #include +#include #include #include #include #include +#include #include #include +#include /* * The SBEFIFO is a pipe-like FSI device for communicating with @@ -44,14 +51,15 @@ #define SBEFIFO_EOT_MAGIC 0xffffffff #define SBEFIFO_EOT_ACK 0x14 +#define SBEFIFO_RESCHEDULE msecs_to_jiffies(500) + struct sbefifo { struct timer_list poll_timer; - struct fsi_device *fsi_dev; - struct miscdevice mdev; + struct fsi_device *fsi_dev; /* parent fsi device */ + struct miscdevice mdev; /* /dev/ entry */ wait_queue_head_t wait; - struct list_head link; struct list_head xfrs; - struct kref kref; + struct kref kref; /* reference counter */ spinlock_t lock; char name[32]; int idx; @@ -87,14 +95,12 @@ struct sbefifo_client { unsigned long f_flags; }; -static struct list_head sbefifo_fifos; - static DEFINE_IDA(sbefifo_ida); static int sbefifo_inw(struct sbefifo *sbefifo, int reg, u32 *word) { int rc; - u32 raw_word; + __be32 raw_word; rc = fsi_device_read(sbefifo->fsi_dev, reg, &raw_word, sizeof(raw_word)); @@ -102,17 +108,19 @@ static int sbefifo_inw(struct sbefifo *sbefifo, int reg, u32 *word) return rc; *word = be32_to_cpu(raw_word); + return 0; } static int sbefifo_outw(struct sbefifo *sbefifo, int reg, u32 word) { - u32 raw_word = cpu_to_be32(word); + __be32 raw_word = cpu_to_be32(word); return fsi_device_write(sbefifo->fsi_dev, reg, &raw_word, sizeof(raw_word)); } +/* Don't flip endianness of data to/from FIFO, just pass through. */ static int sbefifo_readw(struct sbefifo *sbefifo, u32 *word) { return fsi_device_read(sbefifo->fsi_dev, SBEFIFO_DWN, word, @@ -136,7 +144,7 @@ static int sbefifo_ack_eot(struct sbefifo *sbefifo) return ret; return sbefifo_outw(sbefifo, SBEFIFO_DWN | SBEFIFO_EOT_ACK, - SBEFIFO_EOT_MAGIC); + SBEFIFO_EOT_MAGIC); } static size_t sbefifo_dev_nwreadable(u32 sts) @@ -210,6 +218,7 @@ static bool sbefifo_buf_readnb(struct sbefifo_buf *buf, size_t n) rpos = buf->buf; WRITE_ONCE(buf->rpos, rpos); + return rpos == wpos; } @@ -229,14 +238,14 @@ static bool sbefifo_buf_wrotenb(struct sbefifo_buf *buf, size_t n) set_bit(SBEFIFO_BUF_FULL, &buf->flags); WRITE_ONCE(buf->wpos, wpos); + return rpos == wpos; } static void sbefifo_free(struct kref *kref) { - struct sbefifo *sbefifo; + struct sbefifo *sbefifo = container_of(kref, struct sbefifo, kref); - sbefifo = container_of(kref, struct sbefifo, kref); kfree(sbefifo); } @@ -255,9 +264,12 @@ static struct sbefifo_xfr *sbefifo_enq_xfr(struct sbefifo_client *client) struct sbefifo *sbefifo = client->dev; struct sbefifo_xfr *xfr; + if (READ_ONCE(sbefifo->rc)) + return ERR_PTR(sbefifo->rc); + xfr = kzalloc(sizeof(*xfr), GFP_KERNEL); if (!xfr) - return NULL; + return ERR_PTR(-ENOMEM); xfr->rbuf = &client->rbuf; xfr->wbuf = &client->wbuf; @@ -267,21 +279,12 @@ static struct sbefifo_xfr *sbefifo_enq_xfr(struct sbefifo_client *client) return xfr; } -static struct sbefifo_xfr *sbefifo_client_next_xfr( - struct sbefifo_client *client) -{ - if (list_empty(&client->xfrs)) - return NULL; - - return container_of(client->xfrs.next, struct sbefifo_xfr, - client); -} - static bool sbefifo_xfr_rsp_pending(struct sbefifo_client *client) { - struct sbefifo_xfr *xfr; + struct sbefifo_xfr *xfr = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, + client); - xfr = sbefifo_client_next_xfr(client); if (xfr && test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags)) return true; @@ -309,10 +312,11 @@ static struct sbefifo_client *sbefifo_new_client(struct sbefifo *sbefifo) static void sbefifo_client_release(struct kref *kref) { - struct sbefifo_client *client; struct sbefifo_xfr *xfr; + struct sbefifo_client *client = container_of(kref, + struct sbefifo_client, + kref); - client = container_of(kref, struct sbefifo_client, kref); list_for_each_entry(xfr, &client->xfrs, client) { /* * The client left with pending or running xfrs. @@ -349,6 +353,7 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo) kfree(xfr); continue; } + return xfr; } @@ -370,7 +375,7 @@ static void sbefifo_poll_timer(unsigned long data) spin_lock(&sbefifo->lock); xfr = list_first_entry_or_null(&sbefifo->xfrs, struct sbefifo_xfr, - xfrs); + xfrs); if (!xfr) goto out_unlock; @@ -388,8 +393,7 @@ static void sbefifo_poll_timer(unsigned long data) /* Drain the write buffer. */ while ((bufn = sbefifo_buf_nbreadable(wbuf))) { - ret = sbefifo_inw(sbefifo, SBEFIFO_UP | SBEFIFO_STS, - &sts); + ret = sbefifo_inw(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &sts); if (ret) goto out; @@ -397,7 +401,7 @@ static void sbefifo_poll_timer(unsigned long data) if (devn == 0) { /* No open slot for write. Reschedule. */ sbefifo->poll_timer.expires = jiffies + - msecs_to_jiffies(500); + SBEFIFO_RESCHEDULE; add_timer(&sbefifo->poll_timer); goto out_unlock; } @@ -414,9 +418,8 @@ static void sbefifo_poll_timer(unsigned long data) /* Send EOT if the writer is finished. */ if (test_and_clear_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags)) { - ret = sbefifo_outw(sbefifo, - SBEFIFO_UP | SBEFIFO_EOT_RAISE, - SBEFIFO_EOT_MAGIC); + ret = sbefifo_outw(sbefifo, SBEFIFO_UP | SBEFIFO_EOT_RAISE, + SBEFIFO_EOT_MAGIC); if (ret) goto out; @@ -438,7 +441,7 @@ static void sbefifo_poll_timer(unsigned long data) if (devn == 0) { /* No data yet. Reschedule. */ sbefifo->poll_timer.expires = jiffies + - msecs_to_jiffies(500); + SBEFIFO_RESCHEDULE; add_timer(&sbefifo->poll_timer); goto out_unlock; } @@ -466,7 +469,7 @@ static void sbefifo_poll_timer(unsigned long data) set_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags); list_del(&xfr->xfrs); if (unlikely(test_bit(SBEFIFO_XFR_CANCEL, - &xfr->flags))) + &xfr->flags))) kfree(xfr); break; } @@ -476,7 +479,7 @@ static void sbefifo_poll_timer(unsigned long data) if (unlikely(ret)) { sbefifo->rc = ret; dev_err(&sbefifo->fsi_dev->dev, - "Fatal bus access failure: %d\n", ret); + "Fatal bus access failure: %d\n", ret); list_for_each_entry(xfr, &sbefifo->xfrs, xfrs) kfree(xfr); INIT_LIST_HEAD(&sbefifo->xfrs); @@ -497,7 +500,7 @@ static void sbefifo_poll_timer(unsigned long data) static int sbefifo_open(struct inode *inode, struct file *file) { struct sbefifo *sbefifo = container_of(file->private_data, - struct sbefifo, mdev); + struct sbefifo, mdev); struct sbefifo_client *client; int ret; @@ -535,6 +538,19 @@ static unsigned int sbefifo_poll(struct file *file, poll_table *wait) return mask; } +static bool sbefifo_read_ready(struct sbefifo *sbefifo, + struct sbefifo_client *client, size_t *n) +{ + struct sbefifo_xfr *xfr = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, + client); + + *n = sbefifo_buf_nbreadable(&client->rbuf); + + return READ_ONCE(sbefifo->rc) || *n || + (xfr && test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)); +} + static ssize_t sbefifo_read_common(struct sbefifo_client *client, char __user *ubuf, char *kbuf, size_t len) { @@ -551,30 +567,38 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client, sbefifo_get_client(client); if (wait_event_interruptible(sbefifo->wait, - (ret = READ_ONCE(sbefifo->rc)) || - (n = sbefifo_buf_nbreadable( - &client->rbuf)))) { - sbefifo_put_client(client); - return -ERESTARTSYS; + sbefifo_read_ready(sbefifo, client, + &n))) { + ret = -ERESTARTSYS; + goto out; } + + ret = READ_ONCE(sbefifo->rc); if (ret) { INIT_LIST_HEAD(&client->xfrs); - sbefifo_put_client(client); - return ret; + goto out; } n = min_t(size_t, n, len); if (ubuf) { if (copy_to_user(ubuf, READ_ONCE(client->rbuf.rpos), n)) { - sbefifo_put_client(client); - return -EFAULT; + ret = -EFAULT; + goto out; } - } else + } else { memcpy(kbuf, READ_ONCE(client->rbuf.rpos), n); + } if (sbefifo_buf_readnb(&client->rbuf, n)) { - xfr = sbefifo_client_next_xfr(client); + xfr = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, client); + if (!xfr) { + /* should be impossible to not have an xfr here */ + wake_up(&sbefifo->wait); + goto out; + } + if (!test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) { /* * Fill the read buffer back up. @@ -589,22 +613,31 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client, } } - sbefifo_put_client(client); + ret = n; - return n; +out: + sbefifo_put_client(client); + return ret; } -static ssize_t sbefifo_read(struct file *file, char __user *buf, - size_t len, loff_t *offset) +static ssize_t sbefifo_read(struct file *file, char __user *buf, size_t len, + loff_t *offset) { struct sbefifo_client *client = file->private_data; - WARN_ON(*offset); + return sbefifo_read_common(client, buf, NULL, len); +} - if (!access_ok(VERIFY_WRITE, buf, len)) - return -EFAULT; +static bool sbefifo_write_ready(struct sbefifo *sbefifo, + struct sbefifo_xfr *xfr, + struct sbefifo_client *client, size_t *n) +{ + struct sbefifo_xfr *next = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, + client); - return sbefifo_read_common(client, buf, NULL, len); + *n = sbefifo_buf_nbwriteable(&client->wbuf); + return READ_ONCE(sbefifo->rc) || (next == xfr && *n); } static ssize_t sbefifo_write_common(struct sbefifo_client *client, @@ -612,6 +645,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, size_t len) { struct sbefifo *sbefifo = client->dev; + struct sbefifo_buf *wbuf = &client->wbuf; struct sbefifo_xfr *xfr; ssize_t ret = 0; size_t n; @@ -622,24 +656,26 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, if (!len) return 0; - n = sbefifo_buf_nbwriteable(&client->wbuf); + sbefifo_get_client(client); + n = sbefifo_buf_nbwriteable(wbuf); spin_lock_irq(&sbefifo->lock); - xfr = sbefifo_next_xfr(sbefifo); + xfr = sbefifo_next_xfr(sbefifo); /* next xfr to be executed */ if ((client->f_flags & O_NONBLOCK) && xfr && n < len) { spin_unlock_irq(&sbefifo->lock); - return -EAGAIN; + ret = -EAGAIN; + goto out; } - xfr = sbefifo_enq_xfr(client); - if (!xfr) { + xfr = sbefifo_enq_xfr(client); /* this xfr queued up */ + if (IS_ERR(xfr)) { spin_unlock_irq(&sbefifo->lock); - return -ENOMEM; + ret = PTR_ERR(xfr); + goto out; } - spin_unlock_irq(&sbefifo->lock); - sbefifo_get_client(client); + spin_unlock_irq(&sbefifo->lock); /* * Partial writes are not really allowed in that EOT is sent exactly @@ -647,49 +683,47 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, */ while (len) { if (wait_event_interruptible(sbefifo->wait, - READ_ONCE(sbefifo->rc) || - (sbefifo_client_next_xfr(client) == xfr && - (n = sbefifo_buf_nbwriteable( - &client->wbuf))))) { + sbefifo_write_ready(sbefifo, xfr, + client, + &n))) { set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags); sbefifo_get(sbefifo); if (mod_timer(&sbefifo->poll_timer, jiffies)) sbefifo_put(sbefifo); - - sbefifo_put_client(client); - return -ERESTARTSYS; + ret = -ERESTARTSYS; + goto out; } - if (sbefifo->rc) { + + ret = READ_ONCE(sbefifo->rc); + if (ret) { INIT_LIST_HEAD(&client->xfrs); - sbefifo_put_client(client); - return sbefifo->rc; + goto out; } n = min_t(size_t, n, len); if (ubuf) { - if (copy_from_user(READ_ONCE(client->wbuf.wpos), ubuf, - n)) { + if (copy_from_user(READ_ONCE(wbuf->wpos), ubuf, n)) { set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags); sbefifo_get(sbefifo); if (mod_timer(&sbefifo->poll_timer, jiffies)) sbefifo_put(sbefifo); - sbefifo_put_client(client); - return -EFAULT; + ret = -EFAULT; + goto out; } ubuf += n; } else { - memcpy(READ_ONCE(client->wbuf.wpos), kbuf, n); + memcpy(READ_ONCE(wbuf->wpos), kbuf, n); kbuf += n; } - sbefifo_buf_wrotenb(&client->wbuf, n); + sbefifo_buf_wrotenb(wbuf, n); len -= n; ret += n; - /* set flag before starting the worker, as it may run through - * and check the flag before we exit this loop! + /* Set this before starting timer to avoid race condition on + * this flag with the timer function writer. */ if (!len) set_bit(SBEFIFO_XFR_WRITE_DONE, &xfr->flags); @@ -702,21 +736,16 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, sbefifo_put(sbefifo); } +out: sbefifo_put_client(client); - return ret; } static ssize_t sbefifo_write(struct file *file, const char __user *buf, - size_t len, loff_t *offset) + size_t len, loff_t *offset) { struct sbefifo_client *client = file->private_data; - WARN_ON(*offset); - - if (!access_ok(VERIFY_READ, buf, len)) - return -EFAULT; - return sbefifo_write_common(client, buf, NULL, len); } @@ -742,18 +771,15 @@ static int sbefifo_release(struct inode *inode, struct file *file) struct sbefifo_client *sbefifo_drv_open(struct device *dev, unsigned long flags) { - struct sbefifo_client *client = NULL; - struct sbefifo *sbefifo; - struct fsi_device *fsi_dev = to_fsi_dev(dev); + struct sbefifo_client *client; + struct sbefifo *sbefifo = dev_get_drvdata(dev); - list_for_each_entry(sbefifo, &sbefifo_fifos, link) { - if (sbefifo->fsi_dev != fsi_dev) - continue; + if (!sbefifo) + return NULL; - client = sbefifo_new_client(sbefifo); - if (client) - client->f_flags = flags; - } + client = sbefifo_new_client(sbefifo); + if (client) + client->f_flags = flags; return client; } @@ -802,95 +828,92 @@ static int sbefifo_probe(struct device *dev) u32 sts; int ret, child_idx = 0; - dev_dbg(dev, "Found sbefifo device\n"); sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL); if (!sbefifo) return -ENOMEM; sbefifo->fsi_dev = fsi_dev; - ret = sbefifo_inw(sbefifo, - SBEFIFO_UP | SBEFIFO_STS, &sts); + ret = sbefifo_inw(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &sts); if (ret) return ret; + if (!(sts & SBEFIFO_EMPTY)) { - dev_err(&sbefifo->fsi_dev->dev, - "Found data in upstream fifo\n"); + dev_err(dev, "Found data in upstream fifo\n"); return -EIO; } ret = sbefifo_inw(sbefifo, SBEFIFO_DWN | SBEFIFO_STS, &sts); if (ret) return ret; + if (!(sts & SBEFIFO_EMPTY)) { - dev_err(&sbefifo->fsi_dev->dev, - "Found data in downstream fifo\n"); + dev_err(dev, "found data in downstream fifo\n"); return -EIO; } - sbefifo->mdev.minor = MISC_DYNAMIC_MINOR; - sbefifo->mdev.fops = &sbefifo_fops; - sbefifo->mdev.name = sbefifo->name; - sbefifo->mdev.parent = dev; spin_lock_init(&sbefifo->lock); kref_init(&sbefifo->kref); + init_waitqueue_head(&sbefifo->wait); + INIT_LIST_HEAD(&sbefifo->xfrs); sbefifo->idx = ida_simple_get(&sbefifo_ida, 1, INT_MAX, GFP_KERNEL); snprintf(sbefifo->name, sizeof(sbefifo->name), "sbefifo%d", - sbefifo->idx); - init_waitqueue_head(&sbefifo->wait); - INIT_LIST_HEAD(&sbefifo->xfrs); + sbefifo->idx); /* This bit of silicon doesn't offer any interrupts... */ setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer, - (unsigned long)sbefifo); - - if (dev->of_node) { - /* create platform devs for dts child nodes (occ, etc) */ - for_each_child_of_node(dev->of_node, np) { - snprintf(child_name, sizeof(child_name), "%s-dev%d", - sbefifo->name, child_idx++); - child = of_platform_device_create(np, child_name, dev); - if (!child) - dev_warn(&sbefifo->fsi_dev->dev, - "failed to create child node dev\n"); - } + (unsigned long)sbefifo); + + sbefifo->mdev.minor = MISC_DYNAMIC_MINOR; + sbefifo->mdev.fops = &sbefifo_fops; + sbefifo->mdev.name = sbefifo->name; + sbefifo->mdev.parent = dev; + ret = misc_register(&sbefifo->mdev); + if (ret) { + dev_err(dev, "failed to register miscdevice: %d\n", ret); + ida_simple_remove(&sbefifo_ida, sbefifo->idx); + sbefifo_put(sbefifo); + return ret; + } + + /* create platform devs for dts child nodes (occ, etc) */ + for_each_available_child_of_node(dev->of_node, np) { + snprintf(child_name, sizeof(child_name), "%s-dev%d", + sbefifo->name, child_idx++); + child = of_platform_device_create(np, child_name, dev); + if (!child) + dev_warn(dev, "failed to create child %s dev\n", + child_name); } - list_add(&sbefifo->link, &sbefifo_fifos); - - return misc_register(&sbefifo->mdev); + dev_set_drvdata(dev, sbefifo); + + return 0; } static int sbefifo_remove(struct device *dev) { - struct fsi_device *fsi_dev = to_fsi_dev(dev); - struct sbefifo *sbefifo, *sbefifo_tmp; + struct sbefifo *sbefifo = dev_get_drvdata(dev); struct sbefifo_xfr *xfr; - list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) { - if (sbefifo->fsi_dev != fsi_dev) - continue; - - device_for_each_child(dev, NULL, sbefifo_unregister_child); + WRITE_ONCE(sbefifo->rc, -ENODEV); + wake_up(&sbefifo->wait); - misc_deregister(&sbefifo->mdev); - list_del(&sbefifo->link); - ida_simple_remove(&sbefifo_ida, sbefifo->idx); + misc_deregister(&sbefifo->mdev); + device_for_each_child(dev, NULL, sbefifo_unregister_child); - if (del_timer_sync(&sbefifo->poll_timer)) - sbefifo_put(sbefifo); + ida_simple_remove(&sbefifo_ida, sbefifo->idx); - spin_lock(&sbefifo->lock); - list_for_each_entry(xfr, &sbefifo->xfrs, xfrs) - kfree(xfr); - spin_unlock(&sbefifo->lock); + if (del_timer_sync(&sbefifo->poll_timer)) + sbefifo_put(sbefifo); - WRITE_ONCE(sbefifo->rc, -ENODEV); + spin_lock(&sbefifo->lock); + list_for_each_entry(xfr, &sbefifo->xfrs, xfrs) + kfree(xfr); + spin_unlock(&sbefifo->lock); - wake_up(&sbefifo->wait); - sbefifo_put(sbefifo); - } + sbefifo_put(sbefifo); return 0; } @@ -915,17 +938,19 @@ static int sbefifo_remove(struct device *dev) static int sbefifo_init(void) { - INIT_LIST_HEAD(&sbefifo_fifos); return fsi_driver_register(&sbefifo_drv); } static void sbefifo_exit(void) { fsi_driver_unregister(&sbefifo_drv); + + ida_destroy(&sbefifo_ida); } module_init(sbefifo_init); module_exit(sbefifo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Brad Bishop "); -MODULE_DESCRIPTION("Linux device interface to the POWER self boot engine"); +MODULE_AUTHOR("Eddie James "); +MODULE_DESCRIPTION("Linux device interface to the POWER Self Boot Engine"); diff --git a/include/linux/fsi-sbefifo.h b/include/linux/fsi-sbefifo.h index 1b46c63..8e55891 100644 --- a/include/linux/fsi-sbefifo.h +++ b/include/linux/fsi-sbefifo.h @@ -13,8 +13,8 @@ * GNU General Public License for more details. */ -#ifndef __FSI_SBEFIFO_H__ -#define __FSI_SBEFIFO_H__ +#ifndef LINUX_FSI_SBEFIFO_H +#define LINUX_FSI_SBEFIFO_H struct device; struct sbefifo_client; @@ -27,4 +27,4 @@ extern int sbefifo_drv_write(struct sbefifo_client *client, const char *buf, size_t len); extern void sbefifo_drv_release(struct sbefifo_client *client); -#endif /* __FSI_SBEFIFO_H__ */ +#endif /* LINUX_FSI_SBEFIFO_H */ From patchwork Thu Sep 21 22:43:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eddie James X-Patchwork-Id: 817198 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3xysBw5GJ1z9s06 for ; Fri, 22 Sep 2017 08:45:28 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3xysBv2fV8zDsM4 for ; Fri, 22 Sep 2017 08:45:27 +1000 (AEST) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=linux.vnet.ibm.com (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=eajames@linux.vnet.ibm.com; receiver=) Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3xys9d0XsTzDqT0 for ; Fri, 22 Sep 2017 08:44:20 +1000 (AEST) Received: from pps.filterd (m0098410.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v8LMiFim002911 for ; Thu, 21 Sep 2017 18:44:17 -0400 Received: from e17.ny.us.ibm.com (e17.ny.us.ibm.com [129.33.205.207]) by mx0a-001b2d01.pphosted.com with ESMTP id 2d4m4f6mhd-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 21 Sep 2017 18:44:17 -0400 Received: from localhost by e17.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 21 Sep 2017 18:44:06 -0400 Received: from b01cxnp23034.gho.pok.ibm.com (9.57.198.29) by e17.ny.us.ibm.com (146.89.104.204) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 21 Sep 2017 18:44:04 -0400 Received: from b01ledav001.gho.pok.ibm.com (b01ledav001.gho.pok.ibm.com [9.57.199.106]) by b01cxnp23034.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v8LMi32c40829008; Thu, 21 Sep 2017 22:44:03 GMT Received: from b01ledav001.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 310442803E; Thu, 21 Sep 2017 18:43:57 -0400 (EDT) Received: from oc3016140333.ibm.com (unknown [9.41.174.252]) by b01ledav001.gho.pok.ibm.com (Postfix) with ESMTP id AC1B728048; Thu, 21 Sep 2017 18:43:56 -0400 (EDT) From: Eddie James To: openbmc@lists.ozlabs.org Subject: [PATCH linux dev-4.10 2/3] drivers/fsi/occ: refactor to upstream list state Date: Thu, 21 Sep 2017 17:43:57 -0500 X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com> References: <1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17092122-0040-0000-0000-000003A6090A X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00007775; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000231; SDB=6.00920429; UDB=6.00462502; IPR=6.00700651; BA=6.00005601; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00017240; XFM=3.00000015; UTC=2017-09-21 22:44:05 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17092122-0041-0000-0000-0000079B0EB9 Message-Id: <1506033838-2078-3-git-send-email-eajames@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-09-21_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=3 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000 definitions=main-1709210305 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: andrew@aj.id.au, "Edward A. James" Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" From: "Edward A. James" Includes various fixes: - Fix probe failure scenario - General cleanup - Reorder remove() operations for safety - Add cancel boolean to prevent further xfrs when we're removing Signed-off-by: Edward A. James --- drivers/fsi/occ.c | 227 ++++++++++++++++++++++++++++++++-------------------- include/linux/occ.h | 20 ++++- 2 files changed, 155 insertions(+), 92 deletions(-) diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c index 621fbf0..4dd5048 100644 --- a/drivers/fsi/occ.c +++ b/drivers/fsi/occ.c @@ -10,16 +10,22 @@ #include #include #include +#include +#include #include -#include +#include #include +#include #include #include #include +#include #include #include #include +#include #include +#include #include #include #include @@ -28,49 +34,45 @@ #define OCC_CMD_DATA_BYTES 4090 #define OCC_RESP_DATA_BYTES 4089 +#define OCC_TIMEOUT_MS 1000 +#define OCC_CMD_IN_PRG_WAIT_MS 50 + struct occ { struct device *sbefifo; char name[32]; int idx; struct miscdevice mdev; struct list_head xfrs; - spinlock_t list_lock; - struct mutex occ_lock; + spinlock_t list_lock; /* lock access to the xfrs list */ + struct mutex occ_lock; /* lock access to the hardware */ struct work_struct work; + bool cancel; }; #define to_occ(x) container_of((x), struct occ, mdev) -struct occ_command { - u8 seq_no; - u8 cmd_type; - u16 data_length; - u8 data[OCC_CMD_DATA_BYTES]; - u16 checksum; -} __packed; - struct occ_response { u8 seq_no; u8 cmd_type; u8 return_status; - u16 data_length; + __be16 data_length; u8 data[OCC_RESP_DATA_BYTES]; - u16 checksum; + __be16 checksum; } __packed; /* * transfer flags are NOT mutually exclusive - * + * * Initial flags are none; transfer is created and queued from write(). All - * flags are cleared when the transfer is completed by closing the file or - * reading all of the available response data. + * flags are cleared when the transfer is completed by closing the file or + * reading all of the available response data. * XFR_IN_PROGRESS is set when a transfer is started from occ_worker_putsram, - * and cleared if the transfer fails or occ_worker_getsram completes. + * and cleared if the transfer fails or occ_worker_getsram completes. * XFR_COMPLETE is set when a transfer fails or finishes occ_worker_getsram. * XFR_CANCELED is set when the transfer's client is released. * XFR_WAITING is set from read() if the transfer isn't complete and - * NONBLOCKING wasn't specified. Cleared in read() when transfer completes - * or fails. + * O_NONBLOCK wasn't specified. Cleared in read() when transfer completes or + * fails. */ enum { XFR_IN_PROGRESS, @@ -92,9 +94,9 @@ struct occ_xfr { * client flags * * CLIENT_NONBLOCKING is set during open() if the file was opened with the - * O_NONBLOCKING flag. + * O_NONBLOCK flag. * CLIENT_XFR_PENDING is set during write() and cleared when all data has been - * read. + * read. */ enum { CLIENT_NONBLOCKING, @@ -104,7 +106,7 @@ enum { struct occ_client { struct occ *occ; struct occ_xfr xfr; - spinlock_t lock; + spinlock_t lock; /* lock access to the client state */ wait_queue_head_t wait; size_t read_offset; unsigned long flags; @@ -116,12 +118,15 @@ struct occ_client { static DEFINE_IDA(occ_ida); -static void occ_enqueue_xfr(struct occ_xfr *xfr) +static int occ_enqueue_xfr(struct occ_xfr *xfr) { int empty; struct occ_client *client = to_client(xfr); struct occ *occ = client->occ; + if (occ->cancel) + return -ECANCELED; + spin_lock_irq(&occ->list_lock); empty = list_empty(&occ->xfrs); @@ -131,14 +136,15 @@ static void occ_enqueue_xfr(struct occ_xfr *xfr) if (empty) queue_work(occ_wq, &occ->work); + + return 0; } static struct occ_client *occ_open_common(struct occ *occ, unsigned long flags) { - struct occ_client *client; + struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL); - client = kzalloc(sizeof(*client), GFP_KERNEL); - if (!client) + if (!client || occ->cancel) return NULL; client->occ = occ; @@ -172,6 +178,7 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf, int rc; size_t bytes; struct occ_xfr *xfr = &client->xfr; + struct occ *occ = client->occ; if (len > OCC_SRAM_BYTES) return -EINVAL; @@ -183,8 +190,9 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf, if (client->read_offset) { rc = 0; client->read_offset = 0; - } else + } else { rc = -ENOMSG; + } goto done; } @@ -200,8 +208,11 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf, spin_unlock_irq(&client->lock); rc = wait_event_interruptible(client->wait, - test_bit(XFR_COMPLETE, &xfr->flags) || - test_bit(XFR_CANCELED, &xfr->flags)); + test_bit(XFR_COMPLETE, + &xfr->flags) || + test_bit(XFR_CANCELED, + &xfr->flags) || + occ->cancel); spin_lock_irq(&client->lock); @@ -225,12 +236,14 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf, bytes = min(len, xfr->resp_data_length - client->read_offset); if (ubuf) { - if (copy_to_user(ubuf, &xfr->buf[client->read_offset], bytes)) { + if (copy_to_user(ubuf, &xfr->buf[client->read_offset], + bytes)) { rc = -EFAULT; goto done; } - } else + } else { memcpy(kbuf, &xfr->buf[client->read_offset], bytes); + } client->read_offset += bytes; @@ -250,12 +263,6 @@ static ssize_t occ_read(struct file *file, char __user *buf, size_t len, { struct occ_client *client = file->private_data; - /* check this ahead of time so we don't go changing the xfr state - * needlessly - */ - if (!access_ok(VERIFY_WRITE, buf, len)) - return -EFAULT; - return occ_read_common(client, buf, NULL, len); } @@ -272,28 +279,31 @@ static ssize_t occ_write_common(struct occ_client *client, return -EINVAL; spin_lock_irq(&client->lock); - if (test_and_set_bit(CLIENT_XFR_PENDING, &client->flags)) { + + if (test_bit(CLIENT_XFR_PENDING, &client->flags)) { rc = -EBUSY; goto done; } - /* clear out the transfer */ - memset(xfr, 0, sizeof(*xfr)); - - xfr->buf[0] = 1; + memset(xfr, 0, sizeof(*xfr)); /* clear out the transfer */ + xfr->buf[0] = 1; /* occ sequence number */ + /* Assume user data follows the occ command format. + * byte 0: command type + * bytes 1-2: data length (msb first) + * bytes 3-n: data + */ if (ubuf) { if (copy_from_user(&xfr->buf[1], ubuf, len)) { - kfree(xfr); rc = -EFAULT; goto done; } - } else + } else { memcpy(&xfr->buf[1], kbuf, len); + } data_length = (xfr->buf[2] << 8) + xfr->buf[3]; if (data_length > OCC_CMD_DATA_BYTES) { - kfree(xfr); rc = -EINVAL; goto done; } @@ -306,9 +316,11 @@ static ssize_t occ_write_common(struct occ_client *client, xfr->cmd_data_length = data_length + 6; client->read_offset = 0; + rc = occ_enqueue_xfr(xfr); + if (rc) + goto done; - occ_enqueue_xfr(xfr); - + set_bit(CLIENT_XFR_PENDING, &client->flags); rc = len; done: @@ -321,12 +333,6 @@ static ssize_t occ_write(struct file *file, const char __user *buf, { struct occ_client *client = file->private_data; - /* check this ahead of time so we don't go changing the xfr state - * needlessly - */ - if (!access_ok(VERIFY_READ, buf, len)) - return -EFAULT; - return occ_write_common(client, buf, NULL, len); } @@ -366,7 +372,7 @@ static int occ_release_common(struct occ_client *client) return 0; } - /* operation is in progress; let worker clean up*/ + /* operation is in progress; let worker clean up */ spin_unlock_irq(&occ->list_lock); spin_unlock_irq(&client->lock); return 0; @@ -403,7 +409,7 @@ static int occ_write_sbefifo(struct sbefifo_client *client, const char *buf, total += rc; } while (total < len); - return (total == len) ? 0 : -EMSGSIZE; + return (total == len) ? 0 : -ENODATA; } static int occ_read_sbefifo(struct sbefifo_client *client, char *buf, @@ -422,7 +428,7 @@ static int occ_read_sbefifo(struct sbefifo_client *client, char *buf, total += rc; } while (total < len); - return (total == len) ? 0 : -EMSGSIZE; + return (total == len) ? 0 : -ENODATA; } static int occ_getsram(struct device *sbefifo, u32 address, u8 *data, @@ -430,10 +436,13 @@ static int occ_getsram(struct device *sbefifo, u32 address, u8 *data, { int rc; u8 *resp; - u32 buf[5]; - u32 data_len = ((len + 7) / 8) * 8; + __be32 buf[5]; + u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ struct sbefifo_client *client; + /* Magic sequence to do SBE getsram command. SBE will fetch data from + * specified SRAM address. + */ buf[0] = cpu_to_be32(0x5); buf[1] = cpu_to_be32(0xa403); buf[2] = cpu_to_be32(1); @@ -447,7 +456,7 @@ static int occ_getsram(struct device *sbefifo, u32 address, u8 *data, rc = occ_write_sbefifo(client, (const char *)buf, sizeof(buf)); if (rc) goto done; - + resp = kzalloc(data_len, GFP_KERNEL); if (!resp) { rc = -ENOMEM; @@ -467,7 +476,7 @@ static int occ_getsram(struct device *sbefifo, u32 address, u8 *data, (be32_to_cpu(buf[1]) == 0xC0DEA403)) memcpy(data, resp, len); else - rc = -EFAULT; + rc = -EBADMSG; free: kfree(resp); @@ -481,8 +490,8 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data, ssize_t len) { int rc; - u32 *buf; - u32 data_len = ((len + 7) / 8) * 8; + __be32 *buf; + u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ size_t cmd_len = data_len + 20; struct sbefifo_client *client; @@ -490,6 +499,9 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data, if (!buf) return -ENOMEM; + /* Magic sequence to do SBE putsram command. SBE will transfer + * data to specified SRAM address. + */ buf[0] = cpu_to_be32(0x5 + (data_len / 4)); buf[1] = cpu_to_be32(0xa404); buf[2] = cpu_to_be32(1); @@ -515,7 +527,7 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data, /* check for good response */ if ((be32_to_cpu(buf[0]) != data_len) || (be32_to_cpu(buf[1]) != 0xC0DEA404)) - rc = -EFAULT; + rc = -EBADMSG; done: sbefifo_drv_release(client); @@ -527,14 +539,17 @@ static int occ_putsram(struct device *sbefifo, u32 address, u8 *data, static int occ_trigger_attn(struct device *sbefifo) { int rc; - u32 buf[6]; + __be32 buf[6]; struct sbefifo_client *client; + /* Magic sequence to do SBE putscom command. SBE will write 8 bytes to + * specified SCOM address. + */ buf[0] = cpu_to_be32(0x6); buf[1] = cpu_to_be32(0xa202); buf[2] = 0; buf[3] = cpu_to_be32(0x6D035); - buf[4] = cpu_to_be32(0x20010000); + buf[4] = cpu_to_be32(0x20010000); /* trigger occ attention */ buf[5] = 0; client = sbefifo_drv_open(sbefifo, 0); @@ -552,7 +567,7 @@ static int occ_trigger_attn(struct device *sbefifo) /* check for good response */ if ((be32_to_cpu(buf[0]) != 0xC0DEA202) || (be32_to_cpu(buf[1]) & 0x0FFFFFFF)) - rc = -EFAULT; + rc = -EBADMSG; done: sbefifo_drv_release(client); @@ -564,12 +579,19 @@ static void occ_worker(struct work_struct *work) { int rc = 0, empty, waiting, canceled; u16 resp_data_length; + unsigned long start; + const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS); + const long int wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS); struct occ_xfr *xfr; + struct occ_response *resp; struct occ_client *client; struct occ *occ = container_of(work, struct occ, work); struct device *sbefifo = occ->sbefifo; again: + if (occ->cancel) + return; + spin_lock_irq(&occ->list_lock); xfr = list_first_entry_or_null(&occ->xfrs, struct occ_xfr, link); @@ -578,11 +600,14 @@ static void occ_worker(struct work_struct *work) return; } + resp = (struct occ_response *)xfr->buf; set_bit(XFR_IN_PROGRESS, &xfr->flags); spin_unlock_irq(&occ->list_lock); mutex_lock(&occ->occ_lock); + /* write occ command */ + start = jiffies; rc = occ_putsram(sbefifo, 0xFFFBE000, xfr->buf, xfr->cmd_data_length); if (rc) @@ -592,13 +617,26 @@ static void occ_worker(struct work_struct *work) if (rc) goto done; - rc = occ_getsram(sbefifo, 0xFFFBF000, xfr->buf, 8); - if (rc) - goto done; + /* read occ response */ + do { + rc = occ_getsram(sbefifo, 0xFFFBF000, xfr->buf, 8); + if (rc) + goto done; + + if (resp->return_status == OCC_RESP_CMD_IN_PRG) { + rc = -EALREADY; + + if (time_after(jiffies, start + timeout)) + break; - resp_data_length = (xfr->buf[3] << 8) + xfr->buf[4]; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(wait_time); + } + } while (rc); + + resp_data_length = get_unaligned_be16(&resp->data_length); if (resp_data_length > OCC_RESP_DATA_BYTES) { - rc = -EDOM; + rc = -EMSGSIZE; goto done; } @@ -704,9 +742,6 @@ static int occ_probe(struct platform_device *pdev) mutex_init(&occ->occ_lock); INIT_WORK(&occ->work, occ_worker); - /* ensure NULL before we probe children, so they don't hang FSI */ - platform_set_drvdata(pdev, NULL); - if (dev->of_node) { rc = of_property_read_u32(dev->of_node, "reg", ®); if (!rc) { @@ -716,23 +751,13 @@ static int occ_probe(struct platform_device *pdev) if (occ->idx < 0) occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL); - } else + } else { occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL); - - /* create platform devs for dts child nodes (hwmon, etc) */ - for_each_child_of_node(dev->of_node, np) { - snprintf(child_name, sizeof(child_name), "occ%d-dev%d", - occ->idx, child_idx++); - child = of_platform_device_create(np, child_name, dev); - if (!child) - dev_warn(dev, - "failed to create child node dev\n"); } - } else + } else { occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL); - - platform_set_drvdata(pdev, occ); + } snprintf(occ->name, sizeof(occ->name), "occ%d", occ->idx); occ->mdev.fops = &occ_fops; @@ -742,20 +767,42 @@ static int occ_probe(struct platform_device *pdev) rc = misc_register(&occ->mdev); if (rc) { - dev_err(dev, "failed to register miscdevice\n"); + dev_err(dev, "failed to register miscdevice: %d\n", rc); + ida_simple_remove(&occ_ida, occ->idx); return rc; } + /* create platform devs for dts child nodes (hwmon, etc) */ + for_each_available_child_of_node(dev->of_node, np) { + snprintf(child_name, sizeof(child_name), "occ%d-dev%d", + occ->idx, child_idx++); + child = of_platform_device_create(np, child_name, dev); + if (!child) + dev_warn(dev, "failed to create child node dev\n"); + } + + platform_set_drvdata(pdev, occ); + return 0; } static int occ_remove(struct platform_device *pdev) { struct occ *occ = platform_get_drvdata(pdev); + struct occ_xfr *xfr; + struct occ_client *client; + + occ->cancel = true; + list_for_each_entry(xfr, &occ->xfrs, link) { + client = to_client(xfr); + wake_up_interruptible(&client->wait); + } - flush_work(&occ->work); misc_deregister(&occ->mdev); device_for_each_child(&pdev->dev, NULL, occ_unregister_child); + + flush_work(&occ->work); + ida_simple_remove(&occ_ida, occ->idx); return 0; @@ -789,6 +836,8 @@ static void occ_exit(void) destroy_workqueue(occ_wq); platform_driver_unregister(&occ_driver); + + ida_destroy(&occ_ida); } module_init(occ_init); diff --git a/include/linux/occ.h b/include/linux/occ.h index d78332c..0a4a54a 100644 --- a/include/linux/occ.h +++ b/include/linux/occ.h @@ -11,12 +11,26 @@ * GNU General Public License for more details. */ -#ifndef __OCC_H__ -#define __OCC_H__ +#ifndef LINUX_FSI_OCC_H +#define LINUX_FSI_OCC_H struct device; struct occ_client; +#define OCC_RESP_CMD_IN_PRG 0xFF +#define OCC_RESP_SUCCESS 0 +#define OCC_RESP_CMD_INVAL 0x11 +#define OCC_RESP_CMD_LEN_INVAL 0x12 +#define OCC_RESP_DATA_INVAL 0x13 +#define OCC_RESP_CHKSUM_ERR 0x14 +#define OCC_RESP_INT_ERR 0x15 +#define OCC_RESP_BAD_STATE 0x16 +#define OCC_RESP_CRIT_EXCEPT 0xE0 +#define OCC_RESP_CRIT_INIT 0xE1 +#define OCC_RESP_CRIT_WATCHDOG 0xE2 +#define OCC_RESP_CRIT_OCB 0xE3 +#define OCC_RESP_CRIT_HW 0xE4 + extern struct occ_client *occ_drv_open(struct device *dev, unsigned long flags); extern int occ_drv_read(struct occ_client *client, char *buf, size_t len); @@ -24,4 +38,4 @@ extern int occ_drv_write(struct occ_client *client, const char *buf, size_t len); extern void occ_drv_release(struct occ_client *client); -#endif /* __OCC_H__ */ +#endif /* LINUX_FSI_OCC_H */ From patchwork Thu Sep 21 22:43:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eddie James X-Patchwork-Id: 817199 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3xysD65L4Wz9s06 for ; Fri, 22 Sep 2017 08:46:30 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3xysD64QY6zDr4N for ; Fri, 22 Sep 2017 08:46:30 +1000 (AEST) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=linux.vnet.ibm.com (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=eajames@linux.vnet.ibm.com; receiver=) Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3xys9g4lbvzDqT0 for ; Fri, 22 Sep 2017 08:44:23 +1000 (AEST) Received: from pps.filterd (m0098394.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v8LMiI7D138946 for ; Thu, 21 Sep 2017 18:44:21 -0400 Received: from e14.ny.us.ibm.com (e14.ny.us.ibm.com [129.33.205.204]) by mx0a-001b2d01.pphosted.com with ESMTP id 2d4m70xh32-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 21 Sep 2017 18:44:19 -0400 Received: from localhost by e14.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 21 Sep 2017 18:44:08 -0400 Received: from b01cxnp23032.gho.pok.ibm.com (9.57.198.27) by e14.ny.us.ibm.com (146.89.104.201) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 21 Sep 2017 18:44:05 -0400 Received: from b01ledav001.gho.pok.ibm.com (b01ledav001.gho.pok.ibm.com [9.57.199.106]) by b01cxnp23032.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v8LMi5tg29229186; Thu, 21 Sep 2017 22:44:05 GMT Received: from b01ledav001.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id A8DDF2803A; Thu, 21 Sep 2017 18:43:58 -0400 (EDT) Received: from oc3016140333.ibm.com (unknown [9.41.174.252]) by b01ledav001.gho.pok.ibm.com (Postfix) with ESMTP id 17ABE2803E; Thu, 21 Sep 2017 18:43:58 -0400 (EDT) From: Eddie James To: openbmc@lists.ozlabs.org Subject: [PATCH linux dev-4.10 3/3] drivers/hwmon/occ: Remove repeated ops for OCC command in progress Date: Thu, 21 Sep 2017 17:43:58 -0500 X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com> References: <1506033838-2078-1-git-send-email-eajames@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17092122-0052-0000-0000-0000026561FB X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00007775; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000231; SDB=6.00920429; UDB=6.00462502; IPR=6.00700651; BA=6.00005601; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00017240; XFM=3.00000015; UTC=2017-09-21 22:44:07 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17092122-0053-0000-0000-000052158F6E Message-Id: <1506033838-2078-4-git-send-email-eajames@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-09-21_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=1 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000 definitions=main-1709210305 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: andrew@aj.id.au, "Edward A. James" Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" From: "Edward A. James" This is now handled in the occ driver. Signed-off-by: Edward A. James --- drivers/hwmon/occ/p9_sbe.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index 2d50a94..c7e0d9c 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -26,14 +26,10 @@ struct p9_sbe_occ { static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) { int rc, error; - unsigned long start; struct occ_client *client; struct occ_response *resp = &occ->resp; struct p9_sbe_occ *p9_sbe_occ = to_p9_sbe_occ(occ); - start = jiffies; - -retry: client = occ_drv_open(p9_sbe_occ->sbe, 0); if (!client) { rc = -ENODEV; @@ -52,15 +48,7 @@ static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) switch (resp->return_status) { case RESP_RETURN_CMD_IN_PRG: - if (time_after(jiffies, - start + msecs_to_jiffies(OCC_TIMEOUT_MS))) - rc = -EALREADY; - else { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(OCC_CMD_IN_PRG_MS)); - - goto retry; - } + rc = -ETIMEDOUT; break; case RESP_RETURN_SUCCESS: occ_reset_error(occ);