Received: by 10.223.164.202 with SMTP id h10csp4218567wrb; Mon, 20 Nov 2017 11:49:47 -0800 (PST) X-Google-Smtp-Source: AGs4zMZKaEOtWJBTEmWtxcmCTqEugArNOZHibvKm8Gxp+/QIsInRt8/QVh0jn+fxASyliym0gHaN X-Received: by 10.99.114.30 with SMTP id n30mr14456474pgc.129.1511207387719; Mon, 20 Nov 2017 11:49:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1511207387; cv=none; d=google.com; s=arc-20160816; b=sIpbbIhqkdj9Qdl7ImW14oXTN1eijtI0UuQ9mgkn4VZDhuetFgoRDrerC6uC/n5167 niwBLaWGE3c/dx1S0TO4WnhE/kh6nPmZmUCH+LB2zHUSexec2/eWIcW3iBVw1jo6ob1t LCidoS5k2bGhPBrgXmiWBldojs4CG3ZsPoJ1cY7gqdPjtTfqZy4mq8IvRYW2a+UqyWGF U32w7OAyY4yIxruNcZgOuXIGHVReqDUDa4UNiJb+AbmUkmHrwYxPozmA3PfxNTcM8Y1U HagllPn88rxPB0dgg3zKEh1FGvut15bCUhsJ3ejmNFYeBaDK5tfhNGjYxj3TiR0waTOD z9qw== 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=soGmED9Kk+UK0WVI0ApjMAHlqjwqb+0RnxJuY4PLogE=; b=wQktkGpk5AruBhl35a8O+ziSsXbDTksXJKmWGB7OIHwCagEISPepaNQQT6nqBkhaAE Ya761I87CZYcXdW18yGVGJartYmYqPtVCCLo41zR9ouSCzdTPFh+bLkbLlYoKVUiF0RC r4B9pDxXRK9lTZMNZL3HzCQyz6O5CAuDfwE56ty3GGTRrUdKEPBMlpNihFD/K5/mfVjs GJePODP821FQ5aNYVoJ8L8kKYluUZCKL/qF7gn4bV7JXIzDFCXfvsnrjq1md/G3ANRGl VM3Y18w1oNWkqqqMTrjyBrTHunCpR+On2IdAmdCgZj9B2qK3Lo6HMauLzO86/Lwzq8Pr ox0w== 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 q5si5411791pgs.112.2017.11.20.11.49.37; Mon, 20 Nov 2017 11:49:47 -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 S1752811AbdKTTsZ (ORCPT + 67 others); Mon, 20 Nov 2017 14:48:25 -0500 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:36966 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752612AbdKTTrK (ORCPT ); Mon, 20 Nov 2017 14:47:10 -0500 Received: from pps.filterd (m0098413.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id vAKJi6Wb131348 for ; Mon, 20 Nov 2017 14:47:10 -0500 Received: from e33.co.us.ibm.com (e33.co.us.ibm.com [32.97.110.151]) by mx0b-001b2d01.pphosted.com with ESMTP id 2ec4nvjg0c-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Mon, 20 Nov 2017 14:47:09 -0500 Received: from localhost by e33.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 20 Nov 2017 12:47:09 -0700 Received: from b03cxnp07029.gho.boulder.ibm.com (9.17.130.16) by e33.co.us.ibm.com (192.168.1.133) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 20 Nov 2017 12:47:06 -0700 Received: from b03ledav006.gho.boulder.ibm.com (b03ledav006.gho.boulder.ibm.com [9.17.130.237]) by b03cxnp07029.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id vAKJl5Yb10027324; Mon, 20 Nov 2017 12:47:05 -0700 Received: from b03ledav006.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id B445EC6043; Mon, 20 Nov 2017 12:47:05 -0700 (MST) Received: from oc3016140333.ibm.com (unknown [9.41.174.252]) by b03ledav006.gho.boulder.ibm.com (Postfix) with ESMTP id 338FBC6037; Mon, 20 Nov 2017 12:47:05 -0700 (MST) 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 v5 3/8] drivers/fsi: sbefifo: Add miscdevice Date: Mon, 20 Nov 2017 13:46:52 -0600 X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1511207217-14075-1-git-send-email-eajames@linux.vnet.ibm.com> References: <1511207217-14075-1-git-send-email-eajames@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17112019-0008-0000-0000-000008E6BC60 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00008100; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000240; SDB=6.00948744; UDB=6.00479090; IPR=6.00729025; BA=6.00005702; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00018111; XFM=3.00000015; UTC=2017-11-20 19:47:08 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17112019-0009-0000-0000-000044D781FB Message-Id: <1511207217-14075-4-git-send-email-eajames@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2017-11-20_11:,, 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-1711200264 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 | 355 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index 6ba190a..fed3739 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; @@ -430,6 +525,252 @@ static void sbefifo_poll_timer(unsigned long data) spin_unlock_irqrestore(&sbefifo->lock, flags); } +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) +{ + unsigned long flags; + 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_irqsave(&sbefifo->lock, flags); + xfr = sbefifo_next_xfr(sbefifo); /* next xfr to be executed */ + + if ((file->f_flags & O_NONBLOCK) && xfr && n < len) { + spin_unlock_irqrestore(&sbefifo->lock, flags); + ret = -EAGAIN; + goto out; + } + + xfr = sbefifo_enq_xfr(client); /* this xfr queued up */ + if (IS_ERR(xfr)) { + spin_unlock_irqrestore(&sbefifo->lock, flags); + ret = PTR_ERR(xfr); + goto out; + } + + spin_unlock_irqrestore(&sbefifo->lock, flags); + + /* + * 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; @@ -504,6 +845,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; @@ -529,6 +882,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 1584073410268572603@xxx Tue Nov 14 20:08:46 +0000 2017 X-GM-THRID: 1584073410268572603 X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread