Received: by 10.223.164.202 with SMTP id h10csp1309817wrb; Fri, 17 Nov 2017 18:43:24 -0800 (PST) X-Google-Smtp-Source: AGs4zMbINc27vyYVC0GqJ93XzM92+GKWp+2pRg3BFZLMzRjFZ8pV5OoY+zVTn6qUcBiBwvwF3sOC X-Received: by 10.84.242.9 with SMTP id ba9mr7094453plb.305.1510973004405; Fri, 17 Nov 2017 18:43:24 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1510973004; cv=none; d=google.com; s=arc-20160816; b=jMGtxSoym5bwDFaaHHSXTSz2Mf0iLEYCjN8Ya9XXmUaCz+ygUkWKMAbjYQ+X4j4EVX HB0xGXdQg9QCYLOVll+SPboBCqNu738reWxblj2HZGMMPqCtMJzrjKApi0cFe0iTQtlE N3WScu6jlJ2kqUhfhxbcF9lZsccpokPVhYGTREG7aoIOA9L/tB4BdV7SIYEI5PRoDv5Z VClIiJCGWAvhzkrrHBcDtSC0dz9BdExX0r8qSJN9Gxkjbcg4is3/6zzaluL6KPg0R51H HQKpgtkVL2JHxdf7NAggKVXMEyeBzlg6SM6K23BnAv/cnF5iSTqZco8MenqZkRH6R8x3 vtAg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:references:in-reply-to:date :subject:cc:to:from:arc-authentication-results; bh=2rQ6HlRrzDrLs69f3XylNk1GEvimY7i16K4kkiY2wso=; b=GsSpY5X1b6mv0YeQPzMKtkj4t27asipwyVYNrgkLz8B/kvB+hpJbVWu8Xyx9G8l7B7 L80KWDNPnFLxhZzk5oC2XUDCTHPLCsQWs55jpGtyYj5lfNmYDQlPlS5wHtaJbe1H1x6E fScCLrj8McR/Rzyac0Ln6hRbUZUWQwkCrqmiEUpYPF2ORR+rdEFLLKT/gUkFBFNGrbIP j9gVKMeyRp9Sp6ayIessTPiA54wW+S33zNbULrLbcy446H+r12jgD/UjDhI38qKeViK0 gRAjOkM/p/jVqw7gr+Tk6Df7VYYdC/gNArlR2DTgaZzl78mpKbpcMIPxuPaydpdX7eiV wdnw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ibm.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f81si4174651pfj.30.2017.11.17.18.43.11; Fri, 17 Nov 2017 18:43:24 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ibm.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935386AbdKQTfd (ORCPT + 93 others); Fri, 17 Nov 2017 14:35:33 -0500 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:42080 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935630AbdKQTew (ORCPT ); Fri, 17 Nov 2017 14:34:52 -0500 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 vAHJYe9e055370 for ; Fri, 17 Nov 2017 14:34:52 -0500 Received: from e18.ny.us.ibm.com (e18.ny.us.ibm.com [129.33.205.208]) by mx0a-001b2d01.pphosted.com with ESMTP id 2ea4fuvg7b-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Fri, 17 Nov 2017 14:34:50 -0500 Received: from localhost by e18.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 17 Nov 2017 14:34:49 -0500 Received: from b01cxnp22035.gho.pok.ibm.com (9.57.198.25) by e18.ny.us.ibm.com (146.89.104.205) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Fri, 17 Nov 2017 14:34:45 -0500 Received: from b01ledav004.gho.pok.ibm.com (b01ledav004.gho.pok.ibm.com [9.57.199.109]) by b01cxnp22035.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id vAHJYikw47841468; Fri, 17 Nov 2017 19:34:45 GMT Received: from b01ledav004.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 00550112054; Fri, 17 Nov 2017 14:34:09 -0500 (EST) Received: from oc3016140333.ibm.com (unknown [9.41.174.252]) by b01ledav004.gho.pok.ibm.com (Postfix) with ESMTP id 31C86112047; Fri, 17 Nov 2017 14:34:08 -0500 (EST) From: Eddie James To: linux-kernel@vger.kernel.org Cc: gregkh@linuxfoundation.org, devicetree@vger.kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, bradleyb@fuzziesquirrel.com, cbostic@linux.vnet.ibm.com, joel@jms.id.au, eajames@linux.vnet.ibm.com, "Edward A. James" Subject: [PATCH v4 3/4] drivers/fsi: sbefifo: Add miscdevice Date: Fri, 17 Nov 2017 13:34:33 -0600 X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1510947274-7519-1-git-send-email-eajames@linux.vnet.ibm.com> References: <1510947274-7519-1-git-send-email-eajames@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17111719-0044-0000-0000-000003B1C155 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00008084; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000240; SDB=6.00947307; UDB=6.00478258; IPR=6.00727599; BA=6.00005698; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00018068; XFM=3.00000015; UTC=2017-11-17 19:34:47 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17111719-0045-0000-0000-000007E0E382 Message-Id: <1510947274-7519-4-git-send-email-eajames@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2017-11-17_06:,, signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=4 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1711170263 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "Edward A. James" Add a miscdevice for the sbefifo to allow userspace access through standard file operations. Signed-off-by: Edward A. James --- drivers/fsi/fsi-sbefifo.c | 354 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index 2439958..9970e2d 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ struct sbefifo { struct timer_list poll_timer; struct fsi_device *fsi_dev; + struct miscdevice mdev; wait_queue_head_t wait; struct list_head xfrs; struct kref kref; @@ -256,6 +258,99 @@ static void sbefifo_put(struct sbefifo *sbefifo) kref_put(&sbefifo->kref, sbefifo_free); } +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 ERR_PTR(-ENOMEM); + + xfr->rbuf = &client->rbuf; + xfr->wbuf = &client->wbuf; + list_add_tail(&xfr->xfrs, &sbefifo->xfrs); + list_add_tail(&xfr->client, &client->xfrs); + + return xfr; +} + +static bool sbefifo_xfr_rsp_pending(struct sbefifo_client *client) +{ + struct sbefifo_xfr *xfr = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, + client); + + if (xfr && test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags)) + return true; + + return false; +} + +static struct sbefifo_client *sbefifo_new_client(struct sbefifo *sbefifo) +{ + struct sbefifo_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + kref_init(&client->kref); + client->dev = sbefifo; + sbefifo_buf_init(&client->rbuf); + sbefifo_buf_init(&client->wbuf); + INIT_LIST_HEAD(&client->xfrs); + + sbefifo_get(sbefifo); + + return client; +} + +static void sbefifo_client_release(struct kref *kref) +{ + struct sbefifo *sbefifo; + struct sbefifo_client *client; + struct sbefifo_xfr *xfr, *tmp; + + client = container_of(kref, struct sbefifo_client, kref); + sbefifo = client->dev; + + if (!READ_ONCE(sbefifo->rc)) { + list_for_each_entry_safe(xfr, tmp, &client->xfrs, client) { + if (test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) { + list_del(&xfr->client); + kfree(xfr); + continue; + } + + /* + * The client left with pending or running xfrs. + * Cancel them. + */ + set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags); + sbefifo_get(sbefifo); + if (mod_timer(&client->dev->poll_timer, jiffies)) + sbefifo_put(sbefifo); + } + } + + sbefifo_put(sbefifo); + kfree(client); +} + +static void sbefifo_get_client(struct sbefifo_client *client) +{ + kref_get(&client->kref); +} + +static void sbefifo_put_client(struct sbefifo_client *client) +{ + kref_put(&client->kref, sbefifo_client_release); +} + static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo) { struct sbefifo_xfr *xfr, *tmp; @@ -429,6 +524,251 @@ static void sbefifo_poll_timer(unsigned long data) spin_unlock(&sbefifo->lock); } +static int sbefifo_open(struct inode *inode, struct file *file) +{ + struct sbefifo *sbefifo = container_of(file->private_data, + struct sbefifo, mdev); + struct sbefifo_client *client; + int ret; + + ret = READ_ONCE(sbefifo->rc); + if (ret) + return ret; + + client = sbefifo_new_client(sbefifo); + if (!client) + return -ENOMEM; + + file->private_data = client; + + return 0; +} + +static unsigned int sbefifo_poll(struct file *file, poll_table *wait) +{ + struct sbefifo_client *client = file->private_data; + struct sbefifo *sbefifo = client->dev; + unsigned int mask = 0; + + poll_wait(file, &sbefifo->wait, wait); + + if (READ_ONCE(sbefifo->rc)) + mask |= POLLERR; + + if (sbefifo_buf_nbreadable(&client->rbuf)) + mask |= POLLIN; + + if (sbefifo_buf_nbwriteable(&client->wbuf)) + mask |= POLLOUT; + + return mask; +} + +static bool sbefifo_read_ready(struct sbefifo *sbefifo, + struct sbefifo_client *client, size_t *n, + size_t *ret) +{ + struct sbefifo_xfr *xfr = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, + client); + + *n = sbefifo_buf_nbreadable(&client->rbuf); + *ret = READ_ONCE(sbefifo->rc); + + return *ret || *n || + (xfr && test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)); +} + +static ssize_t sbefifo_read(struct file *file, char __user *buf, size_t len, + loff_t *offset) +{ + struct sbefifo_client *client = file->private_data; + struct sbefifo *sbefifo = client->dev; + struct sbefifo_xfr *xfr; + size_t n; + ssize_t ret = 0; + + if ((len >> 2) << 2 != len) + return -EINVAL; + + if ((file->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client)) + return -EAGAIN; + + sbefifo_get_client(client); + if (wait_event_interruptible(sbefifo->wait, + sbefifo_read_ready(sbefifo, client, &n, + &ret))) { + ret = -ERESTARTSYS; + goto out; + } + + if (ret) { + INIT_LIST_HEAD(&client->xfrs); + goto out; + } + + n = min_t(size_t, n, len); + + if (copy_to_user(buf, READ_ONCE(client->rbuf.rpos), n)) { + ret = -EFAULT; + goto out; + } + + if (sbefifo_buf_readnb(&client->rbuf, n)) { + xfr = list_first_entry_or_null(&client->xfrs, + struct sbefifo_xfr, client); + if (!xfr) { + /* should be impossible to not have an xfr here */ + WARN_ONCE(1, "no xfr in queue"); + ret = -EPROTO; + goto out; + } + + if (!test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) { + /* Fill the read buffer back up. */ + sbefifo_get(sbefifo); + if (mod_timer(&client->dev->poll_timer, jiffies)) + sbefifo_put(sbefifo); + } else { + list_del(&xfr->client); + kfree(xfr); + wake_up_interruptible(&sbefifo->wait); + } + } + + ret = n; + +out: + sbefifo_put_client(client); + return ret; +} + +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); + + *n = sbefifo_buf_nbwriteable(&client->wbuf); + return READ_ONCE(sbefifo->rc) || (next == xfr && *n); +} + +static ssize_t sbefifo_write(struct file *file, const char __user *buf, + size_t len, loff_t *offset) +{ + struct sbefifo_client *client = file->private_data; + struct sbefifo *sbefifo = client->dev; + struct sbefifo_xfr *xfr; + ssize_t ret = 0; + size_t n; + + if ((len >> 2) << 2 != len) + return -EINVAL; + + if (!len) + return 0; + + sbefifo_get_client(client); + n = sbefifo_buf_nbwriteable(&client->wbuf); + + spin_lock_irq(&sbefifo->lock); + xfr = sbefifo_next_xfr(sbefifo); /* next xfr to be executed */ + + if ((file->f_flags & O_NONBLOCK) && xfr && n < len) { + spin_unlock_irq(&sbefifo->lock); + ret = -EAGAIN; + goto out; + } + + xfr = sbefifo_enq_xfr(client); /* this xfr queued up */ + if (IS_ERR(xfr)) { + spin_unlock_irq(&sbefifo->lock); + ret = PTR_ERR(xfr); + goto out; + } + + spin_unlock_irq(&sbefifo->lock); + + /* + * Partial writes are not really allowed in that EOT is sent exactly + * once per write. + */ + while (len) { + if (wait_event_interruptible(sbefifo->wait, + 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); + + ret = -ERESTARTSYS; + goto out; + } + + if (sbefifo->rc) { + INIT_LIST_HEAD(&client->xfrs); + ret = sbefifo->rc; + goto out; + } + + n = min_t(size_t, n, len); + + if (copy_from_user(READ_ONCE(client->wbuf.wpos), buf, n)) { + set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags); + sbefifo_get(sbefifo); + if (mod_timer(&sbefifo->poll_timer, jiffies)) + sbefifo_put(sbefifo); + ret = -EFAULT; + goto out; + } + + buf += n; + + sbefifo_buf_wrotenb(&client->wbuf, n); + len -= n; + ret += n; + + /* + * 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); + + /* Drain the write buffer. */ + sbefifo_get(sbefifo); + if (mod_timer(&client->dev->poll_timer, jiffies)) + sbefifo_put(sbefifo); + } + +out: + sbefifo_put_client(client); + return ret; +} + +static int sbefifo_release(struct inode *inode, struct file *file) +{ + struct sbefifo_client *client = file->private_data; + struct sbefifo *sbefifo = client->dev; + + sbefifo_put_client(client); + + return READ_ONCE(sbefifo->rc); +} + +static const struct file_operations sbefifo_fops = { + .owner = THIS_MODULE, + .open = sbefifo_open, + .read = sbefifo_read, + .write = sbefifo_write, + .poll = sbefifo_poll, + .release = sbefifo_release, +}; + static int sbefifo_request_reset(struct sbefifo *sbefifo) { int ret; @@ -503,6 +843,18 @@ static int sbefifo_probe(struct device *dev) setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer, (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; + } + dev_set_drvdata(dev, sbefifo); return 0; @@ -527,6 +879,8 @@ static int sbefifo_remove(struct device *dev) wake_up_all(&sbefifo->wait); + misc_deregister(&sbefifo->mdev); + ida_simple_remove(&sbefifo_ida, sbefifo->idx); if (del_timer_sync(&sbefifo->poll_timer)) -- 1.8.3.1 From 1584352627788098821@xxx Fri Nov 17 22:06:49 +0000 2017 X-GM-THRID: 1584240759214748749 X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread