* devlink dump of mlxsw_adj table triggers a panic
From: David Ahern @ 2017-10-04 23:57 UTC (permalink / raw)
To: Arkadi Sharshevsky, Jiri Pirko; +Cc: netdev@vger.kernel.org
The following devlink command on a 2700 triggers a panic every time.
Kernel is net-next at 26873308b21654b6e0785b9f9e2c5414d37a4c4c
$ devlink dpipe table dump pci/0000:03:00.0 name mlxsw_adj
devlink answers: No buffer space available
<hang>
I have seen several different stack traces and varying amounts of EMAD
errors on console:
[ 77.453364] mlxsw_spectrum 0000:03:00.0: EMAD reg access failed
(tid=64c24a400003688,reg_id=200b(sfn),type=query,status=0(operation
performed))
[ 77.466568] mlxsw_spectrum 0000:03:00.0: Failed to get FDB notifications
If it does not reproduce for you let me know and I'll grab a trace.
David
^ permalink raw reply
* [PATCH net-next 1/4] bpf: Add file mode configuration into bpf maps
From: Chenbo Feng @ 2017-10-04 23:58 UTC (permalink / raw)
To: linux-security-module
In-Reply-To: <59D56EED.1030804@iogearbox.net>
On Wed, Oct 4, 2017 at 4:29 PM, Daniel Borkmann <daniel@iogearbox.net> wrote:
> On 10/04/2017 08:29 PM, Chenbo Feng wrote:
>>
>> From: Chenbo Feng <fengc@google.com>
>>
>> Introduce the map read/write flags to the eBPF syscalls that returns the
>> map fd. The flags is used to set up the file mode when construct a new
>> file descriptor for bpf maps. To not break the backward capability, the
>> f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
>> it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
>> read the map content, it will check the file mode to see if it is
>> allowed to make the change.
>
> [...]
>>
>> +int bpf_get_file_flag(int flags)
>> +{
>> + if ((flags & BPF_F_RDONLY) && (flags & BPF_F_WRONLY))
>> + return -EINVAL;
>> + if (flags & BPF_F_RDONLY)
>> + return O_RDONLY;
>> + if (flags & BPF_F_WRONLY)
>> + return O_WRONLY;
>> + return O_RDWR;
>> }
>>
>> /* helper macro to check that unused fields 'union bpf_attr' are zero */
>> @@ -345,12 +376,17 @@ static int map_create(union bpf_attr *attr)
>> {
>> int numa_node = bpf_map_attr_numa_node(attr);
>> struct bpf_map *map;
>> + int f_flags;
>> int err;
>>
>> err = CHECK_ATTR(BPF_MAP_CREATE);
>> if (err)
>> return -EINVAL;
>>
>> + f_flags = bpf_get_file_flag(attr->map_flags);
>> + if (f_flags < 0)
>> + return f_flags;
>
>
> Wait, I just noticed, given you add BPF_F_RDONLY/BPF_F_WRONLY
> to attr->map_flags, and later go into find_and_alloc_map(),
> for map alloc, which is e.g. array_map_alloc(). There, we
> bail out with EINVAL on attr->map_flags & ~BPF_F_NUMA_NODE,
> which is the case for both BPF_F_RDONLY/BPF_F_WRONLY ... I
> would have expected that the entire code was tested properly;
> what was tested exactly in the set?
>
Thanks for pointing out this, my test for the patch create the map
with RD/WR flag which is 0.... that's why I didn't catch this. And
bpf_obj_get do not have similar checks for map_flags.
>> if (numa_node != NUMA_NO_NODE &&
>> ((unsigned int)numa_node >= nr_node_ids ||
>> !node_online(numa_node)))
>> @@ -376,7 +412,7 @@ static int map_create(union bpf_attr *attr)
>> if (err)
>> goto free_map;
>>
>> - err = bpf_map_new_fd(map);
>> + err = bpf_map_new_fd(map, f_flags);
>> if (err < 0) {
>> /* failed to allocate fd.
>> * bpf_map_put() is needed because the above
>> @@ -491,6 +527,11 @@ static int map_lookup_elem(union bpf_attr *attr)
>
> [...]
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH net-next 1/4] bpf: Add file mode configuration into bpf maps
From: Chenbo Feng @ 2017-10-04 23:58 UTC (permalink / raw)
To: Daniel Borkmann
Cc: Chenbo Feng, netdev, SELinux, linux-security-module,
Jeffrey Vander Stoep, Lorenzo Colitti, Alexei Starovoitov
In-Reply-To: <59D56EED.1030804@iogearbox.net>
On Wed, Oct 4, 2017 at 4:29 PM, Daniel Borkmann <daniel@iogearbox.net> wrote:
> On 10/04/2017 08:29 PM, Chenbo Feng wrote:
>>
>> From: Chenbo Feng <fengc@google.com>
>>
>> Introduce the map read/write flags to the eBPF syscalls that returns the
>> map fd. The flags is used to set up the file mode when construct a new
>> file descriptor for bpf maps. To not break the backward capability, the
>> f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise
>> it should be O_RDONLY or O_WRONLY. When the userspace want to modify or
>> read the map content, it will check the file mode to see if it is
>> allowed to make the change.
>
> [...]
>>
>> +int bpf_get_file_flag(int flags)
>> +{
>> + if ((flags & BPF_F_RDONLY) && (flags & BPF_F_WRONLY))
>> + return -EINVAL;
>> + if (flags & BPF_F_RDONLY)
>> + return O_RDONLY;
>> + if (flags & BPF_F_WRONLY)
>> + return O_WRONLY;
>> + return O_RDWR;
>> }
>>
>> /* helper macro to check that unused fields 'union bpf_attr' are zero */
>> @@ -345,12 +376,17 @@ static int map_create(union bpf_attr *attr)
>> {
>> int numa_node = bpf_map_attr_numa_node(attr);
>> struct bpf_map *map;
>> + int f_flags;
>> int err;
>>
>> err = CHECK_ATTR(BPF_MAP_CREATE);
>> if (err)
>> return -EINVAL;
>>
>> + f_flags = bpf_get_file_flag(attr->map_flags);
>> + if (f_flags < 0)
>> + return f_flags;
>
>
> Wait, I just noticed, given you add BPF_F_RDONLY/BPF_F_WRONLY
> to attr->map_flags, and later go into find_and_alloc_map(),
> for map alloc, which is e.g. array_map_alloc(). There, we
> bail out with EINVAL on attr->map_flags & ~BPF_F_NUMA_NODE,
> which is the case for both BPF_F_RDONLY/BPF_F_WRONLY ... I
> would have expected that the entire code was tested properly;
> what was tested exactly in the set?
>
Thanks for pointing out this, my test for the patch create the map
with RD/WR flag which is 0.... that's why I didn't catch this. And
bpf_obj_get do not have similar checks for map_flags.
>> if (numa_node != NUMA_NO_NODE &&
>> ((unsigned int)numa_node >= nr_node_ids ||
>> !node_online(numa_node)))
>> @@ -376,7 +412,7 @@ static int map_create(union bpf_attr *attr)
>> if (err)
>> goto free_map;
>>
>> - err = bpf_map_new_fd(map);
>> + err = bpf_map_new_fd(map, f_flags);
>> if (err < 0) {
>> /* failed to allocate fd.
>> * bpf_map_put() is needed because the above
>> @@ -491,6 +527,11 @@ static int map_lookup_elem(union bpf_attr *attr)
>
> [...]
^ permalink raw reply
* Re: [PATCH linux dev-4.10 v2 1/9] drivers: fsi: SBEFIFO: General clean-up
From: Andrew Jeffery @ 2017-10-04 23:59 UTC (permalink / raw)
To: Eddie James, openbmc; +Cc: joel, Edward A. James
In-Reply-To: <1506724868-13010-2-git-send-email-eajames@linux.vnet.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 18525 bytes --]
On Fri, 2017-09-29 at 17:41 -0500, Eddie James wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Correct formatting and improve the flow a bit. Remove warnings for
> offset in read/write functions, and remove the user pointer verification
> since the copy_from/to_user will do that anyway. Correct the include
> guards in the header file.
This is similar to what I complained about last time, just without bullet
points in the description.
I tried to read the diff but really struggled to make sense of it. Keeping
track of the added/removed/changed functions - amongst formatting and control
flow changes - to decide whether they were appropriate is just *really*
difficult, and I can't say with confidence that as a whole this patch is okay.
So, apologies, but I don't plan on acking this as it stands, and I don't think
it should be submitted unless several people are ready to weld Tested-by tags
to it.
Andrew
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> ---
> drivers/fsi/fsi-sbefifo.c | 207 +++++++++++++++++++++++++-------------------
> include/linux/fsi-sbefifo.h | 6 +-
> 2 files changed, 119 insertions(+), 94 deletions(-)
>
> diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
> index 1c37ff7..a4cd353 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 <linux/delay.h>
> +#include <linux/device.h>
> #include <linux/errno.h>
> -#include <linux/idr.h>
> +#include <linux/fs.h>
> #include <linux/fsi.h>
> +#include <linux/fsi-sbefifo.h>
> +#include <linux/idr.h>
> +#include <linux/kernel.h>
> +#include <linux/kref.h>
> #include <linux/list.h>
> #include <linux/miscdevice.h>
> #include <linux/module.h>
> #include <linux/of.h>
> +#include <linux/of_device.h>
> #include <linux/of_platform.h>
> #include <linux/poll.h>
> #include <linux/sched.h>
> #include <linux/slab.h>
> +#include <linux/spinlock.h>
> #include <linux/timer.h>
> #include <linux/uaccess.h>
> +#include <linux/wait.h>
>
> /*
> * The SBEFIFO is a pipe-like FSI device for communicating with
> @@ -44,6 +51,8 @@
> #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;
> @@ -94,7 +103,7 @@ struct sbefifo_client {
> 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 +111,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 +147,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 +221,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 +241,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);
> }
>
> @@ -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;
>
> @@ -349,6 +352,7 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)
> kfree(xfr);
> continue;
> }
> +
> return xfr;
> }
>
> @@ -370,7 +374,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 +392,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 +400,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 +417,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 +440,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 +468,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 +478,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 +499,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 +537,16 @@ 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,
> + size_t *ret)
> +{
> + *n = sbefifo_buf_nbreadable(&client->rbuf);
> + *ret = READ_ONCE(sbefifo->rc);
> +
> + return *ret || *n;
> +}
> +
> static ssize_t sbefifo_read_common(struct sbefifo_client *client,
> char __user *ubuf, char *kbuf, size_t len)
> {
> @@ -551,30 +563,37 @@ 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))) {
> + ret = -ERESTARTSYS;
> + goto out;
> }
> +
> 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 */
> + WARN_ONCE(1, "no xfr in queue");
> + goto out;
> + }
> +
> if (!test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) {
> /*
> * Fill the read buffer back up.
> @@ -589,22 +608,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 +640,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 +651,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);
> + xfr = sbefifo_enq_xfr(client); /* this xfr queued up */
> if (!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 +678,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) {
> INIT_LIST_HEAD(&client->xfrs);
> - sbefifo_put_client(client);
> - return sbefifo->rc;
> + ret = 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 +731,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);
> }
>
> @@ -802,29 +826,27 @@ 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;
> }
>
> @@ -837,13 +859,13 @@ static int sbefifo_probe(struct device *dev)
>
> sbefifo->idx = ida_simple_get(&sbefifo_ida, 1, INT_MAX, GFP_KERNEL);
> snprintf(sbefifo->name, sizeof(sbefifo->name), "sbefifo%d",
> - sbefifo->idx);
> + sbefifo->idx);
> init_waitqueue_head(&sbefifo->wait);
> INIT_LIST_HEAD(&sbefifo->xfrs);
>
> /* This bit of silicon doesn't offer any interrupts... */
> setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
> - (unsigned long)sbefifo);
> + (unsigned long)sbefifo);
>
> if (dev->of_node) {
> /* create platform devs for dts child nodes (occ, etc) */
> @@ -852,7 +874,7 @@ static int sbefifo_probe(struct device *dev)
> sbefifo->name, child_idx++);
> child = of_platform_device_create(np, child_name, dev);
> if (!child)
> - dev_warn(&sbefifo->fsi_dev->dev,
> + dev_warn(dev,
> "failed to create child node dev\n");
> }
> }
> @@ -922,10 +944,13 @@ static int sbefifo_init(void)
> 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 <bradleyb@fuzziesquirrel.com>");
> -MODULE_DESCRIPTION("Linux device interface to the POWER self boot engine");
> +MODULE_AUTHOR("Eddie James <eajames@linux.vnet.ibm.com>");
> +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 */
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* [U-Boot] [PATCH v1 08/12] efi_loader: console support for color attributes
From: Rob Clark @ 2017-10-05 0:00 UTC (permalink / raw)
To: u-boot
In-Reply-To: <fd00ccb0-51ad-2c0c-8a83-06c47c0a190e@gmx.de>
On Wed, Oct 4, 2017 at 7:53 PM, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> On 10/05/2017 01:19 AM, Rob Clark wrote:
>> On Wed, Oct 4, 2017 at 6:01 PM, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>> On 10/04/2017 10:54 PM, Rob Clark wrote:
>>>> On Wed, Oct 4, 2017 at 2:53 PM, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>>>> On 09/10/2017 03:22 PM, Rob Clark wrote:
>>>>>> Shell.efi uses this, and supporting color attributes makes things look
>>>>>> nicer. Map the EFI fg/bg color attributes to ANSI escape sequences.
>>>>>> Not all colors have a perfect match, but spec just says "Devices
>>>>>> supporting a different number of text colors are required to emulate the
>>>>>> above colors to the best of the device’s capabilities".
>>>>>>
>>>>>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>>>>>> ---
>>>>>> include/efi_api.h | 29 +++++++++++++++++++++++++++++
>>>>>> lib/efi_loader/efi_console.c | 30 ++++++++++++++++++++++++++++++
>>>>>> 2 files changed, 59 insertions(+)
>>>>>>
>>>>>> diff --git a/include/efi_api.h b/include/efi_api.h
>>>>>> index 87c8ffe68e..3cc1dbac2e 100644
>>>>>> --- a/include/efi_api.h
>>>>>> +++ b/include/efi_api.h
>>>>>> @@ -426,6 +426,35 @@ struct simple_text_output_mode {
>>>>>> EFI_GUID(0x387477c2, 0x69c7, 0x11d2, \
>>>>>> 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
>>>>>>
>>>>>> +#define EFI_BLACK 0x00
>>>>>> +#define EFI_BLUE 0x01
>>>>>> +#define EFI_GREEN 0x02
>>>>>> +#define EFI_CYAN 0x03
>>>>>> +#define EFI_RED 0x04
>>>>>> +#define EFI_MAGENTA 0x05
>>>>>> +#define EFI_BROWN 0x06
>>>>>> +#define EFI_LIGHTGRAY 0x07
>>>>>> +#define EFI_BRIGHT 0x08
>>>>>> +#define EFI_DARKGRAY 0x08
>>>>>> +#define EFI_LIGHTBLUE 0x09
>>>>>> +#define EFI_LIGHTGREEN 0x0a
>>>>>> +#define EFI_LIGHTCYAN 0x0b
>>>>>> +#define EFI_LIGHTRED 0x0c
>>>>>> +#define EFI_LIGHTMAGENTA 0x0d
>>>>>> +#define EFI_YELLOW 0x0e
>>>>>> +#define EFI_WHITE 0x0f
>>>>>> +#define EFI_BACKGROUND_BLACK 0x00
>>>>>> +#define EFI_BACKGROUND_BLUE 0x10
>>>>>> +#define EFI_BACKGROUND_GREEN 0x20
>>>>>> +#define EFI_BACKGROUND_CYAN 0x30
>>>>>> +#define EFI_BACKGROUND_RED 0x40
>>>>>> +#define EFI_BACKGROUND_MAGENTA 0x50
>>>>>> +#define EFI_BACKGROUND_BROWN 0x60
>>>>>> +#define EFI_BACKGROUND_LIGHTGRAY 0x70
>>>>>
>>>>> Will we ever use these constants?
>>>>>
>>>>
>>>> possibly not, but it is useful to understand what is going on with
>>>> efi->ansi mapping, so I would prefer to keep them.
>>>>
>>>>>
>>>>> Where are the comments explaining the defines below?
>>>>>
>>>>>> +
>>>>>> +#define EFI_ATTR_FG(attr) ((attr) & 0x0f)
>>>>>
>>>>> This saves 8 entries in the table below.
>>>>> +#define EFI_ATTR_FG(attr) ((attr) & 0x07)
>>>>>
>>>>>> +#define EFI_ATTR_BG(attr) (((attr) >> 4) & 0x7)
>>>>>
>>>>> Add
>>>>> #define EFI_ATTR_BOLD(attr) (((attr) >> 3) & 0x01)
>>>>>
>>>>>> +
>>>>>> struct efi_simple_text_output_protocol {
>>>>>> void *reset;
>>>>>> efi_status_t (EFIAPI *output_string)(
>>>>>> diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
>>>>>> index 2e13fdc096..fcd65ca488 100644
>>>>>> --- a/lib/efi_loader/efi_console.c
>>>>>> +++ b/lib/efi_loader/efi_console.c
>>>>>> @@ -316,12 +316,42 @@ static efi_status_t EFIAPI efi_cout_set_mode(
>>>>>> return EFI_EXIT(EFI_SUCCESS);
>>>>>> }
>>>>>>
>>>>>> +static const struct {
>>>>>> + unsigned fg;
>>>>>> + unsigned bg;
>>>>>> +} color[] = {
>>>>>> + { 30, 40 }, /* 0: black */
>>>>>> + { 34, 44 }, /* 1: blue */
>>>>>> + { 32, 42 }, /* 2: green */
>>>>>> + { 36, 46 }, /* 3: cyan */
>>>>>> + { 31, 41 }, /* 4: red */
>>>>>> + { 35, 45 }, /* 5: magenta */
>>>>>> + { 30, 40 }, /* 6: brown, map to black */
>>>>>
>>>>> This should be { 33, 43 }
>>>>>
>>>>>> + { 37, 47 }, /* 7: light grey, map to white */
>>>>>
>>>>> The entries below are redundant.
>>>>>
>>>>>> + { 37, 47 }, /* 8: bright, map to white */
>>>>>> + { 34, 44 }, /* 9: light blue, map to blue */
>>>>>> + { 32, 42 }, /* A: light green, map to green */
>>>>>> + { 36, 46 }, /* B: light cyan, map to cyan */
>>>>>> + { 31, 41 }, /* C: light red, map to red */
>>>>>> + { 35, 45 }, /* D: light magenta, map to magenta */
>>>>>> + { 33, 43 }, /* E: yellow */
>>>>>> + { 37, 47 }, /* F: white */
>>>>>> +};
>>>>>> +
>>>>
>>>> I'm not totally convinced about mapping extra colors that UEFI defines
>>>> to bold.. unless you have some example of prior-art for this on other
>>>> platforms.
>>>
>>> See
>>> Standard ECMA-48 - Control Functions for Coded Character Sets
>>> chapter 8.3.117 SGR - SELECT GRAPHIC RENDITION
>>>
>>> 1 - bold or increased intensity
>>> 22 - normal colour or normal intensity (neither bold nor faint)
>>>
>>> You can easily experiment in your bash shell like this:
>>>
>>> printf "\x1b[1;32;40m bold \x1b[22;32;40m normal\x1b[22;39;49m\n";
>>>
>>> You will find that "bold" prints bold and bright in the KDE konsole and
>>> xterm.
>>
>> but I think we don't want (potential) font changes, just color changes..
>>
>> if you can find the code in edk2 that does this, I guess it would be a
>> reasonable precedent to follow.. but if not I wanted to avoid things
>> that might be specific to particular terminal emulators, since I
>> wasn't really looking forward to testing them all. Otherwise I'd just
>> rely on the extension that allowed 256 colors..
>>
>> BR,
>> -R
>
> The same problem seems has led the EDK folks to a similar solution.
>
> See
> MdeModulePkg/Universal/Console/TerminalDxe/TerminalConOut.c
ok, I'll have a closer look at that.. I don't feel badly about doing
the same thing that edk2 does when there is doubt ;-)
BR,
-R
> Everything starts with this array:
>
> { ESC, '[', '0', 'm', ESC, '[', '4', '0', 'm', ESC, '[', '4', '0', 'm', 0 };
>
> The first '0' is replaced by either 0 or 1 depending on brightness.
>
> mSetAttributeString[BRIGHT_CONTROL_OFFSET] =
> (CHAR16) ('0' + BrightControl);
>
> The first '4', '0' is replaced by the foreground color.
> The second '4', '0' is replaced by the background color.
>
> ECMA 48 says:
>
> 0 - default rendition, cancels the effect of any preceding SGR
>
> So you can use this instead of 22.
>
> Best regards
>
> Heinrich
>
>
>>
>>> Using colors 90-97 as foreground colors produces only bright but not
>>> bold in the KDE konsole and xterm:
>>>
>>> printf "\x1b[92;40m bold \x1b[32;40m normal\x1b[22;39;49m\n";
>>>
>>> But these codes are not defined in ECMA-48.
>>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>
>
^ permalink raw reply
* Re: [PATCH] input: synaptics-rmi4: make array rmi_f54_report_type_names static
From: Dmitry Torokhov @ 2017-10-05 0:00 UTC (permalink / raw)
To: Guenter Roeck
Cc: Colin King, Andrew Duggan, linux-input, kernel-janitors,
linux-kernel
In-Reply-To: <20171002221852.GA12530@roeck-us.net>
On Mon, Oct 02, 2017 at 03:18:52PM -0700, Guenter Roeck wrote:
> On Mon, Oct 02, 2017 at 11:05:25PM +0100, Colin King wrote:
> > From: Colin Ian King <colin.king@canonical.com>
> >
> > The array rmi_f54_report_type_names is local to the source and does
> > not need to be in global scope, so make it static. Also make the array
> > const char * const
> >
> > Cleans up sparse warning:
> > symbol 'rmi_f54_report_type_names' was not declared. Should it be static?
> >
> > Signed-off-by: Colin Ian King <colin.king@canonical.com>
>
> Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Applied, thank you.
>
> > ---
> > drivers/input/rmi4/rmi_f54.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
> > index f5206e2c767e..70f6310f1d15 100644
> > --- a/drivers/input/rmi4/rmi_f54.c
> > +++ b/drivers/input/rmi4/rmi_f54.c
> > @@ -73,7 +73,7 @@ enum rmi_f54_report_type {
> > F54_MAX_REPORT_TYPE,
> > };
> >
> > -const char *rmi_f54_report_type_names[] = {
> > +static const char * const rmi_f54_report_type_names[] = {
> > [F54_REPORT_NONE] = "Unknown",
> > [F54_8BIT_IMAGE] = "Normalized 8-Bit Image",
> > [F54_16BIT_IMAGE] = "Normalized 16-Bit Image",
> > --
> > 2.14.1
> >
--
Dmitry
^ permalink raw reply
* Re: [PATCH] input: synaptics-rmi4: make array rmi_f54_report_type_names static
From: Dmitry Torokhov @ 2017-10-05 0:00 UTC (permalink / raw)
To: Guenter Roeck
Cc: Colin King, Andrew Duggan, linux-input, kernel-janitors,
linux-kernel
In-Reply-To: <20171002221852.GA12530@roeck-us.net>
On Mon, Oct 02, 2017 at 03:18:52PM -0700, Guenter Roeck wrote:
> On Mon, Oct 02, 2017 at 11:05:25PM +0100, Colin King wrote:
> > From: Colin Ian King <colin.king@canonical.com>
> >
> > The array rmi_f54_report_type_names is local to the source and does
> > not need to be in global scope, so make it static. Also make the array
> > const char * const
> >
> > Cleans up sparse warning:
> > symbol 'rmi_f54_report_type_names' was not declared. Should it be static?
> >
> > Signed-off-by: Colin Ian King <colin.king@canonical.com>
>
> Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Applied, thank you.
>
> > ---
> > drivers/input/rmi4/rmi_f54.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
> > index f5206e2c767e..70f6310f1d15 100644
> > --- a/drivers/input/rmi4/rmi_f54.c
> > +++ b/drivers/input/rmi4/rmi_f54.c
> > @@ -73,7 +73,7 @@ enum rmi_f54_report_type {
> > F54_MAX_REPORT_TYPE,
> > };
> >
> > -const char *rmi_f54_report_type_names[] = {
> > +static const char * const rmi_f54_report_type_names[] = {
> > [F54_REPORT_NONE] = "Unknown",
> > [F54_8BIT_IMAGE] = "Normalized 8-Bit Image",
> > [F54_16BIT_IMAGE] = "Normalized 16-Bit Image",
> > --
> > 2.14.1
> >
--
Dmitry
^ permalink raw reply
* Static Analysis
From: Brad Hubbard @ 2017-10-05 0:00 UTC (permalink / raw)
To: ceph-devel
Latest static analyser results are up on http://people.redhat.com/bhubbard/
--
Cheers,
Brad
^ permalink raw reply
* [PATCH v6 0/9] block, scsi, md: Improve suspend and resume
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche
Hello Jens,
It is known that during the resume following a hibernate, especially when
using an md RAID1 array created on top of SCSI devices, sometimes the
system hangs instead of coming up properly. This patch series fixes this
problem. This patch series is an alternative for Ming Lei's "block/scsi:
safe SCSI quiescing" patch series. The advantage of this patch series is
that a fix for the md driver has been included.
These patches have been tested on top of the block layer for-next branch.
Please consider these changes for kernel v4.15.
Thanks,
Bart.
Changes between v5 and v6:
- Split an md patch into two patches to make it easier to review the changes.
- For the md patch that suspends I/O while the system is frozen, switched back
to the freezer mechanism because this makes the code shorter and easier to
review.
- Changed blk_set/clear_preempt_only() from EXPORT_SYMBOL() into
EXPORT_SYMBOL_GPL().
- Made blk_set_preempt_only() behave as a test-and-set operation.
- Introduced blk_get_request_flags() and BLK_MQ_REQ_PREEMPT as requested by
Christoph and reduced the number of arguments of blk_queue_enter() back from
three to two.
- In scsi_device_quiesce(), moved the blk_mq_freeze_queue() call out of a
critical section. Made the explanation of why the synchronize_rcu() call
is necessary more detailed.
Changes between v4 and v5:
- Split blk_set_preempt_only() into two functions as requested by Christoph.
- Made blk_get_request() trigger WARN_ONCE() if it is attempted to allocate
a request while the system is frozen. This is a big help to identify drivers
that submit I/O while the system is frozen.
- Since kernel thread freezing is on its way out, reworked the approach for
avoiding that the md driver submits I/O while the system is frozen such that
the approach no longer depends on the kernel thread freeze mechanism.
- Fixed the (theoretical) deadlock in scsi_device_quiesce() that was identified
by Ming.
Changes between v3 and v4:
- Made sure that this patch series not only works for scsi-mq but also for
the legacy SCSI stack.
- Removed an smp_rmb()/smp_wmb() pair from the hot path and added a comment
that explains why that is safe.
- Reordered the patches in this patch series.
Changes between v2 and v3:
- Made md kernel threads freezable.
- Changed the approach for quiescing SCSI devices again.
- Addressed Ming's review comments.
Changes compared to v1 of this patch series:
- Changed the approach and rewrote the patch series.
Bart Van Assche (8):
md: Rename md_notifier into md_reboot_notifier
md: Introduce md_stop_all_writes()
md: Neither resync nor reshape while the system is frozen
block: Introduce blk_get_request_flags()
block: Introduce BLK_MQ_REQ_PREEMPT
ide, scsi: Tell the block layer at request allocation time about
preempt requests
block: Add the QUEUE_FLAG_PREEMPT_ONLY request queue flag
block, scsi: Make SCSI device suspend and resume work reliably
Ming Lei (1):
block: Make q_usage_counter also track legacy requests
block/blk-core.c | 127 ++++++++++++++++++++++++++++++++++++++++--------
block/blk-mq.c | 16 +++---
block/blk-timeout.c | 2 +-
drivers/ide/ide-pm.c | 4 +-
drivers/md/md.c | 45 ++++++++++++++---
drivers/scsi/scsi_lib.c | 33 ++++++++-----
fs/block_dev.c | 4 +-
include/linux/blk-mq.h | 1 +
include/linux/blkdev.h | 11 ++++-
9 files changed, 189 insertions(+), 54 deletions(-)
--
2.14.2
^ permalink raw reply
* [PATCH v6 2/9] md: Introduce md_stop_all_writes()
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Shaohua Li, linux-raid, Hannes Reinecke,
Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
Introduce md_stop_all_writes() because the next patch will add
a second caller for this function. This patch does not change
any functionality.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Shaohua Li <shli@kernel.org>
Cc: linux-raid@vger.kernel.org
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
drivers/md/md.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 8933cafc212d..b99584e5d6b1 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -8937,8 +8937,7 @@ int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
}
EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
-static int md_notify_reboot(struct notifier_block *this,
- unsigned long code, void *x)
+static void md_stop_all_writes(void)
{
struct list_head *tmp;
struct mddev *mddev;
@@ -8962,6 +8961,12 @@ static int md_notify_reboot(struct notifier_block *this,
*/
if (need_delay)
mdelay(1000*1);
+}
+
+static int md_notify_reboot(struct notifier_block *this,
+ unsigned long code, void *x)
+{
+ md_stop_all_writes();
return NOTIFY_DONE;
}
--
2.14.2
^ permalink raw reply related
* [PATCH v6 3/9] md: Neither resync nor reshape while the system is frozen
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Shaohua Li, linux-raid, Hannes Reinecke,
Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
Some people use the md driver on laptops and use the suspend and
resume functionality. Since it is essential that submitting of
new I/O requests stops before a hibernation image is created,
interrupt the md resync and reshape actions if the system is
being frozen. Note: the resync and reshape will restart after
the system is resumed and a message similar to the following
will appear in the system log:
md: md0: data-check interrupted.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Shaohua Li <shli@kernel.org>
Cc: linux-raid@vger.kernel.org
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
drivers/md/md.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index b99584e5d6b1..d712d3320c1d 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -66,6 +66,8 @@
#include <linux/raid/md_u.h>
#include <linux/slab.h>
#include <linux/percpu-refcount.h>
+#include <linux/freezer.h>
+#include <linux/suspend.h>
#include <trace/events/block.h>
#include "md.h"
@@ -7439,6 +7441,7 @@ static int md_thread(void *arg)
*/
allow_signal(SIGKILL);
+ set_freezable();
while (!kthread_should_stop()) {
/* We need to wait INTERRUPTIBLE so that
@@ -7449,7 +7452,7 @@ static int md_thread(void *arg)
if (signal_pending(current))
flush_signals(current);
- wait_event_interruptible_timeout
+ wait_event_freezable_timeout
(thread->wqueue,
test_bit(THREAD_WAKEUP, &thread->flags)
|| kthread_should_stop() || kthread_should_park(),
@@ -8963,6 +8966,29 @@ static void md_stop_all_writes(void)
mdelay(1000*1);
}
+/*
+ * Ensure that neither resyncing nor reshaping occurs while the system is
+ * frozen.
+ */
+static int md_notify_pm(struct notifier_block *bl, unsigned long state,
+ void *unused)
+{
+ pr_debug("%s: state = %ld\n", __func__, state);
+
+ switch (state) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ case PM_RESTORE_PREPARE:
+ md_stop_all_writes();
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block md_pm_notifier = {
+ .notifier_call = md_notify_pm,
+};
+
static int md_notify_reboot(struct notifier_block *this,
unsigned long code, void *x)
{
@@ -9009,6 +9035,7 @@ static int __init md_init(void)
md_probe, NULL, NULL);
register_reboot_notifier(&md_reboot_notifier);
+ register_pm_notifier(&md_pm_notifier);
raid_table_header = register_sysctl_table(raid_root_table);
md_geninit();
@@ -9248,6 +9275,7 @@ static __exit void md_exit(void)
unregister_blkdev(MD_MAJOR,"md");
unregister_blkdev(mdp_major, "mdp");
+ unregister_pm_notifier(&md_pm_notifier);
unregister_reboot_notifier(&md_reboot_notifier);
unregister_sysctl_table(raid_table_header);
--
2.14.2
^ permalink raw reply related
* [PATCH v6 4/9] block: Make q_usage_counter also track legacy requests
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Hannes Reinecke, Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
From: Ming Lei <ming.lei@redhat.com>
This patch makes it possible to pause request allocation for
the legacy block layer by calling blk_mq_freeze_queue() and
blk_mq_unfreeze_queue().
Signed-off-by: Ming Lei <ming.lei@redhat.com>
[ bvanassche: Combined two patches into one, edited a comment and made sure
REQ_NOWAIT is handled properly in blk_old_get_request() ]
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
block/blk-core.c | 12 ++++++++++++
block/blk-mq.c | 10 ++--------
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 14f7674fa0b1..265a69511c47 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -610,6 +610,9 @@ void blk_set_queue_dying(struct request_queue *q)
}
spin_unlock_irq(q->queue_lock);
}
+
+ /* Make blk_queue_enter() reexamine the DYING flag. */
+ wake_up_all(&q->mq_freeze_wq);
}
EXPORT_SYMBOL_GPL(blk_set_queue_dying);
@@ -1395,16 +1398,22 @@ static struct request *blk_old_get_request(struct request_queue *q,
unsigned int op, gfp_t gfp_mask)
{
struct request *rq;
+ int ret = 0;
WARN_ON_ONCE(q->mq_ops);
/* create ioc upfront */
create_io_context(gfp_mask, q->node);
+ ret = blk_queue_enter(q, !(gfp_mask & __GFP_DIRECT_RECLAIM) ||
+ (op & REQ_NOWAIT));
+ if (ret)
+ return ERR_PTR(ret);
spin_lock_irq(q->queue_lock);
rq = get_request(q, op, NULL, gfp_mask);
if (IS_ERR(rq)) {
spin_unlock_irq(q->queue_lock);
+ blk_queue_exit(q);
return rq;
}
@@ -1576,6 +1585,7 @@ void __blk_put_request(struct request_queue *q, struct request *req)
blk_free_request(rl, req);
freed_request(rl, sync, rq_flags);
blk_put_rl(rl);
+ blk_queue_exit(q);
}
}
EXPORT_SYMBOL_GPL(__blk_put_request);
@@ -1857,8 +1867,10 @@ static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio)
* Grab a free request. This is might sleep but can not fail.
* Returns with the queue unlocked.
*/
+ blk_queue_enter_live(q);
req = get_request(q, bio->bi_opf, bio, GFP_NOIO);
if (IS_ERR(req)) {
+ blk_queue_exit(q);
__wbt_done(q->rq_wb, wb_acct);
if (PTR_ERR(req) == -ENOMEM)
bio->bi_status = BLK_STS_RESOURCE;
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 7f01d69879d6..ebf85e270509 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -125,7 +125,8 @@ void blk_freeze_queue_start(struct request_queue *q)
freeze_depth = atomic_inc_return(&q->mq_freeze_depth);
if (freeze_depth == 1) {
percpu_ref_kill(&q->q_usage_counter);
- blk_mq_run_hw_queues(q, false);
+ if (q->mq_ops)
+ blk_mq_run_hw_queues(q, false);
}
}
EXPORT_SYMBOL_GPL(blk_freeze_queue_start);
@@ -255,13 +256,6 @@ void blk_mq_wake_waiters(struct request_queue *q)
queue_for_each_hw_ctx(q, hctx, i)
if (blk_mq_hw_queue_mapped(hctx))
blk_mq_tag_wakeup_all(hctx->tags, true);
-
- /*
- * If we are called because the queue has now been marked as
- * dying, we need to ensure that processes currently waiting on
- * the queue are notified as well.
- */
- wake_up_all(&q->mq_freeze_wq);
}
bool blk_mq_can_queue(struct blk_mq_hw_ctx *hctx)
--
2.14.2
^ permalink raw reply related
* [PATCH v6 5/9] block: Introduce blk_get_request_flags()
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Hannes Reinecke, Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
A side effect of this patch is that the GFP mask that is passed to
several allocation functions in the legacy block layer is changed
from GFP_KERNEL into __GFP_DIRECT_RECLAIM.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
block/blk-core.c | 50 +++++++++++++++++++++++++++++++++++---------------
include/linux/blkdev.h | 3 +++
2 files changed, 38 insertions(+), 15 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 265a69511c47..03156d9ede94 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1157,7 +1157,7 @@ int blk_update_nr_requests(struct request_queue *q, unsigned int nr)
* @rl: request list to allocate from
* @op: operation and flags
* @bio: bio to allocate request for (can be %NULL)
- * @gfp_mask: allocation mask
+ * @flags: BLQ_MQ_REQ_* flags
*
* Get a free request from @q. This function may fail under memory
* pressure or if @q is dead.
@@ -1167,7 +1167,7 @@ int blk_update_nr_requests(struct request_queue *q, unsigned int nr)
* Returns request pointer on success, with @q->queue_lock *not held*.
*/
static struct request *__get_request(struct request_list *rl, unsigned int op,
- struct bio *bio, gfp_t gfp_mask)
+ struct bio *bio, unsigned int flags)
{
struct request_queue *q = rl->q;
struct request *rq;
@@ -1176,6 +1176,8 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
struct io_cq *icq = NULL;
const bool is_sync = op_is_sync(op);
int may_queue;
+ gfp_t gfp_mask = flags & BLK_MQ_REQ_NOWAIT ? GFP_ATOMIC :
+ __GFP_DIRECT_RECLAIM;
req_flags_t rq_flags = RQF_ALLOCED;
lockdep_assert_held(q->queue_lock);
@@ -1336,7 +1338,7 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
* @q: request_queue to allocate request from
* @op: operation and flags
* @bio: bio to allocate request for (can be %NULL)
- * @gfp_mask: allocation mask
+ * @flags: BLK_MQ_REQ_* flags.
*
* Get a free request from @q. If %__GFP_DIRECT_RECLAIM is set in @gfp_mask,
* this function keeps retrying under memory pressure and fails iff @q is dead.
@@ -1346,7 +1348,7 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
* Returns request pointer on success, with @q->queue_lock *not held*.
*/
static struct request *get_request(struct request_queue *q, unsigned int op,
- struct bio *bio, gfp_t gfp_mask)
+ struct bio *bio, unsigned int flags)
{
const bool is_sync = op_is_sync(op);
DEFINE_WAIT(wait);
@@ -1358,7 +1360,7 @@ static struct request *get_request(struct request_queue *q, unsigned int op,
rl = blk_get_rl(q, bio); /* transferred to @rq on success */
retry:
- rq = __get_request(rl, op, bio, gfp_mask);
+ rq = __get_request(rl, op, bio, flags);
if (!IS_ERR(rq))
return rq;
@@ -1367,7 +1369,7 @@ static struct request *get_request(struct request_queue *q, unsigned int op,
return ERR_PTR(-EAGAIN);
}
- if (!gfpflags_allow_blocking(gfp_mask) || unlikely(blk_queue_dying(q))) {
+ if ((flags & BLK_MQ_REQ_NOWAIT) || unlikely(blk_queue_dying(q))) {
blk_put_rl(rl);
return rq;
}
@@ -1394,10 +1396,13 @@ static struct request *get_request(struct request_queue *q, unsigned int op,
goto retry;
}
+/* flags: BLK_MQ_REQ_PREEMPT and/or BLK_MQ_REQ_NOWAIT. */
static struct request *blk_old_get_request(struct request_queue *q,
- unsigned int op, gfp_t gfp_mask)
+ unsigned int op, unsigned int flags)
{
struct request *rq;
+ gfp_t gfp_mask = flags & BLK_MQ_REQ_NOWAIT ? GFP_ATOMIC :
+ __GFP_DIRECT_RECLAIM;
int ret = 0;
WARN_ON_ONCE(q->mq_ops);
@@ -1410,7 +1415,7 @@ static struct request *blk_old_get_request(struct request_queue *q,
if (ret)
return ERR_PTR(ret);
spin_lock_irq(q->queue_lock);
- rq = get_request(q, op, NULL, gfp_mask);
+ rq = get_request(q, op, NULL, flags);
if (IS_ERR(rq)) {
spin_unlock_irq(q->queue_lock);
blk_queue_exit(q);
@@ -1424,25 +1429,40 @@ static struct request *blk_old_get_request(struct request_queue *q,
return rq;
}
-struct request *blk_get_request(struct request_queue *q, unsigned int op,
- gfp_t gfp_mask)
+/**
+ * blk_get_request_flags - allocate a request
+ * @q: request queue to allocate a request for
+ * @op: operation (REQ_OP_*) and REQ_* flags, e.g. REQ_SYNC.
+ * @flags: BLK_MQ_REQ_* flags, e.g. BLK_MQ_REQ_NOWAIT.
+ */
+struct request *blk_get_request_flags(struct request_queue *q, unsigned int op,
+ unsigned int flags)
{
struct request *req;
+ WARN_ON_ONCE(op & REQ_NOWAIT);
+ WARN_ON_ONCE(flags & ~BLK_MQ_REQ_NOWAIT);
+
if (q->mq_ops) {
- req = blk_mq_alloc_request(q, op,
- (gfp_mask & __GFP_DIRECT_RECLAIM) ?
- 0 : BLK_MQ_REQ_NOWAIT);
+ req = blk_mq_alloc_request(q, op, flags);
if (!IS_ERR(req) && q->mq_ops->initialize_rq_fn)
q->mq_ops->initialize_rq_fn(req);
} else {
- req = blk_old_get_request(q, op, gfp_mask);
+ req = blk_old_get_request(q, op, flags);
if (!IS_ERR(req) && q->initialize_rq_fn)
q->initialize_rq_fn(req);
}
return req;
}
+EXPORT_SYMBOL(blk_get_request_flags);
+
+struct request *blk_get_request(struct request_queue *q, unsigned int op,
+ gfp_t gfp_mask)
+{
+ return blk_get_request_flags(q, op, gfp_mask & __GFP_DIRECT_RECLAIM ?
+ 0 : BLK_MQ_REQ_NOWAIT);
+}
EXPORT_SYMBOL(blk_get_request);
/**
@@ -1868,7 +1888,7 @@ static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio)
* Returns with the queue unlocked.
*/
blk_queue_enter_live(q);
- req = get_request(q, bio->bi_opf, bio, GFP_NOIO);
+ req = get_request(q, bio->bi_opf, bio, 0);
if (IS_ERR(req)) {
blk_queue_exit(q);
__wbt_done(q->rq_wb, wb_acct);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 02fa42d24b52..9a85f6ad4f89 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -940,6 +940,9 @@ extern void blk_rq_init(struct request_queue *q, struct request *rq);
extern void blk_init_request_from_bio(struct request *req, struct bio *bio);
extern void blk_put_request(struct request *);
extern void __blk_put_request(struct request_queue *, struct request *);
+extern struct request *blk_get_request_flags(struct request_queue *,
+ unsigned int op,
+ unsigned int flags);
extern struct request *blk_get_request(struct request_queue *, unsigned int op,
gfp_t gfp_mask);
extern void blk_requeue_request(struct request_queue *, struct request *);
--
2.14.2
^ permalink raw reply related
* [PATCH v6 6/9] block: Introduce BLK_MQ_REQ_PREEMPT
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Hannes Reinecke, Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
Set RQF_PREEMPT if BLK_MQ_REQ_PREEMPT is passed to
blk_get_request_flags().
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
block/blk-core.c | 4 +++-
block/blk-mq.c | 2 ++
include/linux/blk-mq.h | 1 +
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 03156d9ede94..842e67e0b7ab 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1260,6 +1260,8 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
blk_rq_set_rl(rq, rl);
rq->cmd_flags = op;
rq->rq_flags = rq_flags;
+ if (flags & BLK_MQ_REQ_PREEMPT)
+ rq->rq_flags |= RQF_PREEMPT;
/* init elvpriv */
if (rq_flags & RQF_ELVPRIV) {
@@ -1441,7 +1443,7 @@ struct request *blk_get_request_flags(struct request_queue *q, unsigned int op,
struct request *req;
WARN_ON_ONCE(op & REQ_NOWAIT);
- WARN_ON_ONCE(flags & ~BLK_MQ_REQ_NOWAIT);
+ WARN_ON_ONCE(flags & ~(BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_PREEMPT));
if (q->mq_ops) {
req = blk_mq_alloc_request(q, op, flags);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index ebf85e270509..271657992d1a 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -290,6 +290,8 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data,
rq->q = data->q;
rq->mq_ctx = data->ctx;
rq->cmd_flags = op;
+ if (data->flags & BLK_MQ_REQ_PREEMPT)
+ rq->rq_flags |= RQF_PREEMPT;
if (blk_queue_io_stat(data->q))
rq->rq_flags |= RQF_IO_STAT;
/* do not touch atomic flags, it needs atomic ops against the timer */
diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h
index 50c6485cb04f..ee9fc3fb7fca 100644
--- a/include/linux/blk-mq.h
+++ b/include/linux/blk-mq.h
@@ -200,6 +200,7 @@ enum {
BLK_MQ_REQ_NOWAIT = (1 << 0), /* return when out of requests */
BLK_MQ_REQ_RESERVED = (1 << 1), /* allocate from reserved pool */
BLK_MQ_REQ_INTERNAL = (1 << 2), /* allocate internal/sched tag */
+ BLK_MQ_REQ_PREEMPT = (1 << 3), /* set RQF_PREEMPT */
};
struct request *blk_mq_alloc_request(struct request_queue *q, unsigned int op,
--
2.14.2
^ permalink raw reply related
* [PATCH v6 9/9] block, scsi: Make SCSI device suspend and resume work reliably
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Hannes Reinecke, Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
It is essential during suspend and resume that neither the filesystem
state nor the filesystem metadata in RAM changes. This is why while
the hibernation image is being written or restored that SCSI devices
are quiesced. The SCSI core quiesces devices through scsi_device_quiesce()
and scsi_device_resume(). In the SDEV_QUIESCE state execution of
non-preempt requests is deferred. This is realized by returning
BLKPREP_DEFER from inside scsi_prep_state_check() for quiesced SCSI
devices. Avoid that a full queue prevents power management requests
to be submitted by deferring allocation of non-preempt requests for
devices in the quiesced state. This patch has been tested by running
the following commands and by verifying that after resume the fio job
is still running:
for d in /sys/class/block/sd*[a-z]; do
hcil=$(readlink "$d/device")
hcil=${hcil#../../../}
echo 4 > "$d/queue/nr_requests"
echo 1 > "/sys/class/scsi_device/$hcil/device/queue_depth"
done
bdev=$(readlink /dev/disk/by-uuid/5217d83f-213e-4b42-b86e-20013325ba6c)
bdev=${bdev#../../}
hcil=$(readlink "/sys/block/$bdev/device")
hcil=${hcil#../../../}
fio --name="$bdev" --filename="/dev/$bdev" --buffered=0 --bs=512 --rw=randread \
--ioengine=libaio --numjobs=4 --iodepth=16 --iodepth_batch=1 --thread \
--loops=$((2**31)) &
pid=$!
sleep 1
systemctl hibernate
sleep 10
kill $pid
Reported-by: Oleksandr Natalenko <oleksandr@natalenko.name>
References: "I/O hangs after resuming from suspend-to-ram" (https://marc.info/?l=linux-block&m=150340235201348).
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
block/blk-core.c | 38 ++++++++++++++++++++++++++++++--------
block/blk-mq.c | 4 ++--
block/blk-timeout.c | 2 +-
drivers/scsi/scsi_lib.c | 27 +++++++++++++++++++--------
fs/block_dev.c | 4 ++--
include/linux/blkdev.h | 2 +-
6 files changed, 55 insertions(+), 22 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index b8d90fc29b35..81a4bb119d50 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -371,6 +371,7 @@ void blk_clear_preempt_only(struct request_queue *q)
spin_lock_irqsave(q->queue_lock, flags);
queue_flag_clear(QUEUE_FLAG_PREEMPT_ONLY, q);
+ wake_up_all(&q->mq_freeze_wq);
spin_unlock_irqrestore(q->queue_lock, flags);
}
EXPORT_SYMBOL_GPL(blk_clear_preempt_only);
@@ -792,15 +793,34 @@ struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
}
EXPORT_SYMBOL(blk_alloc_queue);
-int blk_queue_enter(struct request_queue *q, bool nowait)
+/**
+ * blk_queue_enter() - try to increase q->q_usage_counter
+ * @q: request queue pointer
+ * @flags: BLK_MQ_REQ_NOWAIT and/or BLK_MQ_REQ_PREEMPT
+ */
+int blk_queue_enter(struct request_queue *q, unsigned int flags)
{
+ const bool preempt = flags & BLK_MQ_REQ_PREEMPT;
+
while (true) {
int ret;
- if (percpu_ref_tryget_live(&q->q_usage_counter))
- return 0;
+ if (percpu_ref_tryget_live(&q->q_usage_counter)) {
+ /*
+ * The code that sets the PREEMPT_ONLY flag is
+ * responsible for ensuring that that flag is globally
+ * visible before the queue is unfrozen.
+ */
+ if (preempt || !blk_queue_preempt_only(q)) {
+ return 0;
+ } else {
+ percpu_ref_put(&q->q_usage_counter);
+ WARN_ONCE("%s: Attempt to allocate non-preempt request in preempt-only mode.\n",
+ kobject_name(q->kobj.parent));
+ }
+ }
- if (nowait)
+ if (flags & BLK_MQ_REQ_NOWAIT)
return -EBUSY;
/*
@@ -813,7 +833,8 @@ int blk_queue_enter(struct request_queue *q, bool nowait)
smp_rmb();
ret = wait_event_interruptible(q->mq_freeze_wq,
- !atomic_read(&q->mq_freeze_depth) ||
+ (atomic_read(&q->mq_freeze_depth) == 0 &&
+ (preempt || !blk_queue_preempt_only(q))) ||
blk_queue_dying(q));
if (blk_queue_dying(q))
return -ENODEV;
@@ -1441,8 +1462,7 @@ static struct request *blk_old_get_request(struct request_queue *q,
/* create ioc upfront */
create_io_context(gfp_mask, q->node);
- ret = blk_queue_enter(q, !(gfp_mask & __GFP_DIRECT_RECLAIM) ||
- (op & REQ_NOWAIT));
+ ret = blk_queue_enter(q, flags);
if (ret)
return ERR_PTR(ret);
spin_lock_irq(q->queue_lock);
@@ -2263,8 +2283,10 @@ blk_qc_t generic_make_request(struct bio *bio)
current->bio_list = bio_list_on_stack;
do {
struct request_queue *q = bio->bi_disk->queue;
+ unsigned int flags = bio->bi_opf & REQ_NOWAIT ?
+ BLK_MQ_REQ_NOWAIT : 0;
- if (likely(blk_queue_enter(q, bio->bi_opf & REQ_NOWAIT) == 0)) {
+ if (likely(blk_queue_enter(q, flags) == 0)) {
struct bio_list lower, same;
/* Create a fresh bio_list for all subordinate requests */
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 271657992d1a..1604bc2d4a57 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -386,7 +386,7 @@ struct request *blk_mq_alloc_request(struct request_queue *q, unsigned int op,
struct request *rq;
int ret;
- ret = blk_queue_enter(q, flags & BLK_MQ_REQ_NOWAIT);
+ ret = blk_queue_enter(q, flags);
if (ret)
return ERR_PTR(ret);
@@ -425,7 +425,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q,
if (hctx_idx >= q->nr_hw_queues)
return ERR_PTR(-EIO);
- ret = blk_queue_enter(q, true);
+ ret = blk_queue_enter(q, flags);
if (ret)
return ERR_PTR(ret);
diff --git a/block/blk-timeout.c b/block/blk-timeout.c
index 17ec83bb0900..b75d975cc5a5 100644
--- a/block/blk-timeout.c
+++ b/block/blk-timeout.c
@@ -134,7 +134,7 @@ void blk_timeout_work(struct work_struct *work)
struct request *rq, *tmp;
int next_set = 0;
- if (blk_queue_enter(q, true))
+ if (blk_queue_enter(q, BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_PREEMPT))
return;
spin_lock_irqsave(q->queue_lock, flags);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 1c16a247fae6..0ba7af5debc7 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2926,21 +2926,30 @@ static void scsi_wait_for_queuecommand(struct scsi_device *sdev)
int
scsi_device_quiesce(struct scsi_device *sdev)
{
+ struct request_queue *q = sdev->request_queue;
int err;
+ blk_mq_freeze_queue(q);
+ if (blk_set_preempt_only(q)) {
+ blk_mq_unfreeze_queue(q);
+ return -EINVAL;
+ }
+ /*
+ * Ensure that the effect of blk_set_preempt_only() will be visible
+ * for percpu_ref_tryget() callers that occur after the queue
+ * unfreeze. See also https://lwn.net/Articles/573497/.
+ */
+ synchronize_rcu();
+ blk_mq_unfreeze_queue(q);
+
mutex_lock(&sdev->state_mutex);
err = scsi_device_set_state(sdev, SDEV_QUIESCE);
mutex_unlock(&sdev->state_mutex);
if (err)
- return err;
+ blk_clear_preempt_only(q);
- scsi_run_queue(sdev->request_queue);
- while (atomic_read(&sdev->device_busy)) {
- msleep_interruptible(200);
- scsi_run_queue(sdev->request_queue);
- }
- return 0;
+ return err;
}
EXPORT_SYMBOL(scsi_device_quiesce);
@@ -2961,8 +2970,10 @@ void scsi_device_resume(struct scsi_device *sdev)
*/
mutex_lock(&sdev->state_mutex);
if (sdev->sdev_state == SDEV_QUIESCE &&
- scsi_device_set_state(sdev, SDEV_RUNNING) == 0)
+ scsi_device_set_state(sdev, SDEV_RUNNING) == 0) {
+ blk_clear_preempt_only(sdev->request_queue);
scsi_run_queue(sdev->request_queue);
+ }
mutex_unlock(&sdev->state_mutex);
}
EXPORT_SYMBOL(scsi_device_resume);
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 93d088ffc05c..98cf2d7ee9d3 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -674,7 +674,7 @@ int bdev_read_page(struct block_device *bdev, sector_t sector,
if (!ops->rw_page || bdev_get_integrity(bdev))
return result;
- result = blk_queue_enter(bdev->bd_queue, false);
+ result = blk_queue_enter(bdev->bd_queue, 0);
if (result)
return result;
result = ops->rw_page(bdev, sector + get_start_sect(bdev), page, false);
@@ -710,7 +710,7 @@ int bdev_write_page(struct block_device *bdev, sector_t sector,
if (!ops->rw_page || bdev_get_integrity(bdev))
return -EOPNOTSUPP;
- result = blk_queue_enter(bdev->bd_queue, false);
+ result = blk_queue_enter(bdev->bd_queue, 0);
if (result)
return result;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index c4174f78c6eb..9f21a8bbacc6 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -972,7 +972,7 @@ extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t,
extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t,
struct scsi_ioctl_command __user *);
-extern int blk_queue_enter(struct request_queue *q, bool nowait);
+extern int blk_queue_enter(struct request_queue *q, unsigned int flags);
extern void blk_queue_exit(struct request_queue *q);
extern void blk_start_queue(struct request_queue *q);
extern void blk_start_queue_async(struct request_queue *q);
--
2.14.2
^ permalink raw reply related
* [PATCH v6 8/9] block: Add the QUEUE_FLAG_PREEMPT_ONLY request queue flag
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Hannes Reinecke, Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
This flag will be used in the next patch to let the block layer
core know whether or not a SCSI request queue has been quiesced.
A quiesced SCSI queue namely only processes RQF_PREEMPT requests.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
block/blk-core.c | 29 +++++++++++++++++++++++++++++
include/linux/blkdev.h | 6 ++++++
2 files changed, 35 insertions(+)
diff --git a/block/blk-core.c b/block/blk-core.c
index 842e67e0b7ab..b8d90fc29b35 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -346,6 +346,35 @@ void blk_sync_queue(struct request_queue *q)
}
EXPORT_SYMBOL(blk_sync_queue);
+/**
+ * blk_set_preempt_only - set QUEUE_FLAG_PREEMPT_ONLY.
+ *
+ * Returns the previous value of the PREEMPT_ONLY flag - 0 if the flag was not
+ * set and 1 if the flag was already set.
+ */
+int blk_set_preempt_only(struct request_queue *q)
+{
+ unsigned long flags;
+ int res;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ res = queue_flag_test_and_set(QUEUE_FLAG_PREEMPT_ONLY, q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(blk_set_preempt_only);
+
+void blk_clear_preempt_only(struct request_queue *q)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ queue_flag_clear(QUEUE_FLAG_PREEMPT_ONLY, q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+}
+EXPORT_SYMBOL_GPL(blk_clear_preempt_only);
+
/**
* __blk_run_queue_uncond - run a queue whether or not it has been stopped
* @q: The queue to run
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 9a85f6ad4f89..c4174f78c6eb 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -631,6 +631,7 @@ struct request_queue {
#define QUEUE_FLAG_REGISTERED 26 /* queue has been registered to a disk */
#define QUEUE_FLAG_SCSI_PASSTHROUGH 27 /* queue supports SCSI commands */
#define QUEUE_FLAG_QUIESCED 28 /* queue has been quiesced */
+#define QUEUE_FLAG_PREEMPT_ONLY 29 /* only process REQ_PREEMPT requests */
#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_STACKABLE) | \
@@ -735,6 +736,11 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q)
((rq)->cmd_flags & (REQ_FAILFAST_DEV|REQ_FAILFAST_TRANSPORT| \
REQ_FAILFAST_DRIVER))
#define blk_queue_quiesced(q) test_bit(QUEUE_FLAG_QUIESCED, &(q)->queue_flags)
+#define blk_queue_preempt_only(q) \
+ test_bit(QUEUE_FLAG_PREEMPT_ONLY, &(q)->queue_flags)
+
+extern int blk_set_preempt_only(struct request_queue *q);
+extern void blk_clear_preempt_only(struct request_queue *q);
static inline bool blk_account_rq(struct request *rq)
{
--
2.14.2
^ permalink raw reply related
* [PATCH v6 7/9] ide, scsi: Tell the block layer at request allocation time about preempt requests
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, David S . Miller, Hannes Reinecke,
Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
Convert blk_get_request(q, op, __GFP_RECLAIM) into blk_get_request(q,
op, BLK_MQ_PREEMPT). This patch does not change any functionality.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
drivers/ide/ide-pm.c | 4 ++--
drivers/scsi/scsi_lib.c | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c
index 544f02d673ca..f56d742908df 100644
--- a/drivers/ide/ide-pm.c
+++ b/drivers/ide/ide-pm.c
@@ -89,9 +89,9 @@ int generic_ide_resume(struct device *dev)
}
memset(&rqpm, 0, sizeof(rqpm));
- rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, __GFP_RECLAIM);
+ rq = blk_get_request_flags(drive->queue, REQ_OP_DRV_IN,
+ BLK_MQ_REQ_PREEMPT);
ide_req(rq)->type = ATA_PRIV_PM_RESUME;
- rq->rq_flags |= RQF_PREEMPT;
rq->special = &rqpm;
rqpm.pm_step = IDE_PM_START_RESUME;
rqpm.pm_state = PM_EVENT_ON;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 9cf6a80fe297..1c16a247fae6 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -252,9 +252,9 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
struct scsi_request *rq;
int ret = DRIVER_ERROR << 24;
- req = blk_get_request(sdev->request_queue,
+ req = blk_get_request_flags(sdev->request_queue,
data_direction == DMA_TO_DEVICE ?
- REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, __GFP_RECLAIM);
+ REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, BLK_MQ_REQ_PREEMPT);
if (IS_ERR(req))
return ret;
rq = scsi_req(req);
@@ -268,7 +268,7 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
rq->retries = retries;
req->timeout = timeout;
req->cmd_flags |= flags;
- req->rq_flags |= rq_flags | RQF_QUIET | RQF_PREEMPT;
+ req->rq_flags |= rq_flags | RQF_QUIET;
/*
* head injection *required* here otherwise quiesce won't work
--
2.14.2
^ permalink raw reply related
* [PATCH v6 1/9] md: Rename md_notifier into md_reboot_notifier
From: Bart Van Assche @ 2017-10-05 0:01 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Martin K . Petersen,
=Oleksandr Natalenko, Luis R . Rodriguez, Ming Lei,
Bart Van Assche, Shaohua Li, linux-raid, Hannes Reinecke,
Johannes Thumshirn
In-Reply-To: <20171005000110.15904-1-bart.vanassche@wdc.com>
This avoids confusion with the pm notifier that will be added
through a later patch.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Shaohua Li <shli@kernel.org>
Cc: linux-raid@vger.kernel.org
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Johannes Thumshirn <jthumshirn@suse.de>
---
drivers/md/md.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 5d61049e7417..8933cafc212d 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -8966,7 +8966,7 @@ static int md_notify_reboot(struct notifier_block *this,
return NOTIFY_DONE;
}
-static struct notifier_block md_notifier = {
+static struct notifier_block md_reboot_notifier = {
.notifier_call = md_notify_reboot,
.next = NULL,
.priority = INT_MAX, /* before any real devices */
@@ -9003,7 +9003,7 @@ static int __init md_init(void)
blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,
md_probe, NULL, NULL);
- register_reboot_notifier(&md_notifier);
+ register_reboot_notifier(&md_reboot_notifier);
raid_table_header = register_sysctl_table(raid_root_table);
md_geninit();
@@ -9243,7 +9243,7 @@ static __exit void md_exit(void)
unregister_blkdev(MD_MAJOR,"md");
unregister_blkdev(mdp_major, "mdp");
- unregister_reboot_notifier(&md_notifier);
+ unregister_reboot_notifier(&md_reboot_notifier);
unregister_sysctl_table(raid_table_header);
/* We cannot unload the modules while some process is
--
2.14.2
^ permalink raw reply related
* Re: [PATCH v3 1/1] go-runtime: prevent host leakage into target objects
From: Saul Wold @ 2017-10-05 0:02 UTC (permalink / raw)
To: Matt Madison, openembedded-core; +Cc: otavio
In-Reply-To: <1507141312-10348-2-git-send-email-matt@madison.systems>
On Wed, 2017-10-04 at 18:21 +0000, Matt Madison wrote:
> When building for a target whose architecture matches
> the build host's, the second pass through make.bash
> to build the shareable runtime would also overwrite
> the target's static cgo library with host-compatibile
> binaries.
>
> Fix this by running the host-side build once and
> target-only passes of make.bash twice, for static
> and shareable. This ensures that what gets installed
> is target-compatible.
>
> Also fix an issue with x86-64 targets running MUSL by
> removing the pre-built (for glibc) objects for the
> race detector runtime before building.
>
> [YOCTO #12136]
>
> Signed-off-by: Matt Madison <matt@madison.systems>
Thanks Matt!
Acked-by: Saul Wold <sgw@linux.intel.com>
Tested-by: Saul Wold <sgw@linux.intel.com>
> ---
> meta/recipes-devtools/go/go-runtime.inc | 19 +++++++++++++++----
> 1 file changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/meta/recipes-devtools/go/go-runtime.inc b/meta/recipes-
> devtools/go/go-runtime.inc
> index 934d1aa..f181dc7 100644
> --- a/meta/recipes-devtools/go/go-runtime.inc
> +++ b/meta/recipes-devtools/go/go-runtime.inc
> @@ -15,7 +15,13 @@ export CC_FOR_TARGET="${CC}"
> export CXX_FOR_TARGET="${CXX}"
> export GOROOT_OVERRIDE = "1"
>
> -do_configure[noexec] = "1"
> +do_configure() {
> + :
> +}
> +
> +do_configure_libc-musl() {
> + rm -f ${S}/src/runtime/race/*.syso
> +}
>
> do_compile() {
> export GOBIN="${B}/bin"
> @@ -23,9 +29,13 @@ do_compile() {
> rm -rf ${GOBIN} ${B}/pkg
> mkdir ${GOBIN}
> cd src
> - GO_FLAGS="" ./make.bash
> + ./make.bash --host-only
> + cp ${B}/pkg/tool/${BUILD_GOTUPLE}/go_bootstrap ${B}
> + rm -rf ${B}/pkg/${TARGET_GOTUPLE}
> + ./make.bash --target-only
> if [ -n "${GO_DYNLINK}" ]; then
> - GO_FLAGS="-buildmode=shared" GO_LDFLAGS="-extldflags
> \"${LDFLAGS}\"" ./make.bash
> + cp ${B}/go_bootstrap ${B}/pkg/tool/${BUILD_GOTUPLE}
> + GO_FLAGS="-buildmode=shared" GO_LDFLAGS="-extldflags
> \"${LDFLAGS}\"" ./make.bash --target-only
> fi
> cd ${B}
> }
> @@ -41,8 +51,9 @@ do_install() {
> rm -rf ${D}${libdir}/go/pkg/obj
> rm -rf ${D}${libdir}/go/pkg/bootstrap
> find src -mindepth 1 -maxdepth 1 -type d | while read
> srcdir; do
> - [ "$srcdir" = "./cmd" ] || cp --
> preserve=mode,timestamps -R $srcdir ${D}${libdir}/go/src/
> + cp --preserve=mode,timestamps -R $srcdir
> ${D}${libdir}/go/src/
> done
> + rm -f ${D}${libdir}/go/src/cmd/dist/dist
> }
>
> # Remove test binaries that cannot be relocated
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH v3 5/8] platform/x86: dell-wmi-smbios: introduce character device for userspace
From: Darren Hart @ 2017-10-05 0:02 UTC (permalink / raw)
To: Mario.Limonciello
Cc: greg, andy.shevchenko, linux-kernel, platform-driver-x86, luto,
quasisec, pali.rohar
In-Reply-To: <5cbdbc85d3e14880a187dfb8f76b1745@ausx13mpc120.AMER.DELL.COM>
On Tue, Oct 03, 2017 at 03:49:04PM +0000, Mario.Limonciello@dell.com wrote:
> > -----Original Message-----
> > From: Darren Hart [mailto:dvhart@infradead.org]
> > Sent: Tuesday, October 3, 2017 10:20 AM
> > To: Greg KH <greg@kroah.com>
> > Cc: Limonciello, Mario <Mario_Limonciello@Dell.com>; Andy Shevchenko
> > <andy.shevchenko@gmail.com>; LKML <linux-kernel@vger.kernel.org>;
> > platform-driver-x86@vger.kernel.org; Andy Lutomirski <luto@kernel.org>;
> > quasisec@google.com; pali.rohar@gmail.com
> > Subject: Re: [PATCH v3 5/8] platform/x86: dell-wmi-smbios: introduce character
> > device for userspace
> >
> > On Tue, Oct 03, 2017 at 11:26:11AM +0200, Greg KH wrote:
> > > On Wed, Sep 27, 2017 at 11:02:17PM -0500, Mario Limonciello wrote:
> > > > +static int dell_wmi_smbios_open(struct inode *inode, struct file *file)
> > > > +{
> > > > + return nonseekable_open(inode, file);
> > > > +}
> > > > +
> > > > +static int dell_wmi_smbios_release(struct inode *inode, struct file *file)
> > > > +{
> > > > + return 0;
> > > > +}
> > >
> > > Why even declare an open/release if you don't do anything with them?
> > > Just leave them empty.
> > >
> > > > +static long dell_wmi_smbios_ioctl(struct file *filp, unsigned int cmd,
> > > > + unsigned long arg)
> > > > +{
> > > > + void __user *p = (void __user *) arg;
> > > > + size_t size;
> > > > + int ret = 0;
> > > > +
> > > > + if (_IOC_TYPE(cmd) != DELL_WMI_SMBIOS_IOC)
> > > > + return -ENOTTY;
> > > > +
> > > > + switch (cmd) {
> > > > + case DELL_WMI_SMBIOS_CALL_CMD:
> > > > + size = sizeof(struct wmi_calling_interface_buffer);
> > > > + mutex_lock(&buffer_mutex);
> > > > + if (copy_from_user(devfs_buffer, p, size)) {
> > > > + ret = -EFAULT;
> > > > + goto fail_smbios_cmd;
> > > > + }
> > > > + ret = run_wmi_smbios_call(devfs_buffer);
> > >
> > > You _are_ checking that your structures are valid here, right? I didn't
> > > see you really were, so I'll let you go audit your code paths for the
> > > next set of patches.
> > >
> > > But really, ugh, this seems horrible. It's a huge vague data blob going
> > > both ways, this feels ripe for abuse and other bad things. Do you have
> > > a working userspace implementation for all of this to publish at the
> > > same time?
> > >
> >
> > This is the general challenge with WMI support, as it was designed to
> > provide userspace with a way to talk to the firmware.
> >
> > For the more general case going forward the intent is to perform the MOF
> > parsing in the kernel, which will make it possible to do some automated
> > buffer validation.
> >
> > This particular driver is an intermediate step improving on the
> > mechanism used for existing devices (libsmbios -> dcdbas -> SMM) to use
> > the safer WMI entry point (using an op region instead of passing a
> > physical memory pointer to SMM).
> >
> > But, that said... Mario, in lieu of the MOF description of the buffer,
> > we should be able to do some driver specific validation of this buffer
> > here.
> >
>
> As the WMI interface exists today even the MOF doesn't describe the
> Content of the buffer either. It's a buffer passed from userspace to BIOS and
> back. I described it as the struct, but I'm not really sure how that can
> be further validated by the Linux kernel without a checksum.
> This is no different than how dell-smbios and dcdbas operates.
The notation about equivalence with the dell-smbios and dcdbas
implementation is a valid one. If all we do is move this to a WMI
managed access to SMBIOS and move away from the SMI based one, this is
still an improvement for difference in how memory is passed around as
described above.
I don't think a checksum will help with the concerns Greg is raising. A
userspace generated checksum only tells the kernel that what is in the
buffer is what userpsace intended. Similarly for the buffer coming back
from SMM.
Generally speaking, the kernel would want to validate data passing to
and from userspace and firmware - which WMI was designed to circumvent.
Mario:
Are there no specific bits that can be sanity checked? No reserved bits
in the buffer format? No Command Codes with a known range of values?
> New interfaces will have description that is parsable by MOF.
And to elaborate here. The above is about an improvement to the
dell-smbios driver for existing platforms which do not have available
MOF data. For platforms that do provide MOF data, we're working toward
moving the MOF parser into the kernel (rather than leaving it in
userspace as it was designed to be) which will afford the kernel more
oversight of these messages.
This series is an incremental improvement until the above is ready - but
it does lay out some of the ground work which we want to get agreement
on, mostly from patch 4 of 8 in the series:
1) The char dev IOCTL model for calling WMI methods
2) The /dev/wmi/<driver> path for the char dev
3) The function ops approach for WMI drivers to request the char dev
from the WMI bus
--
Darren Hart
VMware Open Source Technology Center
^ permalink raw reply
* [PATCHv2 net-next] openvswitch: Add erspan tunnel support.
From: William Tu @ 2017-10-05 0:03 UTC (permalink / raw)
To: netdev; +Cc: Pravin B Shelar
Add erspan netlink interface for OVS.
Signed-off-by: William Tu <u9012063@gmail.com>
Cc: Pravin B Shelar <pshelar@ovn.org>
---
v1->v2: remove unnecessary compat code.
---
include/uapi/linux/openvswitch.h | 1 +
net/openvswitch/flow_netlink.c | 51 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 156ee4cab82e..efdbfbfd3ee2 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -359,6 +359,7 @@ enum ovs_tunnel_key_attr {
OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */
OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */
OVS_TUNNEL_KEY_ATTR_PAD,
+ OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, /* be32 ERSPAN index. */
__OVS_TUNNEL_KEY_ATTR_MAX
};
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index e8eb427ce6d1..fc0ca9a89b8e 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -48,6 +48,7 @@
#include <net/ndisc.h>
#include <net/mpls.h>
#include <net/vxlan.h>
+#include <net/erspan.h>
#include "flow_netlink.h"
@@ -319,7 +320,8 @@ size_t ovs_tun_key_attr_size(void)
* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it.
*/
+ nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */
- + nla_total_size(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */
+ + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_DST */
+ + nla_total_size(4); /* OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS */
}
size_t ovs_key_attr_size(void)
@@ -371,6 +373,7 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
.next = ovs_vxlan_ext_key_lens },
[OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = sizeof(struct in6_addr) },
[OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = sizeof(struct in6_addr) },
+ [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = sizeof(u32) },
};
/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
@@ -593,6 +596,33 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
return 0;
}
+static int erspan_tun_opt_from_nlattr(const struct nlattr *attr,
+ struct sw_flow_match *match, bool is_mask,
+ bool log)
+{
+ unsigned long opt_key_offset;
+ struct erspan_metadata opts;
+
+ BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts));
+
+ memset(&opts, 0, sizeof(opts));
+ opts.index = nla_get_be32(attr);
+
+ /* Index has only 20-bit */
+ if (ntohl(opts.index) & ~INDEX_MASK) {
+ OVS_NLERR(log, "ERSPAN index number %x too large.",
+ ntohl(opts.index));
+ return -EINVAL;
+ }
+
+ SW_FLOW_KEY_PUT(match, tun_opts_len, sizeof(opts), is_mask);
+ opt_key_offset = TUN_METADATA_OFFSET(sizeof(opts));
+ SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, &opts, sizeof(opts),
+ is_mask);
+
+ return 0;
+}
+
static int ip_tun_from_nlattr(const struct nlattr *attr,
struct sw_flow_match *match, bool is_mask,
bool log)
@@ -700,6 +730,19 @@ static int ip_tun_from_nlattr(const struct nlattr *attr,
break;
case OVS_TUNNEL_KEY_ATTR_PAD:
break;
+ case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS:
+ if (opts_type) {
+ OVS_NLERR(log, "Multiple metadata blocks provided");
+ return -EINVAL;
+ }
+
+ err = erspan_tun_opt_from_nlattr(a, match, is_mask, log);
+ if (err)
+ return err;
+
+ tun_flags |= TUNNEL_ERSPAN_OPT;
+ opts_type = type;
+ break;
default:
OVS_NLERR(log, "Unknown IP tunnel attribute %d",
type);
@@ -824,6 +867,10 @@ static int __ip_tun_to_nlattr(struct sk_buff *skb,
else if (output->tun_flags & TUNNEL_VXLAN_OPT &&
vxlan_opt_to_nlattr(skb, tun_opts, swkey_tun_opts_len))
return -EMSGSIZE;
+ else if (output->tun_flags & TUNNEL_ERSPAN_OPT &&
+ nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,
+ ((struct erspan_metadata *)tun_opts)->index))
+ return -EMSGSIZE;
}
return 0;
@@ -2195,6 +2242,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
break;
case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS:
break;
+ case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS:
+ break;
}
};
--
2.7.4
^ permalink raw reply related
* [PATCH] powerpc: Drop lockdep_assert_cpus_held call from arch_update_cpu_topology
From: Thiago Jung Bauermann @ 2017-10-05 0:04 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kernel, Thomas Gleixner, Michael Ellerman, Daniel Black,
Thiago Jung Bauermann
It turns out that not all paths calling arch_update_cpu_topology hold
cpu_hotplug_lock, but that's ok because those paths aren't supposed to race
with any concurrent hotplug events.
Callers of arch_update_cpu_topology are expected to know what they are
doing when they call the function without holding the lock, so remove the
lockdep warning.
Here are two reported splats that turned out to be harmless (as far as
I know, at least):
[ 255.654622] ------------[ cut here ]------------
[ 255.654658] WARNING: CPU: 1 PID: 14 at kernel/cpu.c:240 lockdep_assert_cpus_held+0x54/0x80
[ 255.654661] Modules linked in: bridge stp llc binfmt_misc kvm_hv kvm leds_powernv vmx_crypto powernv_rng rng_core led_class powernv_op_panel autofs4 xfs btrfs lzo_compress raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c multipath mlx4_en raid10 crc32c_vpmsum lpfc be2net crc_t10dif crct10dif_generic crct10dif_common mlx4_core
[ 255.654734] CPU: 1 PID: 14 Comm: cpuhp/1 Tainted: G W 4.13.0 #1
[ 255.654737] task: c000001ff25fa100 task.stack: c000001ff2578000
[ 255.654740] NIP: c0000000000f8624 LR: c0000000000f8618 CTR: c0000000001763e0
[ 255.654742] REGS: c000001ff257b780 TRAP: 0700 Tainted: G W (4.13.0)
[ 255.654744] MSR: 9000000000029033 <SF,HV,EE,ME,IR,DR,RI,LE>
[ 255.654764] CR: 24200422 XER: 00000000
[ 255.654766] CFAR: c0000000001783d0 SOFTE: 1
GPR00: c0000000000f8618 c000001ff257ba00 c000000001042100 0000000000000000
GPR04: ffffffffffffffff 0000000000000001 0000000000000000 0000000000000000
GPR08: 0000001ffe490000 0000000000000000 0000000000000000 c000007fee782f50
GPR12: 0000000000000000 c00000000fd80500 c00000000012c878 c000001ff6209180
GPR16: 0000000000000000 0000000000000000 c000000000ef2728 0000000000000000
GPR20: 0000000000000000 0000000000000000 0000000000000001 0000000000000000
GPR24: 0000000000000000 c000000001087d50 c000000000fe0e7d c0000000001448c0
GPR28: 0000001ffe490000 0000000000000000 0000000000000002 c000000001088050
[ 255.654842] NIP [c0000000000f8624] lockdep_assert_cpus_held+0x54/0x80
[ 255.654845] LR [c0000000000f8618] lockdep_assert_cpus_held+0x48/0x80
[ 255.654847] Call Trace:
[ 255.654851] [c000001ff257ba00] [c0000000000f8618] lockdep_assert_cpus_held+0x48/0x80 (unreliable)
[ 255.654858] [c000001ff257ba20] [c00000000007a848] arch_update_cpu_topology+0x18/0x30
[ 255.654864] [c000001ff257ba40] [c00000000016bd0c] partition_sched_domains+0x8c/0x4e0
[ 255.654870] [c000001ff257baf0] [c00000000020a914] cpuset_update_active_cpus+0x24/0x60
[ 255.654876] [c000001ff257bb10] [c0000000001449bc] sched_cpu_deactivate+0xfc/0x1a0
[ 255.654882] [c000001ff257bc20] [c0000000000f5a8c] cpuhp_invoke_callback+0x19c/0xe00
[ 255.654888] [c000001ff257bcb0] [c0000000000f6868] cpuhp_down_callbacks+0x78/0xf0
[ 255.654893] [c000001ff257bd00] [c0000000000f6c1c] cpuhp_thread_fun+0x1fc/0x210
[ 255.654899] [c000001ff257bd50] [c000000000132f4c] smpboot_thread_fn+0x2fc/0x370
[ 255.654905] [c000001ff257bdc0] [c00000000012ca24] kthread+0x1b4/0x1c0
[ 255.654911] [c000001ff257be30] [c00000000000bcec] ret_from_kernel_thread+0x5c/0x70
[ 255.654914] Instruction dump:
[ 255.654919] 4e800020 60000000 60420000 7c0802a6 3c62ffeb 3880ffff 38639280 f8010030
[ 255.654934] 4807fd45 60000000 2fa30000 409e0020 <0fe00000> e8010030 38210020 7c0803a6
[ 255.654950] ---[ end trace 06efa323f571f14b ]---
[ 255.894356] ------------[ cut here ]------------
and:
[ 2.862745] ------------[ cut here ]------------
[ 2.862772] WARNING: CPU: 0 PID: 1 at kernel/cpu.c:240 lockdep_assert_cpus_held+0x5c/0x70
[ 2.862784] Modules linked in:
[ 2.862819] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.13.0-00083-gb38923a #1
[ 2.862835] task: c000003ff3280000 task.stack: c000003ff6104000
[ 2.862847] NIP: c00000000010ac7c LR: c00000000010ac70 CTR: 0000000000000000
[ 2.862862] REGS: c000003ff61078d0 TRAP: 0700 Not tainted (4.13.0-00083-gb38923a)
[ 2.862874] MSR: 9000000002029033 <SF,HV,VEC,EE,ME,IR,DR,RI,LE> CR: 24000022 XER: 00000000
[ 2.863046] CFAR: c000000000190bbc SOFTE: 1
GPR00: c00000000010ac70 c000003ff6107b50 c000000001150500 0000000000000000
GPR04: c000000000fdbe60 0000000000000000 c00000000123c5f8 0000000000000000
GPR08: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR12: 0000000024000082 c00000000fd40000 c00000000000dc08 0000000000000000
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR24: 0000000000000000 c000000000eb392c 0000000000000000 c000000000f2bdb0
GPR28: 0000000000000000 0000000000000000 c000000001199050 c00000000123c4f8
[ 2.863597] NIP [c00000000010ac7c] lockdep_assert_cpus_held+0x5c/0x70
[ 2.863612] LR [c00000000010ac70] lockdep_assert_cpus_held+0x50/0x70
[ 2.863627] Call Trace:
[ 2.863642] [c000003ff6107b50] [c00000000010ac70] lockdep_assert_cpus_held+0x50/0x70 (unreliable)
[ 2.863692] [c000003ff6107b70] [c0000000000802c0] arch_update_cpu_topology+0x20/0x40
[ 2.863724] [c000003ff6107b90] [c000000000182ec4] sched_init_domains+0x74/0x100
[ 2.863752] [c000003ff6107bd0] [c000000000ed5c78] sched_init_smp+0x58/0x168
[ 2.863783] [c000003ff6107d00] [c000000000eb460c] kernel_init_freeable+0x1fc/0x3ac
[ 2.863818] [c000003ff6107dc0] [c00000000000dc2c] kernel_init+0x2c/0x150
[ 2.863852] [c000003ff6107e30] [c00000000000bcec] ret_from_kernel_thread+0x5c/0x70
[ 2.863886] Instruction dump:
[ 2.863915] 409e0014 38210020 e8010010 7c0803a6 4e800020 3c62ffe9 3880ffff 3863b960
[ 2.864036] 48085e3d 60000000 2fa30000 409effd8 <0fe00000> 38210020 e8010010 7c0803a6
[ 2.864172] ---[ end trace 240e34251693e732 ]---
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Fixes: 3e401f7a2e51 ("powerpc: Only obtain cpu_hotplug_lock if called by rtasd")
Link: https://lists.ozlabs.org/pipermail/linuxppc-dev/2017-September/163244.html
Link: https://github.com/linuxppc/linux/issues/106
---
arch/powerpc/mm/numa.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index b95c584ce19d..a51df9ef529d 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1438,7 +1438,6 @@ int numa_update_cpu_topology(bool cpus_locked)
int arch_update_cpu_topology(void)
{
- lockdep_assert_cpus_held();
return numa_update_cpu_topology(true);
}
^ permalink raw reply related
* [PATCH v8 1/2] media:imx274 device tree binding file
From: Leon Luo @ 2017-10-05 0:06 UTC (permalink / raw)
To: mchehab, robh+dt, mark.rutland, hans.verkuil, sakari.ailus
Cc: linux-media, devicetree, linux-kernel, leonl, soren.brinkmann
The binding file for imx274 CMOS sensor V4l2 driver
Signed-off-by: Leon Luo <leonl@leopardimaging.com>
Acked-by: Sören Brinkmann <soren.brinkmann@xilinx.com>
Acked-by: Rob Herring <robh@kernel.org>
---
v8:
- no changes
v7:
- no changes
v6:
- no changes
v5:
- add 'port' and 'endpoint' information
v4:
- no changes
v3:
- remove redundant properties and references
- document 'reg' property
v2:
- no changes
---
.../devicetree/bindings/media/i2c/imx274.txt | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/imx274.txt
diff --git a/Documentation/devicetree/bindings/media/i2c/imx274.txt b/Documentation/devicetree/bindings/media/i2c/imx274.txt
new file mode 100644
index 000000000000..80f2e89568e1
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/imx274.txt
@@ -0,0 +1,33 @@
+* Sony 1/2.5-Inch 8.51Mp CMOS Digital Image Sensor
+
+The Sony imx274 is a 1/2.5-inch CMOS active pixel digital image sensor with
+an active array size of 3864H x 2202V. It is programmable through I2C
+interface. The I2C address is fixed to 0x1a as per sensor data sheet.
+Image data is sent through MIPI CSI-2, which is configured as 4 lanes
+at 1440 Mbps.
+
+
+Required Properties:
+- compatible: value should be "sony,imx274" for imx274 sensor
+- reg: I2C bus address of the device
+
+Optional Properties:
+- reset-gpios: Sensor reset GPIO
+
+The imx274 device node should contain one 'port' child node with
+an 'endpoint' subnode. For further reading on port node refer to
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+ sensor@1a {
+ compatible = "sony,imx274";
+ reg = <0x1a>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reset-gpios = <&gpio_sensor 0 0>;
+ port {
+ sensor_out: endpoint {
+ remote-endpoint = <&csiss_in>;
+ };
+ };
+ };
--
2.14.0.rc1
^ permalink raw reply related
* [PATCH v8 2/2] media:imx274 V4l2 driver for Sony imx274 CMOS sensor
From: Leon Luo @ 2017-10-05 0:06 UTC (permalink / raw)
To: mchehab, robh+dt, mark.rutland, hans.verkuil, sakari.ailus
Cc: linux-media, devicetree, linux-kernel, leonl, soren.brinkmann
In-Reply-To: <20171005000621.27841-1-leonl@leopardimaging.com>
The imx274 is a Sony CMOS image sensor that has 1/2.5 image size.
It supports up to 3840x2160 (4K) 60fps, 1080p 120fps. The interface
is 4-lane MIPI CSI-2 running at 1.44Gbps each.
This driver has been tested on Xilinx ZCU102 platform with a Leopard
LI-IMX274MIPI-FMC camera board.
Support for the following features:
-Resolutions: 3840x2160, 1920x1080, 1280x720
-Frame rate: 3840x2160 : 5 – 60fps
1920x1080 : 5 – 120fps
1280x720 : 5 – 120fps
-Exposure time: 16 – (frame interval) micro-seconds
-Gain: 1x - 180x
-VFLIP: enable/disabledrivers/media/i2c/imx274.c
-Test pattern: 12 test patterns
Signed-off-by: Leon Luo <leonl@leopardimaging.com>
Tested-by: Sören Brinkmann <soren.brinkmann@xilinx.com>
Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
---
v8:
- check division by zero error
v7:
- use __v4l2_ctrl_s_ctrl instead of v4l2_ctrl_s_ctrl to have
clean mutex_lock/mutex_unlock in imx274_s_stream()
- define imx274_tp_regs[] as static, move the test pattern reg
out of imx274_tp_regs[], and define it as a macro
IMX274_TEST_PATTERN_REG
v6:
- remove media/v4l2-image-sizes.h from include header
- make the header file alphabetical order
- remove fmt->pad check in imx274_get_fmt,
the V4L2 subdev framework does it already
- change 'struct reg_8 *regs' to 'struct reg_8 regs[n]',
where n is the exact numbers needed for this function
- move MODULE_DEVICE_TABLE(of, imx274_of_id_table); closer
to imx274_of_id_table definition
- remove return check of imx274_write_table in imx274_remove,
because it should remove all resources even i2c fails here
- move imx274_load_default before v4l2_async_register_subdev
v5:
- no changes
v4:
- use 32-bit data type to avoid __divdi3 compile error for i386
- clean up OR together error codesdrivers/media/i2c/imx274.c
v3:
- clean up header files
- use struct directly instead of alias #define
- use v4l2_ctrl_s_ctrl instead of v4l2_s_ctrl
- revise debug output
- move static helpers closer to their call site
- don't OR toegether error codes
- use closest valid gain value instead of erroring out
- assigne lock to the control handler and omit explicit locking
- acquire mutex lock for imx274_get_fmt
- remove format->pad check in imx274_set_fmt since the pad is always 0
- pass struct v4l2_ctrl pointer in gain/exposure/vlip/test pattern controls
- remove priv->ctrls.vflip->val = val in imx274_set_vflip()
- remove priv->ctrls.test_pattern->val = val in imx274_set_test_pattern()
- remove empty open/close callbacks
- remove empty core ops
- add more error labels in probe
- use v4l2_async_unregister_subdev instead of v4l2_device_unregister_subdev
- use dynamic debug
- split start_stream to two steps: imx274_mode_regs() and imx274_start_stream()
frame rate & exposure can be updated
between imx274_mode_regs() & imx274_start_stream()
v2:
- Fix Kconfig to not remove existing options
---
drivers/media/i2c/Kconfig | 7 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/imx274.c | 1811 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1819 insertions(+)
create mode 100644 drivers/media/i2c/imx274.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 47113774a297..9659849e33a0 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -555,6 +555,13 @@ config VIDEO_APTINA_PLL
config VIDEO_SMIAPP_PLL
tristate
+config VIDEO_IMX274
+ tristate "Sony IMX274 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for the Sony IMX274
+ CMOS image sensor.
+
config VIDEO_OV2640
tristate "OmniVision OV2640 sensor support"
depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index c843c181dfb9..f8d57e453936 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -92,5 +92,6 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
+obj-$(CONFIG_VIDEO_IMX274) += imx274.o
obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
new file mode 100644
index 000000000000..ab6a5f31da74
--- /dev/null
+++ b/drivers/media/i2c/imx274.c
@@ -0,0 +1,1811 @@
+/*
+ * imx274.c - IMX274 CMOS Image Sensor driver
+ *
+ * Copyright (C) 2017, Leopard Imaging, Inc.
+ *
+ * Leon Luo <leonl@leopardimaging.com>
+ * Edwin Zou <edwinz@leopardimaging.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * See "SHR, SVR Setting" in datasheet
+ */
+#define IMX274_DEFAULT_FRAME_LENGTH (4550)
+#define IMX274_MAX_FRAME_LENGTH (0x000fffff)
+
+/*
+ * See "Frame Rate Adjustment" in datasheet
+ */
+#define IMX274_PIXCLK_CONST1 (72000000)
+#define IMX274_PIXCLK_CONST2 (1000000)
+
+/*
+ * The input gain is shifted by IMX274_GAIN_SHIFT to get
+ * decimal number. The real gain is
+ * (float)input_gain_value / (1 << IMX274_GAIN_SHIFT)
+ */
+#define IMX274_GAIN_SHIFT (8)
+#define IMX274_GAIN_SHIFT_MASK ((1 << IMX274_GAIN_SHIFT) - 1)
+
+/*
+ * See "Analog Gain" and "Digital Gain" in datasheet
+ * min gain is 1X
+ * max gain is calculated based on IMX274_GAIN_REG_MAX
+ */
+#define IMX274_GAIN_REG_MAX (1957)
+#define IMX274_MIN_GAIN (0x01 << IMX274_GAIN_SHIFT)
+#define IMX274_MAX_ANALOG_GAIN ((2048 << IMX274_GAIN_SHIFT)\
+ / (2048 - IMX274_GAIN_REG_MAX))
+#define IMX274_MAX_DIGITAL_GAIN (8)
+#define IMX274_DEF_GAIN (20 << IMX274_GAIN_SHIFT)
+#define IMX274_GAIN_CONST (2048) /* for gain formula */
+
+/*
+ * 1 line time in us = (HMAX / 72), minimal is 4 lines
+ */
+#define IMX274_MIN_EXPOSURE_TIME (4 * 260 / 72)
+
+#define IMX274_DEFAULT_MODE IMX274_MODE_3840X2160
+#define IMX274_MAX_WIDTH (3840)
+#define IMX274_MAX_HEIGHT (2160)
+#define IMX274_MAX_FRAME_RATE (120)
+#define IMX274_MIN_FRAME_RATE (5)
+#define IMX274_DEF_FRAME_RATE (60)
+
+/*
+ * register SHR is limited to (SVR value + 1) x VMAX value - 4
+ */
+#define IMX274_SHR_LIMIT_CONST (4)
+
+/*
+ * Constants for sensor reset delay
+ */
+#define IMX274_RESET_DELAY1 (2000)
+#define IMX274_RESET_DELAY2 (2200)
+
+/*
+ * shift and mask constants
+ */
+#define IMX274_SHIFT_8_BITS (8)
+#define IMX274_SHIFT_16_BITS (16)
+#define IMX274_MASK_LSB_2_BITS (0x03)
+#define IMX274_MASK_LSB_3_BITS (0x07)
+#define IMX274_MASK_LSB_4_BITS (0x0f)
+#define IMX274_MASK_LSB_8_BITS (0x00ff)
+
+#define DRIVER_NAME "IMX274"
+
+/*
+ * IMX274 register definitions
+ */
+#define IMX274_FRAME_LENGTH_ADDR_1 0x30FA /* VMAX, MSB */
+#define IMX274_FRAME_LENGTH_ADDR_2 0x30F9 /* VMAX */
+#define IMX274_FRAME_LENGTH_ADDR_3 0x30F8 /* VMAX, LSB */
+#define IMX274_SVR_REG_MSB 0x300F /* SVR */
+#define IMX274_SVR_REG_LSB 0x300E /* SVR */
+#define IMX274_HMAX_REG_MSB 0x30F7 /* HMAX */
+#define IMX274_HMAX_REG_LSB 0x30F6 /* HMAX */
+#define IMX274_COARSE_TIME_ADDR_MSB 0x300D /* SHR */
+#define IMX274_COARSE_TIME_ADDR_LSB 0x300C /* SHR */
+#define IMX274_ANALOG_GAIN_ADDR_LSB 0x300A /* ANALOG GAIN LSB */
+#define IMX274_ANALOG_GAIN_ADDR_MSB 0x300B /* ANALOG GAIN MSB */
+#define IMX274_DIGITAL_GAIN_REG 0x3012 /* Digital Gain */
+#define IMX274_VFLIP_REG 0x301A /* VERTICAL FLIP */
+#define IMX274_TEST_PATTERN_REG 0x303D /* TEST PATTERN */
+#define IMX274_STANDBY_REG 0x3000 /* STANDBY */
+
+#define IMX274_TABLE_WAIT_MS 0
+#define IMX274_TABLE_END 1
+
+/*
+ * imx274 I2C operation related structure
+ */
+struct reg_8 {
+ u16 addr;
+ u8 val;
+};
+
+static const struct regmap_config imx274_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+enum imx274_mode {
+ IMX274_MODE_3840X2160,
+ IMX274_MODE_1920X1080,
+ IMX274_MODE_1280X720,
+
+ IMX274_MODE_START_STREAM_1,
+ IMX274_MODE_START_STREAM_2,
+ IMX274_MODE_START_STREAM_3,
+ IMX274_MODE_START_STREAM_4,
+ IMX274_MODE_STOP_STREAM
+};
+
+/*
+ * imx274 format related structure
+ */
+struct imx274_frmfmt {
+ u32 mbus_code;
+ enum v4l2_colorspace colorspace;
+ struct v4l2_frmsize_discrete size;
+ enum imx274_mode mode;
+};
+
+/*
+ * imx274 test pattern related structure
+ */
+enum {
+ TEST_PATTERN_DISABLED = 0,
+ TEST_PATTERN_ALL_000H,
+ TEST_PATTERN_ALL_FFFH,
+ TEST_PATTERN_ALL_555H,
+ TEST_PATTERN_ALL_AAAH,
+ TEST_PATTERN_VSP_5AH, /* VERTICAL STRIPE PATTERN 555H/AAAH */
+ TEST_PATTERN_VSP_A5H, /* VERTICAL STRIPE PATTERN AAAH/555H */
+ TEST_PATTERN_VSP_05H, /* VERTICAL STRIPE PATTERN 000H/555H */
+ TEST_PATTERN_VSP_50H, /* VERTICAL STRIPE PATTERN 555H/000H */
+ TEST_PATTERN_VSP_0FH, /* VERTICAL STRIPE PATTERN 000H/FFFH */
+ TEST_PATTERN_VSP_F0H, /* VERTICAL STRIPE PATTERN FFFH/000H */
+ TEST_PATTERN_H_COLOR_BARS,
+ TEST_PATTERN_V_COLOR_BARS,
+};
+
+static const char * const tp_qmenu[] = {
+ "Disabled",
+ "All 000h Pattern",
+ "All FFFh Pattern",
+ "All 555h Pattern",
+ "All AAAh Pattern",
+ "Vertical Stripe (555h / AAAh)",
+ "Vertical Stripe (AAAh / 555h)",
+ "Vertical Stripe (000h / 555h)",
+ "Vertical Stripe (555h / 000h)",
+ "Vertical Stripe (000h / FFFh)",
+ "Vertical Stripe (FFFh / 000h)",
+ "Horizontal Color Bars",
+ "Vertical Color Bars",
+};
+
+/*
+ * All-pixel scan mode (10-bit)
+ * imx274 mode1(refer to datasheet) register configuration with
+ * 3840x2160 resolution, raw10 data and mipi four lane output
+ */
+static const struct reg_8 imx274_mode1_3840x2160_raw10[] = {
+ {0x3004, 0x01},
+ {0x3005, 0x01},
+ {0x3006, 0x00},
+ {0x3007, 0x02},
+
+ {0x3018, 0xA2}, /* output XVS, HVS */
+
+ {0x306B, 0x05},
+ {0x30E2, 0x01},
+ {0x30F6, 0x07}, /* HMAX, 263 */
+ {0x30F7, 0x01}, /* HMAX */
+
+ {0x30dd, 0x01}, /* crop to 2160 */
+ {0x30de, 0x06},
+ {0x30df, 0x00},
+ {0x30e0, 0x12},
+ {0x30e1, 0x00},
+ {0x3037, 0x01}, /* to crop to 3840 */
+ {0x3038, 0x0c},
+ {0x3039, 0x00},
+ {0x303a, 0x0c},
+ {0x303b, 0x0f},
+
+ {0x30EE, 0x01},
+ {0x3130, 0x86},
+ {0x3131, 0x08},
+ {0x3132, 0x7E},
+ {0x3133, 0x08},
+ {0x3342, 0x0A},
+ {0x3343, 0x00},
+ {0x3344, 0x16},
+ {0x3345, 0x00},
+ {0x33A6, 0x01},
+ {0x3528, 0x0E},
+ {0x3554, 0x1F},
+ {0x3555, 0x01},
+ {0x3556, 0x01},
+ {0x3557, 0x01},
+ {0x3558, 0x01},
+ {0x3559, 0x00},
+ {0x355A, 0x00},
+ {0x35BA, 0x0E},
+ {0x366A, 0x1B},
+ {0x366B, 0x1A},
+ {0x366C, 0x19},
+ {0x366D, 0x17},
+ {0x3A41, 0x08},
+
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * Horizontal/vertical 2/2-line binning
+ * (Horizontal and vertical weightedbinning, 10-bit)
+ * imx274 mode3(refer to datasheet) register configuration with
+ * 1920x1080 resolution, raw10 data and mipi four lane output
+ */
+static const struct reg_8 imx274_mode3_1920x1080_raw10[] = {
+ {0x3004, 0x02},
+ {0x3005, 0x21},
+ {0x3006, 0x00},
+ {0x3007, 0x11},
+
+ {0x3018, 0xA2}, /* output XVS, HVS */
+
+ {0x306B, 0x05},
+ {0x30E2, 0x02},
+
+ {0x30F6, 0x04}, /* HMAX, 260 */
+ {0x30F7, 0x01}, /* HMAX */
+
+ {0x30dd, 0x01}, /* to crop to 1920x1080 */
+ {0x30de, 0x05},
+ {0x30df, 0x00},
+ {0x30e0, 0x04},
+ {0x30e1, 0x00},
+ {0x3037, 0x01},
+ {0x3038, 0x0c},
+ {0x3039, 0x00},
+ {0x303a, 0x0c},
+ {0x303b, 0x0f},
+
+ {0x30EE, 0x01},
+ {0x3130, 0x4E},
+ {0x3131, 0x04},
+ {0x3132, 0x46},
+ {0x3133, 0x04},
+ {0x3342, 0x0A},
+ {0x3343, 0x00},
+ {0x3344, 0x1A},
+ {0x3345, 0x00},
+ {0x33A6, 0x01},
+ {0x3528, 0x0E},
+ {0x3554, 0x00},
+ {0x3555, 0x01},
+ {0x3556, 0x01},
+ {0x3557, 0x01},
+ {0x3558, 0x01},
+ {0x3559, 0x00},
+ {0x355A, 0x00},
+ {0x35BA, 0x0E},
+ {0x366A, 0x1B},
+ {0x366B, 0x1A},
+ {0x366C, 0x19},
+ {0x366D, 0x17},
+ {0x3A41, 0x08},
+
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * Vertical 2/3 subsampling binning horizontal 3 binning
+ * imx274 mode5(refer to datasheet) register configuration with
+ * 1280x720 resolution, raw10 data and mipi four lane output
+ */
+static const struct reg_8 imx274_mode5_1280x720_raw10[] = {
+ {0x3004, 0x03},
+ {0x3005, 0x31},
+ {0x3006, 0x00},
+ {0x3007, 0x09},
+
+ {0x3018, 0xA2}, /* output XVS, HVS */
+
+ {0x306B, 0x05},
+ {0x30E2, 0x03},
+
+ {0x30F6, 0x04}, /* HMAX, 260 */
+ {0x30F7, 0x01}, /* HMAX */
+
+ {0x30DD, 0x01},
+ {0x30DE, 0x07},
+ {0x30DF, 0x00},
+ {0x40E0, 0x04},
+ {0x30E1, 0x00},
+ {0x3030, 0xD4},
+ {0x3031, 0x02},
+ {0x3032, 0xD0},
+ {0x3033, 0x02},
+
+ {0x30EE, 0x01},
+ {0x3130, 0xE2},
+ {0x3131, 0x02},
+ {0x3132, 0xDE},
+ {0x3133, 0x02},
+ {0x3342, 0x0A},
+ {0x3343, 0x00},
+ {0x3344, 0x1B},
+ {0x3345, 0x00},
+ {0x33A6, 0x01},
+ {0x3528, 0x0E},
+ {0x3554, 0x00},
+ {0x3555, 0x01},
+ {0x3556, 0x01},
+ {0x3557, 0x01},
+ {0x3558, 0x01},
+ {0x3559, 0x00},
+ {0x355A, 0x00},
+ {0x35BA, 0x0E},
+ {0x366A, 0x1B},
+ {0x366B, 0x19},
+ {0x366C, 0x17},
+ {0x366D, 0x17},
+ {0x3A41, 0x04},
+
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 first step register configuration for
+ * starting stream
+ */
+static const struct reg_8 imx274_start_1[] = {
+ {IMX274_STANDBY_REG, 0x12},
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 second step register configuration for
+ * starting stream
+ */
+static const struct reg_8 imx274_start_2[] = {
+ {0x3120, 0xF0}, /* clock settings */
+ {0x3121, 0x00}, /* clock settings */
+ {0x3122, 0x02}, /* clock settings */
+ {0x3129, 0x9C}, /* clock settings */
+ {0x312A, 0x02}, /* clock settings */
+ {0x312D, 0x02}, /* clock settings */
+
+ {0x310B, 0x00},
+
+ /* PLSTMG */
+ {0x304C, 0x00}, /* PLSTMG01 */
+ {0x304D, 0x03},
+ {0x331C, 0x1A},
+ {0x331D, 0x00},
+ {0x3502, 0x02},
+ {0x3529, 0x0E},
+ {0x352A, 0x0E},
+ {0x352B, 0x0E},
+ {0x3538, 0x0E},
+ {0x3539, 0x0E},
+ {0x3553, 0x00},
+ {0x357D, 0x05},
+ {0x357F, 0x05},
+ {0x3581, 0x04},
+ {0x3583, 0x76},
+ {0x3587, 0x01},
+ {0x35BB, 0x0E},
+ {0x35BC, 0x0E},
+ {0x35BD, 0x0E},
+ {0x35BE, 0x0E},
+ {0x35BF, 0x0E},
+ {0x366E, 0x00},
+ {0x366F, 0x00},
+ {0x3670, 0x00},
+ {0x3671, 0x00},
+
+ /* PSMIPI */
+ {0x3304, 0x32}, /* PSMIPI1 */
+ {0x3305, 0x00},
+ {0x3306, 0x32},
+ {0x3307, 0x00},
+ {0x3590, 0x32},
+ {0x3591, 0x00},
+ {0x3686, 0x32},
+ {0x3687, 0x00},
+
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 third step register configuration for
+ * starting stream
+ */
+static const struct reg_8 imx274_start_3[] = {
+ {IMX274_STANDBY_REG, 0x00},
+ {0x303E, 0x02}, /* SYS_MODE = 2 */
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 forth step register configuration for
+ * starting stream
+ */
+static const struct reg_8 imx274_start_4[] = {
+ {0x30F4, 0x00},
+ {0x3018, 0xA2}, /* XHS VHS OUTUPT */
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 register configuration for stoping stream
+ */
+static const struct reg_8 imx274_stop[] = {
+ {IMX274_STANDBY_REG, 0x01},
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 disable test pattern register configuration
+ */
+static const struct reg_8 imx274_tp_disabled[] = {
+ {0x303C, 0x00},
+ {0x377F, 0x00},
+ {0x3781, 0x00},
+ {0x370B, 0x00},
+ {IMX274_TABLE_END, 0x00}
+};
+
+/*
+ * imx274 test pattern register configuration
+ * reg 0x303D defines the test pattern modes
+ */
+static const struct reg_8 imx274_tp_regs[] = {
+ {0x303C, 0x11},
+ {0x370E, 0x01},
+ {0x377F, 0x01},
+ {0x3781, 0x01},
+ {0x370B, 0x11},
+ {IMX274_TABLE_END, 0x00}
+};
+
+static const struct reg_8 *mode_table[] = {
+ [IMX274_MODE_3840X2160] = imx274_mode1_3840x2160_raw10,
+ [IMX274_MODE_1920X1080] = imx274_mode3_1920x1080_raw10,
+ [IMX274_MODE_1280X720] = imx274_mode5_1280x720_raw10,
+
+ [IMX274_MODE_START_STREAM_1] = imx274_start_1,
+ [IMX274_MODE_START_STREAM_2] = imx274_start_2,
+ [IMX274_MODE_START_STREAM_3] = imx274_start_3,
+ [IMX274_MODE_START_STREAM_4] = imx274_start_4,
+ [IMX274_MODE_STOP_STREAM] = imx274_stop,
+};
+
+/*
+ * imx274 format related structure
+ */
+static const struct imx274_frmfmt imx274_formats[] = {
+ {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {3840, 2160},
+ IMX274_MODE_3840X2160},
+ {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1920, 1080},
+ IMX274_MODE_1920X1080},
+ {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1280, 720},
+ IMX274_MODE_1280X720},
+};
+
+/*
+ * minimal frame length for each mode
+ * refer to datasheet section "Frame Rate Adjustment (CSI-2)"
+ */
+static const int min_frame_len[] = {
+ 4550, /* mode 1, 4K */
+ 2310, /* mode 3, 1080p */
+ 2310 /* mode 5, 720p */
+};
+
+/*
+ * minimal numbers of SHR register
+ * refer to datasheet table "Shutter Setting (CSI-2)"
+ */
+static const int min_SHR[] = {
+ 12, /* mode 1, 4K */
+ 8, /* mode 3, 1080p */
+ 8 /* mode 5, 720p */
+};
+
+static const int max_frame_rate[] = {
+ 60, /* mode 1 , 4K */
+ 120, /* mode 3, 1080p */
+ 120 /* mode 5, 720p */
+};
+
+/*
+ * Number of clocks per internal offset period
+ * a constant based on mode
+ * refer to section "Integration Time in Each Readout Drive Mode (CSI-2)"
+ * in the datasheet
+ * for the implemented 3 modes, it happens to be the same number
+ */
+static const int nocpiop[] = {
+ 112, /* mode 1 , 4K */
+ 112, /* mode 3, 1080p */
+ 112 /* mode 5, 720p */
+};
+
+/*
+ * struct imx274_ctrls - imx274 ctrl structure
+ * @handler: V4L2 ctrl handler structure
+ * @exposure: Pointer to expsure ctrl structure
+ * @gain: Pointer to gain ctrl structure
+ * @vflip: Pointer to vflip ctrl structure
+ * @test_pattern: Pointer to test pattern ctrl structure
+ */
+struct imx274_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *test_pattern;
+};
+
+/*
+ * struct stim274 - imx274 device structure
+ * @sd: V4L2 subdevice structure
+ * @pd: Media pad structure
+ * @client: Pointer to I2C client
+ * @ctrls: imx274 control structure
+ * @format: V4L2 media bus frame format structure
+ * @frame_rate: V4L2 frame rate structure
+ * @regmap: Pointer to regmap structure
+ * @reset_gpio: Pointer to reset gpio
+ * @lock: Mutex structure
+ * @mode_index: Resolution mode index
+ */
+struct stimx274 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct i2c_client *client;
+ struct imx274_ctrls ctrls;
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_fract frame_interval;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct mutex lock; /* mutex lock for operations */
+ u32 mode_index;
+};
+
+/*
+ * Function declaration
+ */
+static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl);
+static int imx274_set_exposure(struct stimx274 *priv, int val);
+static int imx274_set_vflip(struct stimx274 *priv, int val);
+static int imx274_set_test_pattern(struct stimx274 *priv, int val);
+static int imx274_set_frame_interval(struct stimx274 *priv,
+ struct v4l2_fract frame_interval);
+
+static inline void msleep_range(unsigned int delay_base)
+{
+ usleep_range(delay_base * 1000, delay_base * 1000 + 500);
+}
+
+/*
+ * v4l2_ctrl and v4l2_subdev related operations
+ */
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler,
+ struct stimx274, ctrls.handler)->sd;
+}
+
+static inline struct stimx274 *to_imx274(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct stimx274, sd);
+}
+
+/*
+ * imx274_regmap_util_write_table_8 - Function for writing register table
+ * @regmap: Pointer to device reg map structure
+ * @table: Table containing register values
+ * @wait_ms_addr: Flag for performing delay
+ * @end_addr: Flag for incating end of table
+ *
+ * This is used to write register table into sensor's reg map.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int imx274_regmap_util_write_table_8(struct regmap *regmap,
+ const struct reg_8 table[],
+ u16 wait_ms_addr, u16 end_addr)
+{
+ int err;
+ const struct reg_8 *next;
+ u8 val;
+
+ int range_start = -1;
+ int range_count = 0;
+ u8 range_vals[16];
+ int max_range_vals = ARRAY_SIZE(range_vals);
+
+ for (next = table;; next++) {
+ if ((next->addr != range_start + range_count) ||
+ (next->addr == end_addr) ||
+ (next->addr == wait_ms_addr) ||
+ (range_count == max_range_vals)) {
+ if (range_count == 1)
+ err = regmap_write(regmap,
+ range_start, range_vals[0]);
+ else if (range_count > 1)
+ err = regmap_bulk_write(regmap, range_start,
+ &range_vals[0],
+ range_count);
+
+ if (err)
+ return err;
+
+ range_start = -1;
+ range_count = 0;
+
+ /* Handle special address values */
+ if (next->addr == end_addr)
+ break;
+
+ if (next->addr == wait_ms_addr) {
+ msleep_range(next->val);
+ continue;
+ }
+ }
+
+ val = next->val;
+
+ if (range_start == -1)
+ range_start = next->addr;
+
+ range_vals[range_count++] = val;
+ }
+ return 0;
+}
+
+static inline int imx274_read_reg(struct stimx274 *priv, u16 addr, u8 *val)
+{
+ int err;
+
+ err = regmap_read(priv->regmap, addr, (unsigned int *)val);
+ if (err)
+ dev_err(&priv->client->dev,
+ "%s : i2c read failed, addr = %x\n", __func__, addr);
+ else
+ dev_dbg(&priv->client->dev,
+ "%s : addr 0x%x, val=0x%x\n", __func__,
+ addr, *val);
+ return err;
+}
+
+static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val)
+{
+ int err;
+
+ err = regmap_write(priv->regmap, addr, val);
+ if (err)
+ dev_err(&priv->client->dev,
+ "%s : i2c write failed, %x = %x\n", __func__,
+ addr, val);
+ else
+ dev_dbg(&priv->client->dev,
+ "%s : addr 0x%x, val=0x%x\n", __func__,
+ addr, val);
+ return err;
+}
+
+static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[])
+{
+ return imx274_regmap_util_write_table_8(priv->regmap,
+ table, IMX274_TABLE_WAIT_MS, IMX274_TABLE_END);
+}
+
+/*
+ * imx274_mode_regs - Function for set mode registers per mode index
+ * @priv: Pointer to device structure
+ * @mode: Mode index value
+ *
+ * This is used to start steam per mode index.
+ * mode = 0, start stream for sensor Mode 1: 4K/raw10
+ * mode = 1, start stream for sensor Mode 3: 1080p/raw10
+ * mode = 2, start stream for sensor Mode 5: 720p/raw10
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int imx274_mode_regs(struct stimx274 *priv, int mode)
+{
+ int err = 0;
+
+ err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_1]);
+ if (err)
+ return err;
+
+ err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_2]);
+ if (err)
+ return err;
+
+ err = imx274_write_table(priv, mode_table[mode]);
+
+ return err;
+}
+
+/*
+ * imx274_start_stream - Function for starting stream per mode index
+ * @priv: Pointer to device structure
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int imx274_start_stream(struct stimx274 *priv)
+{
+ int err = 0;
+
+ /*
+ * Refer to "Standby Cancel Sequence when using CSI-2" in
+ * imx274 datasheet, it should wait 10ms or more here.
+ * give it 1 extra ms for margin
+ */
+ msleep_range(11);
+ err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_3]);
+ if (err)
+ return err;
+
+ /*
+ * Refer to "Standby Cancel Sequence when using CSI-2" in
+ * imx274 datasheet, it should wait 7ms or more here.
+ * give it 1 extra ms for margin
+ */
+ msleep_range(8);
+ err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_4]);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/*
+ * imx274_reset - Function called to reset the sensor
+ * @priv: Pointer to device structure
+ * @rst: Input value for determining the sensor's end state after reset
+ *
+ * Set the senor in reset and then
+ * if rst = 0, keep it in reset;
+ * if rst = 1, bring it out of reset.
+ *
+ */
+static void imx274_reset(struct stimx274 *priv, int rst)
+{
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ usleep_range(IMX274_RESET_DELAY1, IMX274_RESET_DELAY2);
+ gpiod_set_value_cansleep(priv->reset_gpio, !!rst);
+ usleep_range(IMX274_RESET_DELAY1, IMX274_RESET_DELAY2);
+}
+
+/**
+ * imx274_s_ctrl - This is used to set the imx274 V4L2 controls
+ * @ctrl: V4L2 control to be set
+ *
+ * This function is used to set the V4L2 controls for the imx274 sensor.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int imx274_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct stimx274 *imx274 = to_imx274(sd);
+ int ret = -EINVAL;
+
+ dev_dbg(&imx274->client->dev,
+ "%s : s_ctrl: %s, value: %d\n", __func__,
+ ctrl->name, ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&imx274->client->dev,
+ "%s : set V4L2_CID_EXPOSURE\n", __func__);
+ ret = imx274_set_exposure(imx274, ctrl->val);
+ break;
+
+ case V4L2_CID_GAIN:
+ dev_dbg(&imx274->client->dev,
+ "%s : set V4L2_CID_GAIN\n", __func__);
+ ret = imx274_set_gain(imx274, ctrl);
+ break;
+
+ case V4L2_CID_VFLIP:
+ dev_dbg(&imx274->client->dev,
+ "%s : set V4L2_CID_VFLIP\n", __func__);
+ ret = imx274_set_vflip(imx274, ctrl->val);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ dev_dbg(&imx274->client->dev,
+ "%s : set V4L2_CID_TEST_PATTERN\n", __func__);
+ ret = imx274_set_test_pattern(imx274, ctrl->val);
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * imx274_get_fmt - Get the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to get the pad format information.
+ *
+ * Return: 0 on success
+ */
+static int imx274_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct stimx274 *imx274 = to_imx274(sd);
+
+ mutex_lock(&imx274->lock);
+ fmt->format = imx274->format;
+ mutex_unlock(&imx274->lock);
+ return 0;
+}
+
+/**
+ * imx274_set_fmt - This is used to set the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @format: Pointer to pad level media bus format
+ *
+ * This function is used to set the pad format.
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct stimx274 *imx274 = to_imx274(sd);
+ struct i2c_client *client = imx274->client;
+ int index;
+
+ dev_dbg(&client->dev,
+ "%s: width = %d height = %d code = %d mbus_code = %d\n",
+ __func__, fmt->width, fmt->height, fmt->code,
+ imx274_formats[imx274->mode_index].mbus_code);
+
+ mutex_lock(&imx274->lock);
+
+ for (index = 0; index < ARRAY_SIZE(imx274_formats); index++) {
+ if (imx274_formats[index].size.width == fmt->width &&
+ imx274_formats[index].size.height == fmt->height)
+ break;
+ }
+
+ if (index >= ARRAY_SIZE(imx274_formats)) {
+ /* default to first format */
+ index = 0;
+ }
+
+ imx274->mode_index = index;
+
+ if (fmt->width > IMX274_MAX_WIDTH)
+ fmt->width = IMX274_MAX_WIDTH;
+ if (fmt->height > IMX274_MAX_HEIGHT)
+ fmt->height = IMX274_MAX_HEIGHT;
+ fmt->width = fmt->width & (~IMX274_MASK_LSB_2_BITS);
+ fmt->height = fmt->height & (~IMX274_MASK_LSB_2_BITS);
+ fmt->field = V4L2_FIELD_NONE;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ cfg->try_fmt = *fmt;
+ else
+ imx274->format = *fmt;
+
+ mutex_unlock(&imx274->lock);
+ return 0;
+}
+
+/**
+ * imx274_g_frame_interval - Get the frame interval
+ * @sd: Pointer to V4L2 Sub device structure
+ * @fi: Pointer to V4l2 Sub device frame interval structure
+ *
+ * This function is used to get the frame interval.
+ *
+ * Return: 0 on success
+ */
+static int imx274_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct stimx274 *imx274 = to_imx274(sd);
+
+ fi->interval = imx274->frame_interval;
+ dev_dbg(&imx274->client->dev, "%s frame rate = %d / %d\n",
+ __func__, imx274->frame_interval.numerator,
+ imx274->frame_interval.denominator);
+
+ return 0;
+}
+
+/**
+ * imx274_s_frame_interval - Set the frame interval
+ * @sd: Pointer to V4L2 Sub device structure
+ * @fi: Pointer to V4l2 Sub device frame interval structure
+ *
+ * This function is used to set the frame intervavl.
+ *
+ * Return: 0 on success
+ */
+static int imx274_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct stimx274 *imx274 = to_imx274(sd);
+ struct v4l2_ctrl *ctrl = imx274->ctrls.exposure;
+ int min, max, def;
+ int ret;
+
+ mutex_lock(&imx274->lock);
+ ret = imx274_set_frame_interval(imx274, fi->interval);
+
+ if (!ret) {
+ /*
+ * exposure time range is decided by frame interval
+ * need to update it after frame interal changes
+ */
+ min = IMX274_MIN_EXPOSURE_TIME;
+ max = fi->interval.numerator * 1000000
+ / fi->interval.denominator;
+ def = max;
+ if (__v4l2_ctrl_modify_range(ctrl, min, max, 1, def)) {
+ dev_err(&imx274->client->dev,
+ "Exposure ctrl range update failed\n");
+ goto unlock;
+ }
+
+ /* update exposure time accordingly */
+ imx274_set_exposure(imx274, imx274->ctrls.exposure->val);
+
+ dev_dbg(&imx274->client->dev, "set frame interval to %uus\n",
+ fi->interval.numerator * 1000000
+ / fi->interval.denominator);
+ }
+
+unlock:
+ mutex_unlock(&imx274->lock);
+
+ return ret;
+}
+
+/**
+ * imx274_load_default - load default control values
+ * @priv: Pointer to device structure
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int imx274_load_default(struct stimx274 *priv)
+{
+ int ret;
+
+ /* load default control values */
+ priv->frame_interval.numerator = 1;
+ priv->frame_interval.denominator = IMX274_DEF_FRAME_RATE;
+ priv->ctrls.exposure->val = 1000000 / IMX274_DEF_FRAME_RATE;
+ priv->ctrls.gain->val = IMX274_DEF_GAIN;
+ priv->ctrls.vflip->val = 0;
+ priv->ctrls.test_pattern->val = TEST_PATTERN_DISABLED;
+
+ /* update frame rate */
+ ret = imx274_set_frame_interval(priv,
+ priv->frame_interval);
+ if (ret)
+ return ret;
+
+ /* update exposure time */
+ ret = v4l2_ctrl_s_ctrl(priv->ctrls.exposure, priv->ctrls.exposure->val);
+ if (ret)
+ return ret;
+
+ /* update gain */
+ ret = v4l2_ctrl_s_ctrl(priv->ctrls.gain, priv->ctrls.gain->val);
+ if (ret)
+ return ret;
+
+ /* update vflip */
+ ret = v4l2_ctrl_s_ctrl(priv->ctrls.vflip, priv->ctrls.vflip->val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * imx274_s_stream - It is used to start/stop the streaming.
+ * @sd: V4L2 Sub device
+ * @on: Flag (True / False)
+ *
+ * This function controls the start or stop of streaming for the
+ * imx274 sensor.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int imx274_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct stimx274 *imx274 = to_imx274(sd);
+ int ret = 0;
+
+ dev_dbg(&imx274->client->dev, "%s : %s, mode index = %d\n", __func__,
+ on ? "Stream Start" : "Stream Stop", imx274->mode_index);
+
+ mutex_lock(&imx274->lock);
+
+ if (on) {
+ /* load mode registers */
+ imx274_mode_regs(imx274, imx274->mode_index);
+ if (ret)
+ goto fail;
+
+ /*
+ * update frame rate & expsoure. if the last mode is different,
+ * HMAX could be changed. As the result, frame rate & exposure
+ * are changed.
+ * gain is not affected.
+ */
+ ret = imx274_set_frame_interval(imx274,
+ imx274->frame_interval);
+ if (ret)
+ goto fail;
+
+ /* update exposure time */
+ ret = __v4l2_ctrl_s_ctrl(imx274->ctrls.exposure,
+ imx274->ctrls.exposure->val);
+ if (ret)
+ goto fail;
+
+ /* start stream */
+ ret = imx274_start_stream(imx274);
+ if (ret)
+ goto fail;
+ } else {
+ /* stop stream */
+ ret = imx274_write_table(imx274,
+ mode_table[IMX274_MODE_STOP_STREAM]);
+ if (ret)
+ goto fail;
+ }
+
+ mutex_unlock(&imx274->lock);
+ dev_dbg(&imx274->client->dev,
+ "%s : Done: mode = %d\n", __func__, imx274->mode_index);
+ return 0;
+
+fail:
+ mutex_unlock(&imx274->lock);
+ dev_err(&imx274->client->dev, "s_stream failed\n");
+ return ret;
+}
+
+/*
+ * imx274_get_frame_length - Function for obtaining current frame length
+ * @priv: Pointer to device structure
+ * @val: Pointer to obainted value
+ *
+ * frame_length = vmax x (svr + 1), in unit of hmax.
+ *
+ * Return: 0 on success
+ */
+static int imx274_get_frame_length(struct stimx274 *priv, u32 *val)
+{
+ int err;
+ u16 svr;
+ u32 vmax;
+ u8 reg_val[3];
+
+ /* svr */
+ err = imx274_read_reg(priv, IMX274_SVR_REG_LSB, ®_val[0]);
+ if (err)
+ goto fail;
+
+ err = imx274_read_reg(priv, IMX274_SVR_REG_MSB, ®_val[1]);
+ if (err)
+ goto fail;
+
+ svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0];
+
+ /* vmax */
+ err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_3, ®_val[0]);
+ if (err)
+ goto fail;
+
+ err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_2, ®_val[1]);
+ if (err)
+ goto fail;
+
+ err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_1, ®_val[2]);
+ if (err)
+ goto fail;
+
+ vmax = ((reg_val[2] & IMX274_MASK_LSB_3_BITS) << IMX274_SHIFT_16_BITS)
+ + (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0];
+
+ *val = vmax * (svr + 1);
+
+ return 0;
+
+fail:
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+ return err;
+}
+
+static int imx274_clamp_coarse_time(struct stimx274 *priv, u32 *val,
+ u32 *frame_length)
+{
+ int err;
+
+ err = imx274_get_frame_length(priv, frame_length);
+ if (err)
+ return err;
+
+ if (*frame_length < min_frame_len[priv->mode_index])
+ *frame_length = min_frame_len[priv->mode_index];
+
+ *val = *frame_length - *val; /* convert to raw shr */
+ if (*val > *frame_length - IMX274_SHR_LIMIT_CONST)
+ *val = *frame_length - IMX274_SHR_LIMIT_CONST;
+ else if (*val < min_SHR[priv->mode_index])
+ *val = min_SHR[priv->mode_index];
+
+ return 0;
+}
+
+/*
+ * imx274_set_digital gain - Function called when setting digital gain
+ * @priv: Pointer to device structure
+ * @dgain: Value of digital gain.
+ *
+ * Digital gain has only 4 steps: 1x, 2x, 4x, and 8x
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_digital_gain(struct stimx274 *priv, u32 dgain)
+{
+ u8 reg_val;
+
+ reg_val = ffs(dgain);
+
+ if (reg_val)
+ reg_val--;
+
+ reg_val = clamp(reg_val, (u8)0, (u8)3);
+
+ return imx274_write_reg(priv, IMX274_DIGITAL_GAIN_REG,
+ reg_val & IMX274_MASK_LSB_4_BITS);
+}
+
+static inline void imx274_calculate_gain_regs(struct reg_8 regs[2], u16 gain)
+{
+ regs->addr = IMX274_ANALOG_GAIN_ADDR_MSB;
+ regs->val = (gain >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_3_BITS;
+
+ (regs + 1)->addr = IMX274_ANALOG_GAIN_ADDR_LSB;
+ (regs + 1)->val = (gain) & IMX274_MASK_LSB_8_BITS;
+}
+
+/*
+ * imx274_set_gain - Function called when setting gain
+ * @priv: Pointer to device structure
+ * @val: Value of gain. the real value = val << IMX274_GAIN_SHIFT;
+ * @ctrl: v4l2 control pointer
+ *
+ * Set the gain based on input value.
+ * The caller should hold the mutex lock imx274->lock if necessary
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl)
+{
+ struct reg_8 reg_list[2];
+ int err;
+ u32 gain, analog_gain, digital_gain, gain_reg;
+ int i;
+
+ gain = (u32)(ctrl->val);
+
+ dev_dbg(&priv->client->dev,
+ "%s : input gain = %d.%d\n", __func__,
+ gain >> IMX274_GAIN_SHIFT,
+ ((gain & IMX274_GAIN_SHIFT_MASK) * 100) >> IMX274_GAIN_SHIFT);
+
+ if (gain > IMX274_MAX_DIGITAL_GAIN * IMX274_MAX_ANALOG_GAIN)
+ gain = IMX274_MAX_DIGITAL_GAIN * IMX274_MAX_ANALOG_GAIN;
+ else if (gain < IMX274_MIN_GAIN)
+ gain = IMX274_MIN_GAIN;
+
+ if (gain <= IMX274_MAX_ANALOG_GAIN)
+ digital_gain = 1;
+ else if (gain <= IMX274_MAX_ANALOG_GAIN * 2)
+ digital_gain = 2;
+ else if (gain <= IMX274_MAX_ANALOG_GAIN * 4)
+ digital_gain = 4;
+ else
+ digital_gain = IMX274_MAX_DIGITAL_GAIN;
+
+ analog_gain = gain / digital_gain;
+
+ dev_dbg(&priv->client->dev,
+ "%s : digital gain = %d, analog gain = %d.%d\n",
+ __func__, digital_gain, analog_gain >> IMX274_GAIN_SHIFT,
+ ((analog_gain & IMX274_GAIN_SHIFT_MASK) * 100)
+ >> IMX274_GAIN_SHIFT);
+
+ err = imx274_set_digital_gain(priv, digital_gain);
+ if (err)
+ goto fail;
+
+ /* convert to register value, refer to imx274 datasheet */
+ gain_reg = (u32)IMX274_GAIN_CONST -
+ (IMX274_GAIN_CONST << IMX274_GAIN_SHIFT) / analog_gain;
+ if (gain_reg > IMX274_GAIN_REG_MAX)
+ gain_reg = IMX274_GAIN_REG_MAX;
+
+ imx274_calculate_gain_regs(reg_list, (u16)gain_reg);
+
+ for (i = 0; i < ARRAY_SIZE(reg_list); i++) {
+ err = imx274_write_reg(priv, reg_list[i].addr,
+ reg_list[i].val);
+ if (err)
+ goto fail;
+ }
+
+ if (IMX274_GAIN_CONST - gain_reg == 0) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ /* convert register value back to gain value */
+ ctrl->val = (IMX274_GAIN_CONST << IMX274_GAIN_SHIFT)
+ / (IMX274_GAIN_CONST - gain_reg) * digital_gain;
+
+ dev_dbg(&priv->client->dev,
+ "%s : GAIN control success, gain_reg = %d, new gain = %d\n",
+ __func__, gain_reg, ctrl->val);
+
+ return 0;
+
+fail:
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+ return err;
+}
+
+static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2],
+ u32 coarse_time)
+{
+ regs->addr = IMX274_COARSE_TIME_ADDR_MSB;
+ regs->val = (coarse_time >> IMX274_SHIFT_8_BITS)
+ & IMX274_MASK_LSB_8_BITS;
+ (regs + 1)->addr = IMX274_COARSE_TIME_ADDR_LSB;
+ (regs + 1)->val = (coarse_time) & IMX274_MASK_LSB_8_BITS;
+}
+
+/*
+ * imx274_set_coarse_time - Function called when setting SHR value
+ * @priv: Pointer to device structure
+ * @val: Value for exposure time in number of line_length, or [HMAX]
+ *
+ * Set SHR value based on input value.
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_coarse_time(struct stimx274 *priv, u32 *val)
+{
+ struct reg_8 reg_list[2];
+ int err;
+ u32 coarse_time, frame_length;
+ int i;
+
+ coarse_time = *val;
+
+ /* convert exposure_time to appropriate SHR value */
+ err = imx274_clamp_coarse_time(priv, &coarse_time, &frame_length);
+ if (err)
+ goto fail;
+
+ /* prepare SHR registers */
+ imx274_calculate_coarse_time_regs(reg_list, coarse_time);
+
+ /* write to SHR registers */
+ for (i = 0; i < ARRAY_SIZE(reg_list); i++) {
+ err = imx274_write_reg(priv, reg_list[i].addr,
+ reg_list[i].val);
+ if (err)
+ goto fail;
+ }
+
+ *val = frame_length - coarse_time;
+ return 0;
+
+fail:
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+ return err;
+}
+
+/*
+ * imx274_set_exposure - Function called when setting exposure time
+ * @priv: Pointer to device structure
+ * @val: Variable for exposure time, in the unit of micro-second
+ *
+ * Set exposure time based on input value.
+ * The caller should hold the mutex lock imx274->lock if necessary
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_exposure(struct stimx274 *priv, int val)
+{
+ int err;
+ u16 hmax;
+ u8 reg_val[2];
+ u32 coarse_time; /* exposure time in unit of line (HMAX)*/
+
+ dev_dbg(&priv->client->dev,
+ "%s : EXPOSURE control input = %d\n", __func__, val);
+
+ /* step 1: convert input exposure_time (val) into number of 1[HMAX] */
+
+ /* obtain HMAX value */
+ err = imx274_read_reg(priv, IMX274_HMAX_REG_LSB, ®_val[0]);
+ if (err)
+ goto fail;
+ err = imx274_read_reg(priv, IMX274_HMAX_REG_MSB, ®_val[1]);
+ if (err)
+ goto fail;
+ hmax = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0];
+ if (hmax == 0) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ coarse_time = (IMX274_PIXCLK_CONST1 / IMX274_PIXCLK_CONST2 * val
+ - nocpiop[priv->mode_index]) / hmax;
+
+ /* step 2: convert exposure_time into SHR value */
+
+ /* set SHR */
+ err = imx274_set_coarse_time(priv, &coarse_time);
+ if (err)
+ goto fail;
+
+ priv->ctrls.exposure->val =
+ (coarse_time * hmax + nocpiop[priv->mode_index])
+ / (IMX274_PIXCLK_CONST1 / IMX274_PIXCLK_CONST2);
+
+ dev_dbg(&priv->client->dev,
+ "%s : EXPOSURE control success\n", __func__);
+ return 0;
+
+fail:
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+
+ return err;
+}
+
+/*
+ * imx274_set_vflip - Function called when setting vertical flip
+ * @priv: Pointer to device structure
+ * @val: Value for vflip setting
+ *
+ * Set vertical flip based on input value.
+ * val = 0: normal, no vertical flip
+ * val = 1: vertical flip enabled
+ * The caller should hold the mutex lock imx274->lock if necessary
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_vflip(struct stimx274 *priv, int val)
+{
+ int err;
+
+ err = imx274_write_reg(priv, IMX274_VFLIP_REG, val);
+ if (err) {
+ dev_err(&priv->client->dev, "VFILP control error\n");
+ return err;
+ }
+
+ dev_dbg(&priv->client->dev,
+ "%s : VFLIP control success\n", __func__);
+
+ return 0;
+}
+
+/*
+ * imx274_set_test_pattern - Function called when setting test pattern
+ * @priv: Pointer to device structure
+ * @val: Variable for test pattern
+ *
+ * Set to different test patterns based on input value.
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_test_pattern(struct stimx274 *priv, int val)
+{
+ int err = 0;
+
+ if (val == TEST_PATTERN_DISABLED) {
+ err = imx274_write_table(priv, imx274_tp_disabled);
+ } else if (val <= TEST_PATTERN_V_COLOR_BARS) {
+ err = imx274_write_reg(priv, IMX274_TEST_PATTERN_REG, val - 1);
+ if (!err)
+ err = imx274_write_table(priv, imx274_tp_regs);
+ } else {
+ err = -EINVAL;
+ }
+
+ if (!err)
+ dev_dbg(&priv->client->dev,
+ "%s : TEST PATTERN control success\n", __func__);
+ else
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+
+ return err;
+}
+
+static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3],
+ u32 frame_length)
+{
+ regs->addr = IMX274_FRAME_LENGTH_ADDR_1;
+ regs->val = (frame_length >> IMX274_SHIFT_16_BITS)
+ & IMX274_MASK_LSB_4_BITS;
+ (regs + 1)->addr = IMX274_FRAME_LENGTH_ADDR_2;
+ (regs + 1)->val = (frame_length >> IMX274_SHIFT_8_BITS)
+ & IMX274_MASK_LSB_8_BITS;
+ (regs + 2)->addr = IMX274_FRAME_LENGTH_ADDR_3;
+ (regs + 2)->val = (frame_length) & IMX274_MASK_LSB_8_BITS;
+}
+
+/*
+ * imx274_set_frame_length - Function called when setting frame length
+ * @priv: Pointer to device structure
+ * @val: Variable for frame length (= VMAX, i.e. vertical drive period length)
+ *
+ * Set frame length based on input value.
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_frame_length(struct stimx274 *priv, u32 val)
+{
+ struct reg_8 reg_list[3];
+ int err;
+ u32 frame_length;
+ int i;
+
+ dev_dbg(&priv->client->dev, "%s : input length = %d\n",
+ __func__, val);
+
+ frame_length = (u32)val;
+
+ imx274_calculate_frame_length_regs(reg_list, frame_length);
+ for (i = 0; i < ARRAY_SIZE(reg_list); i++) {
+ err = imx274_write_reg(priv, reg_list[i].addr,
+ reg_list[i].val);
+ if (err)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+ return err;
+}
+
+/*
+ * imx274_set_frame_interval - Function called when setting frame interval
+ * @priv: Pointer to device structure
+ * @frame_interval: Variable for frame interval
+ *
+ * Change frame interval by updating VMAX value
+ * The caller should hold the mutex lock imx274->lock if necessary
+ *
+ * Return: 0 on success
+ */
+static int imx274_set_frame_interval(struct stimx274 *priv,
+ struct v4l2_fract frame_interval)
+{
+ int err;
+ u32 frame_length, req_frame_rate;
+ u16 svr;
+ u16 hmax;
+ u8 reg_val[2];
+
+ dev_dbg(&priv->client->dev, "%s: input frame interval = %d / %d",
+ __func__, frame_interval.numerator,
+ frame_interval.denominator);
+
+ if (frame_interval.numerator == 0) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ req_frame_rate = (u32)(frame_interval.denominator
+ / frame_interval.numerator);
+
+ /* boundary check */
+ if (req_frame_rate > max_frame_rate[priv->mode_index]) {
+ frame_interval.numerator = 1;
+ frame_interval.denominator =
+ max_frame_rate[priv->mode_index];
+ } else if (req_frame_rate < IMX274_MIN_FRAME_RATE) {
+ frame_interval.numerator = 1;
+ frame_interval.denominator = IMX274_MIN_FRAME_RATE;
+ }
+
+ /*
+ * VMAX = 1/frame_rate x 72M / (SVR+1) / HMAX
+ * frame_length (i.e. VMAX) = (frame_interval) x 72M /(SVR+1) / HMAX
+ */
+
+ /* SVR */
+ err = imx274_read_reg(priv, IMX274_SVR_REG_LSB, ®_val[0]);
+ if (err)
+ goto fail;
+ err = imx274_read_reg(priv, IMX274_SVR_REG_MSB, ®_val[1]);
+ if (err)
+ goto fail;
+ svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0];
+ dev_dbg(&priv->client->dev,
+ "%s : register SVR = %d\n", __func__, svr);
+
+ /* HMAX */
+ err = imx274_read_reg(priv, IMX274_HMAX_REG_LSB, ®_val[0]);
+ if (err)
+ goto fail;
+ err = imx274_read_reg(priv, IMX274_HMAX_REG_MSB, ®_val[1]);
+ if (err)
+ goto fail;
+ hmax = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0];
+ dev_dbg(&priv->client->dev,
+ "%s : register HMAX = %d\n", __func__, hmax);
+
+ if (hmax == 0 || frame_interval.denominator == 0) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ frame_length = IMX274_PIXCLK_CONST1 / (svr + 1) / hmax
+ * frame_interval.numerator
+ / frame_interval.denominator;
+
+ err = imx274_set_frame_length(priv, frame_length);
+ if (err)
+ goto fail;
+
+ priv->frame_interval = frame_interval;
+ return 0;
+
+fail:
+ dev_err(&priv->client->dev, "%s error = %d\n", __func__, err);
+ return err;
+}
+
+static const struct v4l2_subdev_pad_ops imx274_pad_ops = {
+ .get_fmt = imx274_get_fmt,
+ .set_fmt = imx274_set_fmt,
+};
+
+static const struct v4l2_subdev_video_ops imx274_video_ops = {
+ .g_frame_interval = imx274_g_frame_interval,
+ .s_frame_interval = imx274_s_frame_interval,
+ .s_stream = imx274_s_stream,
+};
+
+static const struct v4l2_subdev_ops imx274_subdev_ops = {
+ .pad = &imx274_pad_ops,
+ .video = &imx274_video_ops,
+};
+
+static const struct v4l2_ctrl_ops imx274_ctrl_ops = {
+ .s_ctrl = imx274_s_ctrl,
+};
+
+static const struct of_device_id imx274_of_id_table[] = {
+ { .compatible = "sony,imx274" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, imx274_of_id_table);
+
+static const struct i2c_device_id imx274_id[] = {
+ { "IMX274", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, imx274_id);
+
+static int imx274_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ struct stimx274 *imx274;
+ int ret;
+
+ /* initialize imx274 */
+ imx274 = devm_kzalloc(&client->dev, sizeof(*imx274), GFP_KERNEL);
+ if (!imx274)
+ return -ENOMEM;
+
+ mutex_init(&imx274->lock);
+
+ /* initialize regmap */
+ imx274->regmap = devm_regmap_init_i2c(client, &imx274_regmap_config);
+ if (IS_ERR(imx274->regmap)) {
+ dev_err(&client->dev,
+ "regmap init failed: %ld\n", PTR_ERR(imx274->regmap));
+ ret = -ENODEV;
+ goto err_regmap;
+ }
+
+ /* initialize subdevice */
+ imx274->client = client;
+ sd = &imx274->sd;
+ v4l2_i2c_subdev_init(sd, client, &imx274_subdev_ops);
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+
+ /* initialize subdev media pad */
+ imx274->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &imx274->pad);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s : media entity init Failed %d\n", __func__, ret);
+ goto err_regmap;
+ }
+
+ /* initialize sensor reset gpio */
+ imx274->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(imx274->reset_gpio)) {
+ if (PTR_ERR(imx274->reset_gpio) != -EPROBE_DEFER)
+ dev_err(&client->dev, "Reset GPIO not setup in DT");
+ ret = PTR_ERR(imx274->reset_gpio);
+ goto err_me;
+ }
+
+ /* pull sensor out of reset */
+ imx274_reset(imx274, 1);
+
+ /* initialize controls */
+ ret = v4l2_ctrl_handler_init(&imx274->ctrls.handler, 2);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s : ctrl handler init Failed\n", __func__);
+ goto err_me;
+ }
+
+ imx274->ctrls.handler.lock = &imx274->lock;
+
+ /* add new controls */
+ imx274->ctrls.test_pattern = v4l2_ctrl_new_std_menu_items(
+ &imx274->ctrls.handler, &imx274_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(tp_qmenu) - 1, 0, 0, tp_qmenu);
+
+ imx274->ctrls.gain = v4l2_ctrl_new_std(
+ &imx274->ctrls.handler,
+ &imx274_ctrl_ops,
+ V4L2_CID_GAIN, IMX274_MIN_GAIN,
+ IMX274_MAX_DIGITAL_GAIN * IMX274_MAX_ANALOG_GAIN, 1,
+ IMX274_DEF_GAIN);
+
+ imx274->ctrls.exposure = v4l2_ctrl_new_std(
+ &imx274->ctrls.handler,
+ &imx274_ctrl_ops,
+ V4L2_CID_EXPOSURE, IMX274_MIN_EXPOSURE_TIME,
+ 1000000 / IMX274_DEF_FRAME_RATE, 1,
+ IMX274_MIN_EXPOSURE_TIME);
+
+ imx274->ctrls.vflip = v4l2_ctrl_new_std(
+ &imx274->ctrls.handler,
+ &imx274_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ imx274->sd.ctrl_handler = &imx274->ctrls.handler;
+ if (imx274->ctrls.handler.error) {
+ ret = imx274->ctrls.handler.error;
+ goto err_ctrls;
+ }
+
+ /* setup default controls */
+ ret = v4l2_ctrl_handler_setup(&imx274->ctrls.handler);
+ if (ret) {
+ dev_err(&client->dev,
+ "Error %d setup default controls\n", ret);
+ goto err_ctrls;
+ }
+
+ /* initialize format */
+ imx274->mode_index = IMX274_MODE_3840X2160;
+ imx274->format.width = imx274_formats[0].size.width;
+ imx274->format.height = imx274_formats[0].size.height;
+ imx274->format.field = V4L2_FIELD_NONE;
+ imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10;
+ imx274->format.colorspace = V4L2_COLORSPACE_SRGB;
+ imx274->frame_interval.numerator = 1;
+ imx274->frame_interval.denominator = IMX274_DEF_FRAME_RATE;
+
+ /* load default control values */
+ ret = imx274_load_default(imx274);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s : imx274_load_default failed %d\n",
+ __func__, ret);
+ goto err_ctrls;
+ }
+
+ /* register subdevice */
+ ret = v4l2_async_register_subdev(sd);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s : v4l2_async_register_subdev failed %d\n",
+ __func__, ret);
+ goto err_ctrls;
+ }
+
+ dev_info(&client->dev, "imx274 : imx274 probe success !\n");
+ return 0;
+
+err_ctrls:
+ v4l2_async_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+err_me:
+ media_entity_cleanup(&sd->entity);
+err_regmap:
+ mutex_destroy(&imx274->lock);
+ return ret;
+}
+
+static int imx274_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct stimx274 *imx274 = to_imx274(sd);
+
+ /* stop stream */
+ imx274_write_table(imx274, mode_table[IMX274_MODE_STOP_STREAM]);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+ mutex_destroy(&imx274->lock);
+ return 0;
+}
+
+static struct i2c_driver imx274_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = imx274_of_id_table,
+ },
+ .probe = imx274_probe,
+ .remove = imx274_remove,
+ .id_table = imx274_id,
+};
+
+module_i2c_driver(imx274_i2c_driver);
+
+MODULE_AUTHOR("Leon Luo <leonl@leopardimaging.com>");
+MODULE_DESCRIPTION("IMX274 CMOS Image Sensor driver");
+MODULE_LICENSE("GPL v2");
--
2.14.0.rc1
^ permalink raw reply related
* Re: [PATCH] pci/uio: enable prefetchable resources mapping
From: Ferruh Yigit @ 2017-10-05 0:06 UTC (permalink / raw)
To: Changpeng Liu, dev
In-Reply-To: <1496530644-8393-1-git-send-email-changpeng.liu@intel.com>
On 6/3/2017 11:57 PM, Changpeng Liu wrote:
> For PCI prefetchable resources, Linux will create a
> write combined file as well, the library will try
> to map resourceX_wc file first, if the file does
> not exist, then it will map resourceX as usual.
Hi Changpeng,
Code part looks OK, but can you please describe more why we should try
write combined resource file first, what is the benefit of using it _wc
file?
Thanks,
ferruh
>
> Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> ---
> lib/librte_eal/linuxapp/eal/eal_pci_uio.c | 19 ++++++++++++++-----
> 1 file changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
> index fa10329..d9fc20a 100644
> --- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
> +++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
> @@ -321,7 +321,7 @@
>
> /* update devname for mmap */
> snprintf(devname, sizeof(devname),
> - "%s/" PCI_PRI_FMT "/resource%d",
> + "%s/" PCI_PRI_FMT "/resource%d_wc",
> pci_get_sysfs_path(),
> loc->domain, loc->bus, loc->devid,
> loc->function, res_idx);
> @@ -335,13 +335,22 @@
> }
>
> /*
> - * open resource file, to mmap it
> + * open prefetchable resource file first, try to mmap it
> */
> fd = open(devname, O_RDWR);
> if (fd < 0) {
> - RTE_LOG(ERR, EAL, "Cannot open %s: %s\n",
> - devname, strerror(errno));
> - goto error;
> + snprintf(devname, sizeof(devname),
> + "%s/" PCI_PRI_FMT "/resource%d",
> + pci_get_sysfs_path(),
> + loc->domain, loc->bus, loc->devid,
> + loc->function, res_idx);
> + /* then try to map resource file */
> + fd = open(devname, O_RDWR);
> + if (fd < 0) {
> + RTE_LOG(ERR, EAL, "Cannot open %s: %s\n",
> + devname, strerror(errno));
> + goto error;
> + }
> }
>
> /* try mapping somewhere close to the end of hugepages */
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.