* [PATCH v15 19/32] v4l: async: Ensure only unique fwnodes are registered to notifiers
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
While registering a notifier, check that each newly added fwnode is
unique, and return an error if it is not. Also check that a newly added
notifier does not have the same fwnodes twice.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-async.c | 82 +++++++++++++++++++++++++++++++++---
1 file changed, 77 insertions(+), 5 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 3621ed6e6e3c..e1fe3567127a 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -330,8 +330,71 @@ static void v4l2_async_notifier_unbind_all_subdevs(
notifier->parent = NULL;
}
+/* See if an fwnode can be found in a notifier's lists. */
+static bool __v4l2_async_notifier_fwnode_has_async_subdev(
+ struct v4l2_async_notifier *notifier, struct fwnode_handle *fwnode)
+{
+ struct v4l2_async_subdev *asd;
+ struct v4l2_subdev *sd;
+
+ list_for_each_entry(asd, ¬ifier->waiting, list) {
+ if (asd->match_type != V4L2_ASYNC_MATCH_FWNODE)
+ continue;
+
+ if (asd->match.fwnode.fwnode == fwnode)
+ return true;
+ }
+
+ list_for_each_entry(sd, ¬ifier->done, async_list) {
+ if (WARN_ON(!sd->asd))
+ continue;
+
+ if (sd->asd->match_type != V4L2_ASYNC_MATCH_FWNODE)
+ continue;
+
+ if (sd->asd->match.fwnode.fwnode == fwnode)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Find out whether an async sub-device was set up for an fwnode already or
+ * whether it exists in a given notifier before @this_index.
+ */
+static bool v4l2_async_notifier_fwnode_has_async_subdev(
+ struct v4l2_async_notifier *notifier, struct fwnode_handle *fwnode,
+ unsigned int this_index)
+{
+ unsigned int j;
+
+ lockdep_assert_held(&list_lock);
+
+ /* Check that an fwnode is not being added more than once. */
+ for (j = 0; j < this_index; j++) {
+ struct v4l2_async_subdev *asd = notifier->subdevs[this_index];
+ struct v4l2_async_subdev *other_asd = notifier->subdevs[j];
+
+ if (other_asd->match_type == V4L2_ASYNC_MATCH_FWNODE &&
+ asd->match.fwnode.fwnode ==
+ other_asd->match.fwnode.fwnode)
+ return true;
+ }
+
+ /* Check than an fwnode did not exist in other notifiers. */
+ list_for_each_entry(notifier, ¬ifier_list, list)
+ if (__v4l2_async_notifier_fwnode_has_async_subdev(
+ notifier, fwnode))
+ return true;
+
+ return false;
+}
+
static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
{
+ struct device *dev =
+ notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
struct v4l2_async_subdev *asd;
int ret;
int i;
@@ -342,6 +405,8 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
+ mutex_lock(&list_lock);
+
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
@@ -349,19 +414,25 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
case V4L2_ASYNC_MATCH_CUSTOM:
case V4L2_ASYNC_MATCH_DEVNAME:
case V4L2_ASYNC_MATCH_I2C:
+ break;
case V4L2_ASYNC_MATCH_FWNODE:
+ if (v4l2_async_notifier_fwnode_has_async_subdev(
+ notifier, asd->match.fwnode.fwnode, i)) {
+ dev_err(dev,
+ "fwnode has already been registered or in notifier's subdev list\n");
+ ret = -EEXIST;
+ goto out_unlock;
+ }
break;
default:
- dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
- "Invalid match type %u on %p\n",
+ dev_err(dev, "Invalid match type %u on %p\n",
asd->match_type, asd);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_unlock;
}
list_add_tail(&asd->list, ¬ifier->waiting);
}
- mutex_lock(&list_lock);
-
ret = v4l2_async_notifier_try_all_subdevs(notifier);
if (ret)
goto err_unbind;
@@ -373,6 +444,7 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
/* Keep also completed notifiers on the list */
list_add(¬ifier->list, ¬ifier_list);
+out_unlock:
mutex_unlock(&list_lock);
return 0;
--
2.11.0
^ permalink raw reply related
* [PATCH v15 18/32] v4l: async: Allow binding notifiers to sub-devices
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Registering a notifier has required the knowledge of struct v4l2_device
for the reason that sub-devices generally are registered to the
v4l2_device (as well as the media device, also available through
v4l2_device).
This information is not available for sub-device drivers at probe time.
What this patch does is that it allows registering notifiers without
having v4l2_device around. Instead the sub-device pointer is stored in the
notifier. Once the sub-device of the driver that registered the notifier
is registered, the notifier will gain the knowledge of the v4l2_device,
and the binding of async sub-devices from the sub-device driver's notifier
may proceed.
The complete callbacks will be called only when the v4l2_device is
available and no notifier has pending sub-devices to bind.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-async.c | 239 ++++++++++++++++++++++++++++-------
include/media/v4l2-async.h | 16 ++-
2 files changed, 211 insertions(+), 44 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 1812310746d9..3621ed6e6e3c 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -124,11 +124,109 @@ static struct v4l2_async_subdev *v4l2_async_find_match(
return NULL;
}
+/* Find the sub-device notifier registered by a sub-device driver. */
+static struct v4l2_async_notifier *v4l2_async_find_subdev_notifier(
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_async_notifier *n;
+
+ list_for_each_entry(n, ¬ifier_list, list)
+ if (n->sd == sd)
+ return n;
+
+ return NULL;
+}
+
+/* Get v4l2_device related to the notifier if one can be found. */
+static struct v4l2_device *v4l2_async_notifier_find_v4l2_dev(
+ struct v4l2_async_notifier *notifier)
+{
+ while (notifier->parent)
+ notifier = notifier->parent;
+
+ return notifier->v4l2_dev;
+}
+
+/*
+ * Return true if all child sub-device notifiers are complete, false otherwise.
+ */
+static bool v4l2_async_notifier_can_complete(
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_subdev *sd;
+
+ if (!list_empty(¬ifier->waiting))
+ return false;
+
+ list_for_each_entry(sd, ¬ifier->done, async_list) {
+ struct v4l2_async_notifier *subdev_notifier =
+ v4l2_async_find_subdev_notifier(sd);
+
+ if (subdev_notifier &&
+ !v4l2_async_notifier_can_complete(subdev_notifier))
+ return false;
+ }
+
+ return true;
+}
+
+/* Complete all notifiers. Call on the root notifier. */
+static int v4l2_async_notifier_complete(
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_subdev *sd;
+
+ list_for_each_entry(sd, ¬ifier->done, async_list) {
+ struct v4l2_async_notifier *subdev_notifier =
+ v4l2_async_find_subdev_notifier(sd);
+ int ret;
+
+ if (!subdev_notifier)
+ continue;
+
+ ret = v4l2_async_notifier_complete(subdev_notifier);
+ if (ret)
+ return ret;
+ }
+
+ return v4l2_async_notifier_call_complete(notifier);
+}
+
+/*
+ * Complete notifiers if possible. This is done when all async sub-devices have
+ * been bound; v4l2_device is also available then.
+ */
+static int v4l2_async_notifier_try_complete(
+ struct v4l2_async_notifier *notifier)
+{
+ /* Quick check whether there are still more sub-devices here. */
+ if (!list_empty(¬ifier->waiting))
+ return 0;
+
+ /* Check the entire notifier tree; find the root notifier first. */
+ while (notifier->parent)
+ notifier = notifier->parent;
+
+ /* This is root if it has v4l2_dev. */
+ if (!notifier->v4l2_dev)
+ return 0;
+
+ /* Is everything ready? */
+ if (!v4l2_async_notifier_can_complete(notifier))
+ return 0;
+
+ return v4l2_async_notifier_complete(notifier);
+}
+
+static int v4l2_async_notifier_try_all_subdevs(
+ struct v4l2_async_notifier *notifier);
+
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
+ struct v4l2_async_notifier *subdev_notifier;
int ret;
ret = v4l2_device_register_subdev(v4l2_dev, sd);
@@ -149,17 +247,36 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
/* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, ¬ifier->done);
- return 0;
+ /*
+ * See if the sub-device has a notifier. If not, return here.
+ */
+ subdev_notifier = v4l2_async_find_subdev_notifier(sd);
+ if (!subdev_notifier || subdev_notifier->parent)
+ return 0;
+
+ /*
+ * Proceed with checking for the sub-device notifier's async
+ * sub-devices, and return the result. The error will be handled by the
+ * caller.
+ */
+ subdev_notifier->parent = notifier;
+
+ return v4l2_async_notifier_try_all_subdevs(subdev_notifier);
}
/* Test all async sub-devices in a notifier for a match. */
static int v4l2_async_notifier_try_all_subdevs(
struct v4l2_async_notifier *notifier)
{
- struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
- struct v4l2_subdev *sd, *tmp;
+ struct v4l2_device *v4l2_dev =
+ v4l2_async_notifier_find_v4l2_dev(notifier);
+ struct v4l2_subdev *sd;
- list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
+ if (!v4l2_dev)
+ return 0;
+
+again:
+ list_for_each_entry(sd, &subdev_list, async_list) {
struct v4l2_async_subdev *asd;
int ret;
@@ -168,10 +285,16 @@ static int v4l2_async_notifier_try_all_subdevs(
continue;
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
- if (ret < 0) {
- mutex_unlock(&list_lock);
+ if (ret < 0)
return ret;
- }
+
+ /*
+ * v4l2_async_match_notify() may lead to registering a
+ * new notifier and thus changing the async subdevs
+ * list. In order to proceed safely from here, restart
+ * parsing the list from the beginning.
+ */
+ goto again;
}
return 0;
@@ -185,17 +308,26 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd)
sd->asd = NULL;
}
+/* Unbind all sub-devices in the notifier tree. */
static void v4l2_async_notifier_unbind_all_subdevs(
struct v4l2_async_notifier *notifier)
{
struct v4l2_subdev *sd, *tmp;
list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
+ struct v4l2_async_notifier *subdev_notifier =
+ v4l2_async_find_subdev_notifier(sd);
+
+ if (subdev_notifier)
+ v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
+
v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd);
list_move(&sd->async_list, &subdev_list);
}
+
+ notifier->parent = NULL;
}
static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
@@ -210,15 +342,6 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
- if (!notifier->num_subdevs) {
- int ret;
-
- ret = v4l2_async_notifier_call_complete(notifier);
- notifier->v4l2_dev = NULL;
-
- return ret;
- }
-
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
@@ -240,16 +363,12 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
mutex_lock(&list_lock);
ret = v4l2_async_notifier_try_all_subdevs(notifier);
- if (ret) {
- mutex_unlock(&list_lock);
- return ret;
- }
+ if (ret)
+ goto err_unbind;
- if (list_empty(¬ifier->waiting)) {
- ret = v4l2_async_notifier_call_complete(notifier);
- if (ret)
- goto err_complete;
- }
+ ret = v4l2_async_notifier_try_complete(notifier);
+ if (ret)
+ goto err_unbind;
/* Keep also completed notifiers on the list */
list_add(¬ifier->list, ¬ifier_list);
@@ -258,7 +377,10 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
return 0;
-err_complete:
+err_unbind:
+ /*
+ * On failure, unbind all sub-devices registered through this notifier.
+ */
v4l2_async_notifier_unbind_all_subdevs(notifier);
mutex_unlock(&list_lock);
@@ -270,7 +392,8 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
int ret;
- if (WARN_ON(!v4l2_dev))
+
+ if (WARN_ON(!v4l2_dev || notifier->sd))
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
@@ -283,20 +406,39 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
}
EXPORT_SYMBOL(v4l2_async_notifier_register);
+int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
+ struct v4l2_async_notifier *notifier)
+{
+ int ret;
+
+ if (WARN_ON(!sd || notifier->v4l2_dev))
+ return -EINVAL;
+
+ notifier->sd = sd;
+
+ ret = __v4l2_async_notifier_register(notifier);
+ if (ret)
+ notifier->sd = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_async_subdev_notifier_register);
+
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{
- if (!notifier->v4l2_dev)
+ if (!notifier->v4l2_dev && !notifier->sd)
return;
mutex_lock(&list_lock);
- list_del(¬ifier->list);
-
v4l2_async_notifier_unbind_all_subdevs(notifier);
- mutex_unlock(&list_lock);
-
+ notifier->sd = NULL;
notifier->v4l2_dev = NULL;
+
+ list_del(¬ifier->list);
+
+ mutex_unlock(&list_lock);
}
EXPORT_SYMBOL(v4l2_async_notifier_unregister);
@@ -332,6 +474,7 @@ EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup);
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
+ struct v4l2_async_notifier *subdev_notifier;
struct v4l2_async_notifier *notifier;
int ret;
@@ -348,24 +491,26 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
INIT_LIST_HEAD(&sd->async_list);
list_for_each_entry(notifier, ¬ifier_list, list) {
- struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier,
- sd);
+ struct v4l2_device *v4l2_dev =
+ v4l2_async_notifier_find_v4l2_dev(notifier);
+ struct v4l2_async_subdev *asd;
int ret;
+ if (!v4l2_dev)
+ continue;
+
+ asd = v4l2_async_find_match(notifier, sd);
if (!asd)
continue;
ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd,
asd);
if (ret)
- goto err_unlock;
+ goto err_unbind;
- if (!list_empty(¬ifier->waiting))
- goto out_unlock;
-
- ret = v4l2_async_notifier_call_complete(notifier);
+ ret = v4l2_async_notifier_try_complete(notifier);
if (ret)
- goto err_cleanup;
+ goto err_unbind;
goto out_unlock;
}
@@ -378,11 +523,19 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
return 0;
-err_cleanup:
- v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
+err_unbind:
+ /*
+ * Complete failed. Unbind the sub-devices bound through registering
+ * this async sub-device.
+ */
+ subdev_notifier = v4l2_async_find_subdev_notifier(sd);
+ if (subdev_notifier)
+ v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
+
+ if (sd->asd)
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd);
-err_unlock:
mutex_unlock(&list_lock);
return ret;
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 68606afb5ef9..d33a52be2501 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -102,7 +102,9 @@ struct v4l2_async_notifier_operations {
* @num_subdevs: number of subdevices used in the subdevs array
* @max_subdevs: number of subdevices allocated in the subdevs array
* @subdevs: array of pointers to subdevice descriptors
- * @v4l2_dev: pointer to struct v4l2_device
+ * @v4l2_dev: v4l2_device of the root notifier, NULL otherwise
+ * @sd: sub-device that registered the notifier, NULL otherwise
+ * @parent: parent notifier
* @waiting: list of struct v4l2_async_subdev, waiting for their drivers
* @done: list of struct v4l2_subdev, already probed
* @list: member in a global list of notifiers
@@ -113,6 +115,8 @@ struct v4l2_async_notifier {
unsigned int max_subdevs;
struct v4l2_async_subdev **subdevs;
struct v4l2_device *v4l2_dev;
+ struct v4l2_subdev *sd;
+ struct v4l2_async_notifier *parent;
struct list_head waiting;
struct list_head done;
struct list_head list;
@@ -128,6 +132,16 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier);
/**
+ * v4l2_async_subdev_notifier_register - registers a subdevice asynchronous
+ * notifier for a sub-device
+ *
+ * @sd: pointer to &struct v4l2_subdev
+ * @notifier: pointer to &struct v4l2_async_notifier
+ */
+int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
+ struct v4l2_async_notifier *notifier);
+
+/**
* v4l2_async_notifier_unregister - unregisters a subdevice asynchronous notifier
*
* @notifier: pointer to &struct v4l2_async_notifier
--
2.11.0
^ permalink raw reply related
* [PATCH v15 14/32] v4l: async: Introduce helpers for calling async ops callbacks
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Add three helper functions to call async operations callbacks. Besides
simplifying callbacks, this allows async notifiers to have no ops set,
i.e. it can be left NULL.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
drivers/media/v4l2-core/v4l2-async.c | 56 +++++++++++++++++++++++++-----------
1 file changed, 39 insertions(+), 17 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 9d6fc5f25619..e170682dae78 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -25,6 +25,34 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+static int v4l2_async_notifier_call_bound(struct v4l2_async_notifier *n,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ if (!n->ops || !n->ops->bound)
+ return 0;
+
+ return n->ops->bound(n, subdev, asd);
+}
+
+static void v4l2_async_notifier_call_unbind(struct v4l2_async_notifier *n,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ if (!n->ops || !n->ops->unbind)
+ return;
+
+ n->ops->unbind(n, subdev, asd);
+}
+
+static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n)
+{
+ if (!n->ops || !n->ops->complete)
+ return 0;
+
+ return n->ops->complete(n);
+}
+
static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
#if IS_ENABLED(CONFIG_I2C)
@@ -102,16 +130,13 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
{
int ret;
- if (notifier->ops->bound) {
- ret = notifier->ops->bound(notifier, sd, asd);
- if (ret < 0)
- return ret;
- }
+ ret = v4l2_async_notifier_call_bound(notifier, sd, asd);
+ if (ret < 0)
+ return ret;
ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
if (ret < 0) {
- if (notifier->ops->unbind)
- notifier->ops->unbind(notifier, sd, asd);
+ v4l2_async_notifier_call_unbind(notifier, sd, asd);
return ret;
}
@@ -140,8 +165,7 @@ static void v4l2_async_notifier_unbind_all_subdevs(
struct v4l2_subdev *sd, *tmp;
list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
- if (notifier->ops->unbind)
- notifier->ops->unbind(notifier, sd, sd->asd);
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd);
list_move(&sd->async_list, &subdev_list);
@@ -198,8 +222,8 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
}
}
- if (list_empty(¬ifier->waiting) && notifier->ops->complete) {
- ret = notifier->ops->complete(notifier);
+ if (list_empty(¬ifier->waiting)) {
+ ret = v4l2_async_notifier_call_complete(notifier);
if (ret)
goto err_complete;
}
@@ -296,10 +320,10 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
if (ret)
goto err_unlock;
- if (!list_empty(¬ifier->waiting) || !notifier->ops->complete)
+ if (!list_empty(¬ifier->waiting))
goto out_unlock;
- ret = notifier->ops->complete(notifier);
+ ret = v4l2_async_notifier_call_complete(notifier);
if (ret)
goto err_cleanup;
@@ -315,8 +339,7 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
return 0;
err_cleanup:
- if (notifier->ops->unbind)
- notifier->ops->unbind(notifier, sd, sd->asd);
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd);
err_unlock:
@@ -335,8 +358,7 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
list_add(&sd->asd->list, ¬ifier->waiting);
- if (notifier->ops->unbind)
- notifier->ops->unbind(notifier, sd, sd->asd);
+ v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
}
v4l2_async_cleanup(sd);
--
2.11.0
^ permalink raw reply related
* [PATCH v15 12/32] omap3isp: Print the name of the entity where no source pads could be found
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
If no source pads are found in an entity, print the name of the entity.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
drivers/media/platform/omap3isp/isp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 4afd7ba4fad6..35687c9707e0 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -1669,8 +1669,8 @@ static int isp_link_entity(
break;
}
if (i == entity->num_pads) {
- dev_err(isp->dev, "%s: no source pad in external entity\n",
- __func__);
+ dev_err(isp->dev, "%s: no source pad in external entity %s\n",
+ __func__, entity->name);
return -EINVAL;
}
--
2.11.0
^ permalink raw reply related
* [PATCH v15 17/32] v4l: async: Prepare for async sub-device notifiers
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Refactor the V4L2 async framework a little in preparation for async
sub-device notifiers. This avoids making some structural changes in the
patch actually implementing sub-device notifiers, making that patch easier
to review.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-async.c | 70 ++++++++++++++++++++++++++----------
1 file changed, 51 insertions(+), 19 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 1b536d68cedf..1812310746d9 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -125,12 +125,13 @@ static struct v4l2_async_subdev *v4l2_async_find_match(
}
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
+ struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
int ret;
- ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0)
return ret;
@@ -151,6 +152,31 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
return 0;
}
+/* Test all async sub-devices in a notifier for a match. */
+static int v4l2_async_notifier_try_all_subdevs(
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
+ struct v4l2_subdev *sd, *tmp;
+
+ list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
+ struct v4l2_async_subdev *asd;
+ int ret;
+
+ asd = v4l2_async_find_match(notifier, sd);
+ if (!asd)
+ continue;
+
+ ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
+ if (ret < 0) {
+ mutex_unlock(&list_lock);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static void v4l2_async_cleanup(struct v4l2_subdev *sd)
{
v4l2_device_unregister_subdev(sd);
@@ -172,18 +198,15 @@ static void v4l2_async_notifier_unbind_all_subdevs(
}
}
-int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
- struct v4l2_async_notifier *notifier)
+static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
{
- struct v4l2_subdev *sd, *tmp;
struct v4l2_async_subdev *asd;
int ret;
int i;
- if (!v4l2_dev || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
+ if (notifier->num_subdevs > V4L2_MAX_SUBDEVS)
return -EINVAL;
- notifier->v4l2_dev = v4l2_dev;
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
@@ -216,18 +239,10 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
mutex_lock(&list_lock);
- list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
- int ret;
-
- asd = v4l2_async_find_match(notifier, sd);
- if (!asd)
- continue;
-
- ret = v4l2_async_match_notify(notifier, sd, asd);
- if (ret < 0) {
- mutex_unlock(&list_lock);
- return ret;
- }
+ ret = v4l2_async_notifier_try_all_subdevs(notifier);
+ if (ret) {
+ mutex_unlock(&list_lock);
+ return ret;
}
if (list_empty(¬ifier->waiting)) {
@@ -250,6 +265,22 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
return ret;
}
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+ struct v4l2_async_notifier *notifier)
+{
+ int ret;
+ if (WARN_ON(!v4l2_dev))
+ return -EINVAL;
+
+ notifier->v4l2_dev = v4l2_dev;
+
+ ret = __v4l2_async_notifier_register(notifier);
+ if (ret)
+ notifier->v4l2_dev = NULL;
+
+ return ret;
+}
EXPORT_SYMBOL(v4l2_async_notifier_register);
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
@@ -324,7 +355,8 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
if (!asd)
continue;
- ret = v4l2_async_match_notify(notifier, sd, asd);
+ ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd,
+ asd);
if (ret)
goto err_unlock;
--
2.11.0
^ permalink raw reply related
* [PATCH v15 10/32] rcar-vin: Use generic parser for parsing fwnode endpoints
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Instead of using a custom driver implementation, use
v4l2_async_notifier_parse_fwnode_endpoints() to parse the fwnode endpoints
of the device.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
drivers/media/platform/rcar-vin/rcar-core.c | 107 +++++++++-------------------
drivers/media/platform/rcar-vin/rcar-dma.c | 10 +--
drivers/media/platform/rcar-vin/rcar-v4l2.c | 14 ++--
drivers/media/platform/rcar-vin/rcar-vin.h | 4 +-
4 files changed, 46 insertions(+), 89 deletions(-)
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 142de447aaaa..380288658601 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-fwnode.h>
#include "rcar-vin.h"
@@ -77,14 +78,14 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
int ret;
/* Verify subdevices mbus format */
- if (!rvin_mbus_supported(&vin->digital)) {
+ if (!rvin_mbus_supported(vin->digital)) {
vin_err(vin, "Unsupported media bus format for %s\n",
- vin->digital.subdev->name);
+ vin->digital->subdev->name);
return -EINVAL;
}
vin_dbg(vin, "Found media bus format for %s: %d\n",
- vin->digital.subdev->name, vin->digital.code);
+ vin->digital->subdev->name, vin->digital->code);
ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
if (ret < 0) {
@@ -103,7 +104,7 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
rvin_v4l2_remove(vin);
- vin->digital.subdev = NULL;
+ vin->digital->subdev = NULL;
}
static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
@@ -120,117 +121,71 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
if (ret < 0)
return ret;
- vin->digital.source_pad = ret;
+ vin->digital->source_pad = ret;
ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
- vin->digital.sink_pad = ret < 0 ? 0 : ret;
+ vin->digital->sink_pad = ret < 0 ? 0 : ret;
- vin->digital.subdev = subdev;
+ vin->digital->subdev = subdev;
vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
- subdev->name, vin->digital.source_pad,
- vin->digital.sink_pad);
+ subdev->name, vin->digital->source_pad,
+ vin->digital->sink_pad);
return 0;
}
-static int rvin_digitial_parse_v4l2(struct rvin_dev *vin,
- struct device_node *ep,
- struct v4l2_mbus_config *mbus_cfg)
+static int rvin_digital_parse_v4l2(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
{
- struct v4l2_fwnode_endpoint v4l2_ep;
- int ret;
+ struct rvin_dev *vin = dev_get_drvdata(dev);
+ struct rvin_graph_entity *rvge =
+ container_of(asd, struct rvin_graph_entity, asd);
- ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep);
- if (ret) {
- vin_err(vin, "Could not parse v4l2 endpoint\n");
- return -EINVAL;
- }
+ if (vep->base.port || vep->base.id)
+ return -ENOTCONN;
- mbus_cfg->type = v4l2_ep.bus_type;
+ rvge->mbus_cfg.type = vep->bus_type;
- switch (mbus_cfg->type) {
+ switch (rvge->mbus_cfg.type) {
case V4L2_MBUS_PARALLEL:
vin_dbg(vin, "Found PARALLEL media bus\n");
- mbus_cfg->flags = v4l2_ep.bus.parallel.flags;
+ rvge->mbus_cfg.flags = vep->bus.parallel.flags;
break;
case V4L2_MBUS_BT656:
vin_dbg(vin, "Found BT656 media bus\n");
- mbus_cfg->flags = 0;
+ rvge->mbus_cfg.flags = 0;
break;
default:
vin_err(vin, "Unknown media bus type\n");
return -EINVAL;
}
- return 0;
-}
-
-static int rvin_digital_graph_parse(struct rvin_dev *vin)
-{
- struct device_node *ep, *np;
- int ret;
-
- vin->digital.asd.match.fwnode.fwnode = NULL;
- vin->digital.subdev = NULL;
-
- /*
- * Port 0 id 0 is local digital input, try to get it.
- * Not all instances can or will have this, that is OK
- */
- ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0);
- if (!ep)
- return 0;
-
- np = of_graph_get_remote_port_parent(ep);
- if (!np) {
- vin_err(vin, "No remote parent for digital input\n");
- of_node_put(ep);
- return -EINVAL;
- }
- of_node_put(np);
-
- ret = rvin_digitial_parse_v4l2(vin, ep, &vin->digital.mbus_cfg);
- of_node_put(ep);
- if (ret)
- return ret;
-
- vin->digital.asd.match.fwnode.fwnode = of_fwnode_handle(np);
- vin->digital.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ vin->digital = rvge;
return 0;
}
static int rvin_digital_graph_init(struct rvin_dev *vin)
{
- struct v4l2_async_subdev **subdevs = NULL;
int ret;
- ret = rvin_digital_graph_parse(vin);
+ ret = v4l2_async_notifier_parse_fwnode_endpoints(
+ vin->dev, &vin->notifier,
+ sizeof(struct rvin_graph_entity), rvin_digital_parse_v4l2);
if (ret)
return ret;
- if (!vin->digital.asd.match.fwnode.fwnode) {
- vin_dbg(vin, "No digital subdevice found\n");
+ if (!vin->digital)
return -ENODEV;
- }
-
- /* Register the subdevices notifier. */
- subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL);
- if (subdevs == NULL)
- return -ENOMEM;
-
- subdevs[0] = &vin->digital.asd;
vin_dbg(vin, "Found digital subdevice %pOF\n",
- to_of_node(subdevs[0]->match.fwnode.fwnode));
+ to_of_node(vin->digital->asd.match.fwnode.fwnode));
- vin->notifier.num_subdevs = 1;
- vin->notifier.subdevs = subdevs;
vin->notifier.bound = rvin_digital_notify_bound;
vin->notifier.unbind = rvin_digital_notify_unbind;
vin->notifier.complete = rvin_digital_notify_complete;
-
ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
if (ret < 0) {
vin_err(vin, "Notifier registration failed\n");
@@ -290,6 +245,8 @@ static int rcar_vin_probe(struct platform_device *pdev)
if (ret)
return ret;
+ platform_set_drvdata(pdev, vin);
+
ret = rvin_digital_graph_init(vin);
if (ret < 0)
goto error;
@@ -297,11 +254,10 @@ static int rcar_vin_probe(struct platform_device *pdev)
pm_suspend_ignore_children(&pdev->dev, true);
pm_runtime_enable(&pdev->dev);
- platform_set_drvdata(pdev, vin);
-
return 0;
error:
rvin_dma_remove(vin);
+ v4l2_async_notifier_cleanup(&vin->notifier);
return ret;
}
@@ -313,6 +269,7 @@ static int rcar_vin_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
v4l2_async_notifier_unregister(&vin->notifier);
+ v4l2_async_notifier_cleanup(&vin->notifier);
rvin_dma_remove(vin);
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index b136844499f6..23fdff7a7370 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -183,7 +183,7 @@ static int rvin_setup(struct rvin_dev *vin)
/*
* Input interface
*/
- switch (vin->digital.code) {
+ switch (vin->digital->code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
/* BT.601/BT.1358 16bit YCbCr422 */
vnmc |= VNMC_INF_YUV16;
@@ -191,7 +191,7 @@ static int rvin_setup(struct rvin_dev *vin)
break;
case MEDIA_BUS_FMT_UYVY8_2X8:
/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
- vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ?
+ vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ?
VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
input_is_yuv = true;
break;
@@ -200,7 +200,7 @@ static int rvin_setup(struct rvin_dev *vin)
break;
case MEDIA_BUS_FMT_UYVY10_2X10:
/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
- vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ?
+ vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ?
VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
input_is_yuv = true;
break;
@@ -212,11 +212,11 @@ static int rvin_setup(struct rvin_dev *vin)
dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
/* Hsync Signal Polarity Select */
- if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+ if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
dmr2 |= VNDMR2_HPS;
/* Vsync Signal Polarity Select */
- if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+ if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
dmr2 |= VNDMR2_VPS;
/*
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index dd37ea811680..b479b882da12 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -111,7 +111,7 @@ static int rvin_reset_format(struct rvin_dev *vin)
struct v4l2_mbus_framefmt *mf = &fmt.format;
int ret;
- fmt.pad = vin->digital.source_pad;
+ fmt.pad = vin->digital->source_pad;
ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
if (ret)
@@ -172,13 +172,13 @@ static int __rvin_try_format_source(struct rvin_dev *vin,
sd = vin_to_source(vin);
- v4l2_fill_mbus_format(&format.format, pix, vin->digital.code);
+ v4l2_fill_mbus_format(&format.format, pix, vin->digital->code);
pad_cfg = v4l2_subdev_alloc_pad_config(sd);
if (pad_cfg == NULL)
return -ENOMEM;
- format.pad = vin->digital.source_pad;
+ format.pad = vin->digital->source_pad;
field = pix->field;
@@ -555,7 +555,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
if (timings->pad)
return -EINVAL;
- timings->pad = vin->digital.sink_pad;
+ timings->pad = vin->digital->sink_pad;
ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
@@ -607,7 +607,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
if (cap->pad)
return -EINVAL;
- cap->pad = vin->digital.sink_pad;
+ cap->pad = vin->digital->sink_pad;
ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
@@ -625,7 +625,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
if (edid->pad)
return -EINVAL;
- edid->pad = vin->digital.sink_pad;
+ edid->pad = vin->digital->sink_pad;
ret = v4l2_subdev_call(sd, pad, get_edid, edid);
@@ -643,7 +643,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
if (edid->pad)
return -EINVAL;
- edid->pad = vin->digital.sink_pad;
+ edid->pad = vin->digital->sink_pad;
ret = v4l2_subdev_call(sd, pad, set_edid, edid);
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 9bfb5a7c4dc4..5382078143fb 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -126,7 +126,7 @@ struct rvin_dev {
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_async_notifier notifier;
- struct rvin_graph_entity digital;
+ struct rvin_graph_entity *digital;
struct mutex lock;
struct vb2_queue queue;
@@ -145,7 +145,7 @@ struct rvin_dev {
struct v4l2_rect compose;
};
-#define vin_to_source(vin) vin->digital.subdev
+#define vin_to_source(vin) ((vin)->digital->subdev)
/* Debug */
#define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg)
--
2.11.0
^ permalink raw reply related
* [PATCH v15 16/32] v4l: async: Allow async notifier register call succeed with no subdevs
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
The information on how many async sub-devices would be bindable to a
notifier is typically dependent on information from platform firmware and
it's not driver's business to be aware of that.
Many V4L2 main drivers are perfectly usable (and useful) without async
sub-devices and so if there aren't any around, just proceed call the
notifier's complete callback immediately without registering the notifier
itself.
If a driver needs to check whether there are async sub-devices available,
it can be done by inspecting the notifier's num_subdevs field which tells
the number of async sub-devices.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-async.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 46db85685894..1b536d68cedf 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -180,14 +180,22 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
int ret;
int i;
- if (!v4l2_dev || !notifier->num_subdevs ||
- notifier->num_subdevs > V4L2_MAX_SUBDEVS)
+ if (!v4l2_dev || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
+ if (!notifier->num_subdevs) {
+ int ret;
+
+ ret = v4l2_async_notifier_call_complete(notifier);
+ notifier->v4l2_dev = NULL;
+
+ return ret;
+ }
+
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
--
2.11.0
^ permalink raw reply related
* [PATCH v15 11/32] omap3isp: Fix check for our own sub-devices
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
We only want to link sub-devices that were bound to the async notifier the
isp driver registered but there may be other sub-devices in the
v4l2_device as well. Check for the correct async notifier.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
drivers/media/platform/omap3isp/isp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 97a5206b6ddc..4afd7ba4fad6 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -2155,7 +2155,7 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
return ret;
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (!sd->asd)
+ if (sd->notifier != &isp->notifier)
continue;
ret = isp_link_entity(isp, &sd->entity,
--
2.11.0
^ permalink raw reply related
* [PATCH v15 09/32] omap3isp: Use generic parser for parsing fwnode endpoints
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Instead of using a custom driver implementation, use
v4l2_async_notifier_parse_fwnode_endpoints() to parse the fwnode endpoints
of the device.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/platform/omap3isp/isp.c | 121 +++++++++++-----------------------
drivers/media/platform/omap3isp/isp.h | 5 +-
2 files changed, 40 insertions(+), 86 deletions(-)
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 1a428fe9f070..97a5206b6ddc 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -2001,6 +2001,7 @@ static int isp_remove(struct platform_device *pdev)
__omap3isp_put(isp, false);
media_entity_enum_cleanup(&isp->crashed);
+ v4l2_async_notifier_cleanup(&isp->notifier);
return 0;
}
@@ -2011,44 +2012,41 @@ enum isp_of_phy {
ISP_OF_PHY_CSIPHY2,
};
-static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,
- struct isp_async_subdev *isd)
+static int isp_fwnode_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
{
+ struct isp_async_subdev *isd =
+ container_of(asd, struct isp_async_subdev, asd);
struct isp_bus_cfg *buscfg = &isd->bus;
- struct v4l2_fwnode_endpoint vep;
- unsigned int i;
- int ret;
bool csi1 = false;
-
- ret = v4l2_fwnode_endpoint_parse(fwnode, &vep);
- if (ret)
- return ret;
+ unsigned int i;
dev_dbg(dev, "parsing endpoint %pOF, interface %u\n",
- to_of_node(fwnode), vep.base.port);
+ to_of_node(vep->base.local_fwnode), vep->base.port);
- switch (vep.base.port) {
+ switch (vep->base.port) {
case ISP_OF_PHY_PARALLEL:
buscfg->interface = ISP_INTERFACE_PARALLEL;
buscfg->bus.parallel.data_lane_shift =
- vep.bus.parallel.data_shift;
+ vep->bus.parallel.data_shift;
buscfg->bus.parallel.clk_pol =
- !!(vep.bus.parallel.flags
+ !!(vep->bus.parallel.flags
& V4L2_MBUS_PCLK_SAMPLE_FALLING);
buscfg->bus.parallel.hs_pol =
- !!(vep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ !!(vep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW);
buscfg->bus.parallel.vs_pol =
- !!(vep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ !!(vep->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW);
buscfg->bus.parallel.fld_pol =
- !!(vep.bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW);
+ !!(vep->bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW);
buscfg->bus.parallel.data_pol =
- !!(vep.bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW);
- buscfg->bus.parallel.bt656 = vep.bus_type == V4L2_MBUS_BT656;
+ !!(vep->bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW);
+ buscfg->bus.parallel.bt656 = vep->bus_type == V4L2_MBUS_BT656;
break;
case ISP_OF_PHY_CSIPHY1:
case ISP_OF_PHY_CSIPHY2:
- switch (vep.bus_type) {
+ switch (vep->bus_type) {
case V4L2_MBUS_CCP2:
case V4L2_MBUS_CSI1:
dev_dbg(dev, "CSI-1/CCP-2 configuration\n");
@@ -2060,11 +2058,11 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,
break;
default:
dev_err(dev, "unsupported bus type %u\n",
- vep.bus_type);
+ vep->bus_type);
return -EINVAL;
}
- switch (vep.base.port) {
+ switch (vep->base.port) {
case ISP_OF_PHY_CSIPHY1:
if (csi1)
buscfg->interface = ISP_INTERFACE_CCP2B_PHY1;
@@ -2080,47 +2078,47 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,
}
if (csi1) {
buscfg->bus.ccp2.lanecfg.clk.pos =
- vep.bus.mipi_csi1.clock_lane;
+ vep->bus.mipi_csi1.clock_lane;
buscfg->bus.ccp2.lanecfg.clk.pol =
- vep.bus.mipi_csi1.lane_polarity[0];
+ vep->bus.mipi_csi1.lane_polarity[0];
dev_dbg(dev, "clock lane polarity %u, pos %u\n",
buscfg->bus.ccp2.lanecfg.clk.pol,
buscfg->bus.ccp2.lanecfg.clk.pos);
buscfg->bus.ccp2.lanecfg.data[0].pos =
- vep.bus.mipi_csi1.data_lane;
+ vep->bus.mipi_csi1.data_lane;
buscfg->bus.ccp2.lanecfg.data[0].pol =
- vep.bus.mipi_csi1.lane_polarity[1];
+ vep->bus.mipi_csi1.lane_polarity[1];
dev_dbg(dev, "data lane polarity %u, pos %u\n",
buscfg->bus.ccp2.lanecfg.data[0].pol,
buscfg->bus.ccp2.lanecfg.data[0].pos);
buscfg->bus.ccp2.strobe_clk_pol =
- vep.bus.mipi_csi1.clock_inv;
- buscfg->bus.ccp2.phy_layer = vep.bus.mipi_csi1.strobe;
+ vep->bus.mipi_csi1.clock_inv;
+ buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe;
buscfg->bus.ccp2.ccp2_mode =
- vep.bus_type == V4L2_MBUS_CCP2;
+ vep->bus_type == V4L2_MBUS_CCP2;
buscfg->bus.ccp2.vp_clk_pol = 1;
buscfg->bus.ccp2.crc = 1;
} else {
buscfg->bus.csi2.lanecfg.clk.pos =
- vep.bus.mipi_csi2.clock_lane;
+ vep->bus.mipi_csi2.clock_lane;
buscfg->bus.csi2.lanecfg.clk.pol =
- vep.bus.mipi_csi2.lane_polarities[0];
+ vep->bus.mipi_csi2.lane_polarities[0];
dev_dbg(dev, "clock lane polarity %u, pos %u\n",
buscfg->bus.csi2.lanecfg.clk.pol,
buscfg->bus.csi2.lanecfg.clk.pos);
buscfg->bus.csi2.num_data_lanes =
- vep.bus.mipi_csi2.num_data_lanes;
+ vep->bus.mipi_csi2.num_data_lanes;
for (i = 0; i < buscfg->bus.csi2.num_data_lanes; i++) {
buscfg->bus.csi2.lanecfg.data[i].pos =
- vep.bus.mipi_csi2.data_lanes[i];
+ vep->bus.mipi_csi2.data_lanes[i];
buscfg->bus.csi2.lanecfg.data[i].pol =
- vep.bus.mipi_csi2.lane_polarities[i + 1];
+ vep->bus.mipi_csi2.lane_polarities[i + 1];
dev_dbg(dev,
"data lane %u polarity %u, pos %u\n", i,
buscfg->bus.csi2.lanecfg.data[i].pol,
@@ -2137,57 +2135,13 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,
default:
dev_warn(dev, "%pOF: invalid interface %u\n",
- to_of_node(fwnode), vep.base.port);
+ to_of_node(vep->base.local_fwnode), vep->base.port);
return -EINVAL;
}
return 0;
}
-static int isp_fwnodes_parse(struct device *dev,
- struct v4l2_async_notifier *notifier)
-{
- struct fwnode_handle *fwnode = NULL;
-
- notifier->subdevs = devm_kcalloc(
- dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), GFP_KERNEL);
- if (!notifier->subdevs)
- return -ENOMEM;
-
- while (notifier->num_subdevs < ISP_MAX_SUBDEVS &&
- (fwnode = fwnode_graph_get_next_endpoint(
- of_fwnode_handle(dev->of_node), fwnode))) {
- struct isp_async_subdev *isd;
-
- isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL);
- if (!isd)
- goto error;
-
- if (isp_fwnode_parse(dev, fwnode, isd)) {
- devm_kfree(dev, isd);
- continue;
- }
-
- notifier->subdevs[notifier->num_subdevs] = &isd->asd;
-
- isd->asd.match.fwnode.fwnode =
- fwnode_graph_get_remote_port_parent(fwnode);
- if (!isd->asd.match.fwnode.fwnode) {
- dev_warn(dev, "bad remote port parent\n");
- goto error;
- }
-
- isd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
- notifier->num_subdevs++;
- }
-
- return notifier->num_subdevs;
-
-error:
- fwnode_handle_put(fwnode);
- return -EINVAL;
-}
-
static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
{
struct isp_device *isp = container_of(async, struct isp_device,
@@ -2256,15 +2210,17 @@ static int isp_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = isp_fwnodes_parse(&pdev->dev, &isp->notifier);
- if (ret < 0)
- return ret;
-
isp->autoidle = autoidle;
mutex_init(&isp->isp_mutex);
spin_lock_init(&isp->stat_lock);
+ ret = v4l2_async_notifier_parse_fwnode_endpoints(
+ &pdev->dev, &isp->notifier, sizeof(struct isp_async_subdev),
+ isp_fwnode_parse);
+ if (ret < 0)
+ goto error;
+
isp->dev = &pdev->dev;
isp->ref_count = 0;
@@ -2406,6 +2362,7 @@ static int isp_probe(struct platform_device *pdev)
isp_xclk_cleanup(isp);
__omap3isp_put(isp, false);
error:
+ v4l2_async_notifier_cleanup(&isp->notifier);
mutex_destroy(&isp->isp_mutex);
return ret;
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index e528df6efc09..8b9043db94b3 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -220,14 +220,11 @@ struct isp_device {
unsigned int sbl_resources;
unsigned int subclk_resources;
-
-#define ISP_MAX_SUBDEVS 8
- struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS];
};
struct isp_async_subdev {
- struct isp_bus_cfg bus;
struct v4l2_async_subdev asd;
+ struct isp_bus_cfg bus;
};
#define v4l2_subdev_to_bus_cfg(sd) \
--
2.11.0
^ permalink raw reply related
* [PATCH v15 15/32] v4l: async: Register sub-devices before calling bound callback
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Register the sub-device before calling the notifier's bound callback.
Doing this the other way around is problematic as the struct v4l2_device
has not assigned for the sub-device yet and may be required by the bound
callback.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-async.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index e170682dae78..46db85685894 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -130,13 +130,13 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
{
int ret;
- ret = v4l2_async_notifier_call_bound(notifier, sd, asd);
+ ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
if (ret < 0)
return ret;
- ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
+ ret = v4l2_async_notifier_call_bound(notifier, sd, asd);
if (ret < 0) {
- v4l2_async_notifier_call_unbind(notifier, sd, asd);
+ v4l2_device_unregister_subdev(sd);
return ret;
}
--
2.11.0
^ permalink raw reply related
* Re: mdadm: Patch to restrict --size when shrinking unless forced
From: NeilBrown @ 2017-10-04 21:50 UTC (permalink / raw)
To: John Stoffel; +Cc: Eli Ben-Shoshan, Jes.Sorensen, linux-raid
In-Reply-To: <22997.8664.67459.119616@quad.stoffel.home>
[-- Attachment #1: Type: text/plain, Size: 5561 bytes --]
On Wed, Oct 04 2017, John Stoffel wrote:
> Since Eli had such a horrible experience where he shrunk the
> individual component raid device size, instead of growing the overall
> raid by adding a device, I came up with this hacky patch to warn you
> when you are about to shoot yourself in the foot.
>
> The idea is it will warn you and exit unless you pass in the --force
> (or -f) switch when using the command. For example, on a set of loop
> devices:
>
> # cat /proc/mdstat
> Personalities : [linear] [raid0] [raid1] [raid10] [raid6] [raid5]
> [raid4] [multipath] [faulty]
> md99 : active raid6 loop4p1[4] loop3p1[3] loop2p1[2] loop1p1[1]
> loop0p1[0]
> 606720 blocks super 1.2 level 6, 512k chunk, algorithm 2 [5/5]
> [UUUUU]
>
> # ./mdadm --grow /dev/md99 --size 128
> mdadm: Cannot set device size smaller than current component_size of /dev/md99 array. Use -f to force change.
>
> # ./mdadm --grow /dev/md99 --size 128 -f
> mdadm: component size of /dev/md99 has been set to 0K
>
I'm not sure I like this.
The reason that mdadm will quietly accept a size change like this is
that it is trivial to revert - just set the same to a big number and all
your data is still there.
Eli's problem was that he made a harmless mistake, realized that he had
made a mistake, but didn't address the mistake before continuing!
If you really want to make this a two-step process, an approach that
would be more consistent with other aspects of mdadm is to require that
--array-size be reduced first. i.e. setting --size mustn't reduce the
size of the array.
I think that separating the two steps (resize array, resize component)
gives the user a better picture of what is happening, where as requiring
-f just causes people to use -f more often.
Thanks,
NeilBrown
>
> I suspect I could do a better job of showing the original component
> size, so that you have a chance of recovering even then.
>
> But the patch:
>
> diff --git a/Grow.c b/Grow.c
> index 455c5f9..701590f 100755
> --- a/Grow.c
> +++ b/Grow.c
> @@ -1561,7 +1561,7 @@ static int reshape_container(char *container, char *devname,
> char *backup_file, int verbose,
> int forked, int restart, int freeze_reshape);
>
> -int Grow_reshape(char *devname, int fd,
> +int Grow_reshape(char *devname, int fd, int force,
> struct mddev_dev *devlist,
> unsigned long long data_offset,
> struct context *c, struct shape *s)
> @@ -1574,6 +1574,7 @@ int Grow_reshape(char *devname, int fd,
> * requested everything (if kernel supports freezing - 2.6.30).
> * The steps are:
> * - change size (i.e. component_size)
> + * - when shrinking, you must force the change
> * - change level
> * - change layout/chunksize/ndisks
> *
> @@ -1617,6 +1618,11 @@ int Grow_reshape(char *devname, int fd,
> return 1;
> }
>
> + if ((s->size < (unsigned)array.size) && !force) {
> + pr_err("Cannot set device size smaller than current component_size of %s array. Use -f to force change.\n",devname);
> + return 1;
> + }
> +
> if (s->raiddisks && s->raiddisks < array.raid_disks && array.level > 1 &&
> get_linux_version() < 2006032 &&
> !check_env("MDADM_FORCE_FEWER")) {
> diff --git a/ReadMe.c b/ReadMe.c
> index 50d3807..46988ae 100644
> --- a/ReadMe.c
> +++ b/ReadMe.c
> @@ -203,6 +203,7 @@ struct option long_options[] = {
> {"invalid-backup",0,0,InvalidBackup},
> {"array-size", 1, 0, 'Z'},
> {"continue", 0, 0, Continue},
> + {"force", 0, 0, Force},
>
> /* For Incremental */
> {"rebuild-map", 0, 0, RebuildMapOpt},
> @@ -563,6 +564,7 @@ char Help_grow[] =
> " : This is useful if all devices have been replaced\n"
> " : with larger devices. Value is in Kilobytes, or\n"
> " : the special word 'max' meaning 'as large as possible'.\n"
> +" --force -f : Override normal checks and be more forceful\n"
> " --assume-clean : When increasing the --size, this flag will avoid\n"
> " : a resync of the new space\n"
> " --chunk= -c : Change the chunksize of the array\n"
> diff --git a/mdadm.c b/mdadm.c
> index c3a265b..821658a 100644
> --- a/mdadm.c
> +++ b/mdadm.c
> @@ -1617,7 +1617,7 @@ int main(int argc, char *argv[])
> else if (s.size > 0 || s.raiddisks || s.layout_str ||
> s.chunk != 0 || s.level != UnSet ||
> data_offset != INVALID_SECTORS) {
> - rv = Grow_reshape(devlist->devname, mdfd,
> + rv = Grow_reshape(devlist->devname, mdfd, c.force,
> devlist->next,
> data_offset, &c, &s);
> } else if (array_size == 0)
> diff --git a/mdadm.h b/mdadm.h
> index 71b8afb..9e00f05 100644
> --- a/mdadm.h
> +++ b/mdadm.h
> @@ -1300,7 +1300,7 @@ extern int autodetect(void);
> extern int Grow_Add_device(char *devname, int fd, char *newdev);
> extern int Grow_addbitmap(char *devname, int fd,
> struct context *c, struct shape *s);
> -extern int Grow_reshape(char *devname, int fd,
> +extern int Grow_reshape(char *devname, int fd, int force,
> struct mddev_dev *devlist,
> unsigned long long data_offset,
> struct context *c, struct shape *s);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-raid" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* [PATCH v15 04/32] v4l: async: Fix notifier complete callback error handling
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
The notifier complete callback may return an error. This error code was
simply returned to the caller but never handled properly.
Move calling the complete callback function to the caller from
v4l2_async_test_notify and undo the work that was done either in async
sub-device or async notifier registration.
Reported-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
drivers/media/v4l2-core/v4l2-async.c | 78 +++++++++++++++++++++++++++---------
1 file changed, 60 insertions(+), 18 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index ca281438a0ae..4924481451ca 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -122,9 +122,6 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
/* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, ¬ifier->done);
- if (list_empty(¬ifier->waiting) && notifier->complete)
- return notifier->complete(notifier);
-
return 0;
}
@@ -136,11 +133,27 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd)
sd->asd = NULL;
}
+static void v4l2_async_notifier_unbind_all_subdevs(
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_subdev *sd, *tmp;
+
+ list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asd);
+
+ v4l2_async_cleanup(sd);
+
+ list_move(&sd->async_list, &subdev_list);
+ }
+}
+
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
struct v4l2_subdev *sd, *tmp;
struct v4l2_async_subdev *asd;
+ int ret;
int i;
if (!v4l2_dev || !notifier->num_subdevs ||
@@ -185,19 +198,30 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
}
}
+ if (list_empty(¬ifier->waiting) && notifier->complete) {
+ ret = notifier->complete(notifier);
+ if (ret)
+ goto err_complete;
+ }
+
/* Keep also completed notifiers on the list */
list_add(¬ifier->list, ¬ifier_list);
mutex_unlock(&list_lock);
return 0;
+
+err_complete:
+ v4l2_async_notifier_unbind_all_subdevs(notifier);
+
+ mutex_unlock(&list_lock);
+
+ return ret;
}
EXPORT_SYMBOL(v4l2_async_notifier_register);
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{
- struct v4l2_subdev *sd, *tmp;
-
if (!notifier->v4l2_dev)
return;
@@ -205,14 +229,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
list_del(¬ifier->list);
- list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
- if (notifier->unbind)
- notifier->unbind(notifier, sd, sd->asd);
-
- v4l2_async_cleanup(sd);
-
- list_move(&sd->async_list, &subdev_list);
- }
+ v4l2_async_notifier_unbind_all_subdevs(notifier);
mutex_unlock(&list_lock);
@@ -223,6 +240,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister);
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
struct v4l2_async_notifier *notifier;
+ int ret;
/*
* No reference taken. The reference is held by the device
@@ -238,19 +256,43 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
list_for_each_entry(notifier, ¬ifier_list, list) {
struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
- if (asd) {
- int ret = v4l2_async_test_notify(notifier, sd, asd);
- mutex_unlock(&list_lock);
- return ret;
- }
+ int ret;
+
+ if (!asd)
+ continue;
+
+ ret = v4l2_async_test_notify(notifier, sd, asd);
+ if (ret)
+ goto err_unlock;
+
+ if (!list_empty(¬ifier->waiting) || !notifier->complete)
+ goto out_unlock;
+
+ ret = notifier->complete(notifier);
+ if (ret)
+ goto err_cleanup;
+
+ goto out_unlock;
}
/* None matched, wait for hot-plugging */
list_add(&sd->async_list, &subdev_list);
+out_unlock:
mutex_unlock(&list_lock);
return 0;
+
+err_cleanup:
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asd);
+
+ v4l2_async_cleanup(sd);
+
+err_unlock:
+ mutex_unlock(&list_lock);
+
+ return ret;
}
EXPORT_SYMBOL(v4l2_async_register_subdev);
--
2.11.0
^ permalink raw reply related
* [PATCH v15 07/32] v4l: async: Add V4L2 async documentation to the documentation build
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
The V4L2 async wasn't part of the documentation build. Fix this.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Documentation/media/kapi/v4l2-async.rst | 3 +++
Documentation/media/kapi/v4l2-core.rst | 1 +
2 files changed, 4 insertions(+)
create mode 100644 Documentation/media/kapi/v4l2-async.rst
diff --git a/Documentation/media/kapi/v4l2-async.rst b/Documentation/media/kapi/v4l2-async.rst
new file mode 100644
index 000000000000..523ff9eb09a0
--- /dev/null
+++ b/Documentation/media/kapi/v4l2-async.rst
@@ -0,0 +1,3 @@
+V4L2 async kAPI
+^^^^^^^^^^^^^^^
+.. kernel-doc:: include/media/v4l2-async.h
diff --git a/Documentation/media/kapi/v4l2-core.rst b/Documentation/media/kapi/v4l2-core.rst
index c7434f38fd9c..5cf292037a48 100644
--- a/Documentation/media/kapi/v4l2-core.rst
+++ b/Documentation/media/kapi/v4l2-core.rst
@@ -19,6 +19,7 @@ Video4Linux devices
v4l2-mc
v4l2-mediabus
v4l2-mem2mem
+ v4l2-async
v4l2-fwnode
v4l2-rect
v4l2-tuner
--
2.11.0
^ permalink raw reply related
* [PATCH v15 05/32] v4l: async: Correctly serialise async sub-device unregistration
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
The check whether an async sub-device is bound to a notifier was performed
without list_lock held, making it possible for another process to
unbind the async sub-device before the sub-device unregistration function
proceeds to take the lock.
Fix this by first acquiring the lock and then proceeding with the check.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
drivers/media/v4l2-core/v4l2-async.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 4924481451ca..cde2cf2ab4b0 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -298,20 +298,16 @@ EXPORT_SYMBOL(v4l2_async_register_subdev);
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
{
- struct v4l2_async_notifier *notifier = sd->notifier;
-
- if (!sd->asd) {
- if (!list_empty(&sd->async_list))
- v4l2_async_cleanup(sd);
- return;
- }
-
mutex_lock(&list_lock);
- list_add(&sd->asd->list, ¬ifier->waiting);
+ if (sd->asd) {
+ struct v4l2_async_notifier *notifier = sd->notifier;
- if (notifier->unbind)
- notifier->unbind(notifier, sd, sd->asd);
+ list_add(&sd->asd->list, ¬ifier->waiting);
+
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asd);
+ }
v4l2_async_cleanup(sd);
--
2.11.0
^ permalink raw reply related
* [PATCH v15 06/32] v4l: async: Use more intuitive names for internal functions
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Rename internal functions to make the names of the functions better
describe what they do.
Old name New name
v4l2_async_test_notify v4l2_async_match_notify
v4l2_async_belongs v4l2_async_find_match
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/v4l2-core/v4l2-async.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index cde2cf2ab4b0..8b84fea50c2a 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -60,8 +60,8 @@ static LIST_HEAD(subdev_list);
static LIST_HEAD(notifier_list);
static DEFINE_MUTEX(list_lock);
-static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd)
+static struct v4l2_async_subdev *v4l2_async_find_match(
+ struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd)
{
bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
struct v4l2_async_subdev *asd;
@@ -95,9 +95,9 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
return NULL;
}
-static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd,
- struct v4l2_async_subdev *asd)
+static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
{
int ret;
@@ -187,11 +187,11 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
int ret;
- asd = v4l2_async_belongs(notifier, sd);
+ asd = v4l2_async_find_match(notifier, sd);
if (!asd)
continue;
- ret = v4l2_async_test_notify(notifier, sd, asd);
+ ret = v4l2_async_match_notify(notifier, sd, asd);
if (ret < 0) {
mutex_unlock(&list_lock);
return ret;
@@ -255,13 +255,14 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
INIT_LIST_HEAD(&sd->async_list);
list_for_each_entry(notifier, ¬ifier_list, list) {
- struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
+ struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier,
+ sd);
int ret;
if (!asd)
continue;
- ret = v4l2_async_test_notify(notifier, sd, asd);
+ ret = v4l2_async_match_notify(notifier, sd, asd);
if (ret)
goto err_unlock;
--
2.11.0
^ permalink raw reply related
* Re: [PATCH] glib-2.0: Remove recommend shared-mime-info for MinGW
From: Alistair Francis @ 2017-10-04 21:50 UTC (permalink / raw)
To: Alistair Francis; +Cc: OE-core
In-Reply-To: <20170925225655.24585-1-alistair.francis@xilinx.com>
On Mon, Sep 25, 2017 at 3:56 PM, Alistair Francis
<alistair.francis@xilinx.com> wrote:
> Commit glib-2.0: recommend shared-mime-info
> (51e4f9ca5368af5cefa26f4ca50b282e858982f8) broke compilation when cross
> compiling for Windows. This patch removes the recommendation for
> shared-mime-info when using MinGW cross compile.
>
> Signed-off-by: Alistair Francis <alistair.francis@xilinx.com>
> Cc: Ross Burton <ross.burton@intel.com>
> Cc: Richard Purdie <richard.purdie@linuxfoundation.org>
Ping!
Thanks,
Alistair
> ---
> meta/recipes-core/glib-2.0/glib.inc | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/meta/recipes-core/glib-2.0/glib.inc b/meta/recipes-core/glib-2.0/glib.inc
> index ce15ccefc3..8434b7dae3 100644
> --- a/meta/recipes-core/glib-2.0/glib.inc
> +++ b/meta/recipes-core/glib-2.0/glib.inc
> @@ -74,6 +74,8 @@ FILES_${PN}-codegen = "${datadir}/glib-2.0/codegen/*.py \
> FILES_${PN}-utils = "${bindir}/*"
>
> RRECOMMENDS_${PN} += "shared-mime-info"
> +# When cross compiling for Windows we don't want to include this
> +RRECOMMENDS_${PN}_remove_mingw32 = "shared-mime-info"
>
> ARM_INSTRUCTION_SET_armv4 = "arm"
> ARM_INSTRUCTION_SET_armv5 = "arm"
> --
> 2.11.0
>
^ permalink raw reply
* [PATCH v15 03/32] v4l: async: fix unbind error in v4l2_async_notifier_unregister()
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre, Niklas Söderlund
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
The call to v4l2_async_cleanup() will set sd->asd to NULL so passing it to
notifier->unbind() have no effect and leaves the notifier confused. Call
the unbind() callback prior to cleaning up the subdevice to avoid this.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
drivers/media/v4l2-core/v4l2-async.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 21c748bf3a7b..ca281438a0ae 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -206,11 +206,11 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
list_del(¬ifier->list);
list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
- v4l2_async_cleanup(sd);
-
if (notifier->unbind)
notifier->unbind(notifier, sd, sd->asd);
+ v4l2_async_cleanup(sd);
+
list_move(&sd->async_list, &subdev_list);
}
@@ -268,11 +268,11 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
list_add(&sd->asd->list, ¬ifier->waiting);
- v4l2_async_cleanup(sd);
-
if (notifier->unbind)
notifier->unbind(notifier, sd, sd->asd);
+ v4l2_async_cleanup(sd);
+
mutex_unlock(&list_lock);
}
EXPORT_SYMBOL(v4l2_async_unregister_subdev);
--
2.11.0
^ permalink raw reply related
* [PATCH v15 02/32] v4l: async: Don't set sd->dev NULL in v4l2_async_cleanup
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
v4l2_async_cleanup() is called when the async sub-device is unbound from
the media device. As the pointer is set by the driver registering the
async sub-device, leave the pointer as set by the driver.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
drivers/media/v4l2-core/v4l2-async.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 60a1a50b9537..21c748bf3a7b 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -134,7 +134,6 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd)
/* Subdevice driver will reprobe and put the subdev back onto the list */
list_del_init(&sd->async_list);
sd->asd = NULL;
- sd->dev = NULL;
}
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
--
2.11.0
^ permalink raw reply related
* [PATCH v15 01/32] v4l: async: Remove re-probing support
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Remove V4L2 async re-probing support. The re-probing support has been
there to support cases where the sub-devices require resources provided by
the main driver's hardware to function, such as clocks.
Reprobing has allowed unbinding and again binding the main driver without
explicilty unbinding the sub-device drivers. This is certainly not a
common need, and the responsibility will be the user's going forward.
An alternative could have been to introduce notifier specific locks.
Considering the complexity of the re-probing and that it isn't really a
solution to a problem but a workaround, remove re-probing instead.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/v4l2-core/v4l2-async.c | 54 +-----------------------------------
1 file changed, 1 insertion(+), 53 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index d741a8e0fdac..60a1a50b9537 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -198,78 +198,26 @@ EXPORT_SYMBOL(v4l2_async_notifier_register);
void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{
struct v4l2_subdev *sd, *tmp;
- unsigned int notif_n_subdev = notifier->num_subdevs;
- unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
- struct device **dev;
- int i = 0;
if (!notifier->v4l2_dev)
return;
- dev = kvmalloc_array(n_subdev, sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- dev_err(notifier->v4l2_dev->dev,
- "Failed to allocate device cache!\n");
- }
-
mutex_lock(&list_lock);
list_del(¬ifier->list);
list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) {
- struct device *d;
-
- d = get_device(sd->dev);
-
v4l2_async_cleanup(sd);
- /* If we handled USB devices, we'd have to lock the parent too */
- device_release_driver(d);
-
if (notifier->unbind)
notifier->unbind(notifier, sd, sd->asd);
- /*
- * Store device at the device cache, in order to call
- * put_device() on the final step
- */
- if (dev)
- dev[i++] = d;
- else
- put_device(d);
+ list_move(&sd->async_list, &subdev_list);
}
mutex_unlock(&list_lock);
- /*
- * Call device_attach() to reprobe devices
- *
- * NOTE: If dev allocation fails, i is 0, and the whole loop won't be
- * executed.
- */
- while (i--) {
- struct device *d = dev[i];
-
- if (d && device_attach(d) < 0) {
- const char *name = "(none)";
- int lock = device_trylock(d);
-
- if (lock && d->driver)
- name = d->driver->name;
- dev_err(d, "Failed to re-probe to %s\n", name);
- if (lock)
- device_unlock(d);
- }
- put_device(d);
- }
- kvfree(dev);
-
notifier->v4l2_dev = NULL;
-
- /*
- * Don't care about the waiting list, it is initialised and populated
- * upon notifier registration.
- */
}
EXPORT_SYMBOL(v4l2_async_notifier_unregister);
--
2.11.0
^ permalink raw reply related
* [PATCH v15 21/32] dt: bindings: Add lens-focus binding for image sensors
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre, Rob Herring, devicetree
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
The lens-focus property contains a phandle to the lens voice coil driver
that is associated to the sensor; typically both are contained in the same
camera module.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Acked-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
Documentation/devicetree/bindings/media/video-interfaces.txt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
index fdba30479b47..b535bdde861c 100644
--- a/Documentation/devicetree/bindings/media/video-interfaces.txt
+++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
@@ -74,6 +74,8 @@ Optional properties
- flash-leds: An array of phandles, each referring to a flash LED, a sub-node
of the LED driver device node.
+- lens-focus: A phandle to the node of the focus lens controller.
+
Optional endpoint properties
----------------------------
--
2.11.0
^ permalink raw reply related
* [PATCH v15 20/32] dt: bindings: Add a binding for flash LED devices associated to a sensor
From: Sakari Ailus @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-media
Cc: niklas.soderlund, maxime.ripard, hverkuil, laurent.pinchart,
pavel, sre, Rob Herring, devicetree
In-Reply-To: <20171004215051.13385-1-sakari.ailus@linux.intel.com>
Camera flash drivers (and LEDs) are separate from the sensor devices in
DT. In order to make an association between the two, provide the
association information to the software.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
Documentation/devicetree/bindings/media/video-interfaces.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
index 852041a7480c..fdba30479b47 100644
--- a/Documentation/devicetree/bindings/media/video-interfaces.txt
+++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
@@ -67,6 +67,14 @@ are required in a relevant parent node:
identifier, should be 1.
- #size-cells : should be zero.
+
+Optional properties
+-------------------
+
+- flash-leds: An array of phandles, each referring to a flash LED, a sub-node
+ of the LED driver device node.
+
+
Optional endpoint properties
----------------------------
--
2.11.0
^ permalink raw reply related
* [PATCH tip/core/rcu 2/4] rcu: Make RCU CPU stall warnings check for irq-disabled CPUs
From: Paul E. McKenney @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-kernel
Cc: mingo, jiangshanlai, dipankar, akpm, mathieu.desnoyers, josh,
tglx, peterz, rostedt, dhowells, edumazet, fweisbec, oleg,
Paul E. McKenney
In-Reply-To: <20171004215006.GA12792@linux.vnet.ibm.com>
One common question upon seeing an RCU CPU stall warning is "did
the stalled CPUs have interrupts disabled?" However, the current
stall warnings are silent on this point. This commit therefore
uses irq_work to check whether stalled CPUs still respond to IPIs,
and flags this state in the RCU CPU stall warning console messages.
Reported-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
---
kernel/rcu/tree.c | 104 +++++++++++++++++++++++++++++++++++++++++------
kernel/rcu/tree.h | 5 +++
kernel/rcu/tree_plugin.h | 7 +++-
3 files changed, 103 insertions(+), 13 deletions(-)
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index d3b1d926bb91..5609fbcb50b5 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -1207,6 +1207,22 @@ static int rcu_is_cpu_rrupt_from_idle(void)
}
/*
+ * We are reporting a quiescent state on behalf of some other CPU, so
+ * it is our responsibility to check for and handle potential overflow
+ * of the rcu_node ->gpnum counter with respect to the rcu_data counters.
+ * After all, the CPU might be in deep idle state, and thus executing no
+ * code whatsoever.
+ */
+static void rcu_gpnum_ovf(struct rcu_node *rnp, struct rcu_data *rdp)
+{
+ lockdep_assert_held(&rnp->lock);
+ if (ULONG_CMP_LT(READ_ONCE(rdp->gpnum) + ULONG_MAX / 4, rnp->gpnum))
+ WRITE_ONCE(rdp->gpwrap, true);
+ if (ULONG_CMP_LT(rdp->rcu_iw_gpnum + ULONG_MAX / 4, rnp->gpnum))
+ rdp->rcu_iw_gpnum = rnp->gpnum + ULONG_MAX / 4;
+}
+
+/*
* Snapshot the specified CPU's dynticks counter so that we can later
* credit them with an implicit quiescent state. Return 1 if this CPU
* is in dynticks idle mode, which is an extended quiescent state.
@@ -1216,15 +1232,34 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp)
rdp->dynticks_snap = rcu_dynticks_snap(rdp->dynticks);
if (rcu_dynticks_in_eqs(rdp->dynticks_snap)) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti"));
- if (ULONG_CMP_LT(READ_ONCE(rdp->gpnum) + ULONG_MAX / 4,
- rdp->mynode->gpnum))
- WRITE_ONCE(rdp->gpwrap, true);
+ rcu_gpnum_ovf(rdp->mynode, rdp);
return 1;
}
return 0;
}
/*
+ * Handler for the irq_work request posted when a grace period has
+ * gone on for too long, but not yet long enough for an RCU CPU
+ * stall warning. Set state appropriately, but just complain if
+ * there is unexpected state on entry.
+ */
+static void rcu_iw_handler(struct irq_work *iwp)
+{
+ struct rcu_data *rdp;
+ struct rcu_node *rnp;
+
+ rdp = container_of(iwp, struct rcu_data, rcu_iw);
+ rnp = rdp->mynode;
+ raw_spin_lock_rcu_node(rnp);
+ if (!WARN_ON_ONCE(!rdp->rcu_iw_pending)) {
+ rdp->rcu_iw_gpnum = rnp->gpnum;
+ rdp->rcu_iw_pending = false;
+ }
+ raw_spin_unlock_rcu_node(rnp);
+}
+
+/*
* Return true if the specified CPU has passed through a quiescent
* state by virtue of being in or having passed through an dynticks
* idle state since the last call to dyntick_save_progress_counter()
@@ -1235,7 +1270,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
unsigned long jtsq;
bool *rnhqp;
bool *ruqp;
- struct rcu_node *rnp;
+ struct rcu_node *rnp = rdp->mynode;
/*
* If the CPU passed through or entered a dynticks idle phase with
@@ -1248,6 +1283,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
if (rcu_dynticks_in_eqs_since(rdp->dynticks, rdp->dynticks_snap)) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti"));
rdp->dynticks_fqs++;
+ rcu_gpnum_ovf(rnp, rdp);
return 1;
}
@@ -1258,12 +1294,12 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
* might not be the case for nohz_full CPUs looping in the kernel.
*/
jtsq = jiffies_till_sched_qs;
- rnp = rdp->mynode;
ruqp = per_cpu_ptr(&rcu_dynticks.rcu_urgent_qs, rdp->cpu);
if (time_after(jiffies, rdp->rsp->gp_start + jtsq) &&
READ_ONCE(rdp->rcu_qs_ctr_snap) != per_cpu(rcu_dynticks.rcu_qs_ctr, rdp->cpu) &&
READ_ONCE(rdp->gpnum) == rnp->gpnum && !rdp->gpwrap) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("rqc"));
+ rcu_gpnum_ovf(rnp, rdp);
return 1;
} else if (time_after(jiffies, rdp->rsp->gp_start + jtsq)) {
/* Load rcu_qs_ctr before store to rcu_urgent_qs. */
@@ -1274,6 +1310,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
if (!(rdp->grpmask & rcu_rnp_online_cpus(rnp))) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("ofl"));
rdp->offline_fqs++;
+ rcu_gpnum_ovf(rnp, rdp);
return 1;
}
@@ -1305,11 +1342,22 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
}
/*
- * If more than halfway to RCU CPU stall-warning time, do
- * a resched_cpu() to try to loosen things up a bit.
+ * If more than halfway to RCU CPU stall-warning time, do a
+ * resched_cpu() to try to loosen things up a bit. Also check to
+ * see if the CPU is getting hammered with interrupts, but only
+ * once per grace period, just to keep the IPIs down to a dull roar.
*/
- if (jiffies - rdp->rsp->gp_start > rcu_jiffies_till_stall_check() / 2)
+ if (jiffies - rdp->rsp->gp_start > rcu_jiffies_till_stall_check() / 2) {
resched_cpu(rdp->cpu);
+ if (IS_ENABLED(CONFIG_IRQ_WORK) &&
+ !rdp->rcu_iw_pending && rdp->rcu_iw_gpnum != rnp->gpnum &&
+ (rnp->ffmask & rdp->grpmask)) {
+ init_irq_work(&rdp->rcu_iw, rcu_iw_handler);
+ rdp->rcu_iw_pending = true;
+ rdp->rcu_iw_gpnum = rnp->gpnum;
+ irq_work_queue_on(&rdp->rcu_iw, rdp->cpu);
+ }
+ }
return 0;
}
@@ -1498,6 +1546,7 @@ static void print_cpu_stall(struct rcu_state *rsp)
{
int cpu;
unsigned long flags;
+ struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
struct rcu_node *rnp = rcu_get_root(rsp);
long totqlen = 0;
@@ -1513,7 +1562,9 @@ static void print_cpu_stall(struct rcu_state *rsp)
*/
pr_err("INFO: %s self-detected stall on CPU", rsp->name);
print_cpu_stall_info_begin();
+ raw_spin_lock_irqsave_rcu_node(rdp->mynode, flags);
print_cpu_stall_info(rsp, smp_processor_id());
+ raw_spin_unlock_irqrestore_rcu_node(rdp->mynode, flags);
print_cpu_stall_info_end();
for_each_possible_cpu(cpu)
totqlen += rcu_segcblist_n_cbs(&per_cpu_ptr(rsp->rda,
@@ -1907,6 +1958,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp,
rdp->core_needs_qs = need_gp;
zero_cpu_stall_ticks(rdp);
WRITE_ONCE(rdp->gpwrap, false);
+ rcu_gpnum_ovf(rnp, rdp);
}
return ret;
}
@@ -3685,6 +3737,8 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
rdp->cpu_no_qs.b.norm = true;
rdp->rcu_qs_ctr_snap = per_cpu(rcu_dynticks.rcu_qs_ctr, cpu);
rdp->core_needs_qs = false;
+ rdp->rcu_iw_pending = false;
+ rdp->rcu_iw_gpnum = rnp->gpnum - 1;
trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuonl"));
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
}
@@ -3722,10 +3776,24 @@ static void rcutree_affinity_setting(unsigned int cpu, int outgoing)
*/
int rcutree_online_cpu(unsigned int cpu)
{
- sync_sched_exp_online_cleanup(cpu);
- rcutree_affinity_setting(cpu, -1);
+ unsigned long flags;
+ struct rcu_data *rdp;
+ struct rcu_node *rnp;
+ struct rcu_state *rsp;
+
+ for_each_rcu_flavor(rsp) {
+ rdp = per_cpu_ptr(rsp->rda, cpu);
+ rnp = rdp->mynode;
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
+ rnp->ffmask |= rdp->grpmask;
+ raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
+ }
if (IS_ENABLED(CONFIG_TREE_SRCU))
srcu_online_cpu(cpu);
+ if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
+ return 0; /* Too early in boot for scheduler work. */
+ sync_sched_exp_online_cleanup(cpu);
+ rcutree_affinity_setting(cpu, -1);
return 0;
}
@@ -3735,6 +3803,19 @@ int rcutree_online_cpu(unsigned int cpu)
*/
int rcutree_offline_cpu(unsigned int cpu)
{
+ unsigned long flags;
+ struct rcu_data *rdp;
+ struct rcu_node *rnp;
+ struct rcu_state *rsp;
+
+ for_each_rcu_flavor(rsp) {
+ rdp = per_cpu_ptr(rsp->rda, cpu);
+ rnp = rdp->mynode;
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
+ rnp->ffmask &= ~rdp->grpmask;
+ raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
+ }
+
rcutree_affinity_setting(cpu, cpu);
if (IS_ENABLED(CONFIG_TREE_SRCU))
srcu_offline_cpu(cpu);
@@ -4183,8 +4264,7 @@ void __init rcu_init(void)
for_each_online_cpu(cpu) {
rcutree_prepare_cpu(cpu);
rcu_cpu_starting(cpu);
- if (IS_ENABLED(CONFIG_TREE_SRCU))
- srcu_online_cpu(cpu);
+ rcutree_online_cpu(cpu);
}
}
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h
index 8e1f285f0a70..46a5d1991450 100644
--- a/kernel/rcu/tree.h
+++ b/kernel/rcu/tree.h
@@ -103,6 +103,7 @@ struct rcu_node {
/* Online CPUs for next expedited GP. */
/* Any CPU that has ever been online will */
/* have its bit set. */
+ unsigned long ffmask; /* Fully functional CPUs. */
unsigned long grpmask; /* Mask to apply to parent qsmask. */
/* Only one bit will be set in this mask. */
int grplo; /* lowest-numbered CPU or group here. */
@@ -285,6 +286,10 @@ struct rcu_data {
/* 8) RCU CPU stall data. */
unsigned int softirq_snap; /* Snapshot of softirq activity. */
+ /* ->rcu_iw* fields protected by leaf rcu_node ->lock. */
+ struct irq_work rcu_iw; /* Check for non-irq activity. */
+ bool rcu_iw_pending; /* Is ->rcu_iw pending? */
+ unsigned long rcu_iw_gpnum; /* ->gpnum associated with ->rcu_iw. */
int cpu;
struct rcu_state *rsp;
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index e012b9be777e..14977d0470d1 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -1671,6 +1671,7 @@ static void print_cpu_stall_info_begin(void)
*/
static void print_cpu_stall_info(struct rcu_state *rsp, int cpu)
{
+ unsigned long delta;
char fast_no_hz[72];
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
struct rcu_dynticks *rdtp = rdp->dynticks;
@@ -1685,11 +1686,15 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu)
ticks_value = rsp->gpnum - rdp->gpnum;
}
print_cpu_stall_fast_no_hz(fast_no_hz, cpu);
- pr_err("\t%d-%c%c%c: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u fqs=%ld %s\n",
+ delta = rdp->mynode->gpnum - rdp->rcu_iw_gpnum;
+ pr_err("\t%d-%c%c%c%c: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u fqs=%ld %s\n",
cpu,
"O."[!!cpu_online(cpu)],
"o."[!!(rdp->grpmask & rdp->mynode->qsmaskinit)],
"N."[!!(rdp->grpmask & rdp->mynode->qsmaskinitnext)],
+ !IS_ENABLED(CONFIG_IRQ_WORK) ? '?' :
+ rdp->rcu_iw_pending ? (int)min(delta, 9UL) + '0' :
+ "!."[!delta],
ticks_value, ticks_title,
rcu_dynticks_snap(rdtp) & 0xfff,
rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting,
--
2.5.2
^ permalink raw reply related
* [PATCH tip/core/rcu 4/4] rcu: Suppress RCU CPU stall warnings while dumping trace
From: Paul E. McKenney @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-kernel
Cc: mingo, jiangshanlai, dipankar, akpm, mathieu.desnoyers, josh,
tglx, peterz, rostedt, dhowells, edumazet, fweisbec, oleg,
Paul E. McKenney
In-Reply-To: <20171004215006.GA12792@linux.vnet.ibm.com>
Currently, RCU emits Suppress RCU CPU stall warnings during its
automatically initiated ftrace_dump() calls after detecting an error
condition, which can result in excessively excessive console output
and lost trace events. This commit therefore suppresses RCU CPU stall
warnings across any of these ftrace_dump() calls.
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
---
kernel/rcu/rcu.h | 17 +++++++++++++++++
kernel/rcu/update.c | 1 +
2 files changed, 18 insertions(+)
diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h
index b8729eb09a5d..59c471de342a 100644
--- a/kernel/rcu/rcu.h
+++ b/kernel/rcu/rcu.h
@@ -203,6 +203,21 @@ static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head)
extern int rcu_cpu_stall_suppress;
int rcu_jiffies_till_stall_check(void);
+#define rcu_ftrace_dump_stall_suppress() \
+do { \
+ if (!rcu_cpu_stall_suppress) \
+ rcu_cpu_stall_suppress = 3; \
+} while (0)
+
+#define rcu_ftrace_dump_stall_unsuppress() \
+do { \
+ if (rcu_cpu_stall_suppress == 3) \
+ rcu_cpu_stall_suppress = 0; \
+} while (0)
+
+#else /* #endif #ifdef CONFIG_RCU_STALL_COMMON */
+#define rcu_ftrace_dump_stall_suppress()
+#define rcu_ftrace_dump_stall_unsuppress()
#endif /* #ifdef CONFIG_RCU_STALL_COMMON */
/*
@@ -222,7 +237,9 @@ do { \
if (!atomic_read(&___rfd_beenhere) && \
!atomic_xchg(&___rfd_beenhere, 1)) { \
tracing_off(); \
+ rcu_ftrace_dump_stall_suppress(); \
ftrace_dump(oops_dump_mode); \
+ rcu_ftrace_dump_stall_unsuppress(); \
} \
} while (0)
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index 5033b66d2753..3dc8efb16dc7 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -494,6 +494,7 @@ EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
#endif
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
+EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress);
static int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT;
module_param(rcu_cpu_stall_suppress, int, 0644);
--
2.5.2
^ permalink raw reply related
* [PATCH 0/2] [v5] pinctrl: qcom: add support for sparse GPIOs
From: Stephen Boyd @ 2017-10-04 21:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <40a0ab68-dc3a-10e2-f78e-9a386b4a72bd@codeaurora.org>
On 10/03, Timur Tabi wrote:
> On 10/03/2017 05:03 PM, Stephen Boyd wrote:
> >I've run into this now on our mobile SoCs after I pull in commit
> >8e51533780ba ("pinctrl: qcom: add get_direction function").
> >Before that commit we never read each pin of the device. On our
> >mobile SoCs we have devicetree and it feels like having that
> >describe which pins are available and not available is
> >half-duplicating information we would already have via consumers
> >indicating which pins they care about. I don't see any value
> >beyond system wide debug in figuring out the default pin
> >configuration of a pin that doesn't have a consumer in Linux.
>
> At the time I wrote that patch, the ACPI tables exposed all of the
> GPIOs, even the ones it didn't care about. The new ACPI tables list
> only specific GPIOs, and so we no longer need to blindly read the
> direction of all GPIOs.
>
Do you avoid this problem on new ACPI tables because only pins
that are able to be read are exposed?
>
> >This is basically a revert of commit 72d320006177 ("gpio: set up
> >initial state from .get_direction()").
>
> I would be in favor of either reverting that patch, or moving the
> code into gpiochip_add_pin_range().
If it's in gpiochip_add_pin_range() would we still read the
hardware when creating the pin ranges? I don't want to have to
describe pin ranges of "valid" pins that won't cause the system
to blow up if we touch them, because those pins are never used by
Linux so reading them is not useful.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH 0/2] [v5] pinctrl: qcom: add support for sparse GPIOs
From: Stephen Boyd @ 2017-10-04 21:50 UTC (permalink / raw)
To: Timur Tabi
Cc: Linus Walleij, Andy Gross, David Brown, anjiandi, Bjorn Andersson,
linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-arm-msm@vger.kernel.org, thierry.reding@gmail.com,
Mika Westerberg, Andy Shevchenko
In-Reply-To: <40a0ab68-dc3a-10e2-f78e-9a386b4a72bd@codeaurora.org>
On 10/03, Timur Tabi wrote:
> On 10/03/2017 05:03 PM, Stephen Boyd wrote:
> >I've run into this now on our mobile SoCs after I pull in commit
> >8e51533780ba ("pinctrl: qcom: add get_direction function").
> >Before that commit we never read each pin of the device. On our
> >mobile SoCs we have devicetree and it feels like having that
> >describe which pins are available and not available is
> >half-duplicating information we would already have via consumers
> >indicating which pins they care about. I don't see any value
> >beyond system wide debug in figuring out the default pin
> >configuration of a pin that doesn't have a consumer in Linux.
>
> At the time I wrote that patch, the ACPI tables exposed all of the
> GPIOs, even the ones it didn't care about. The new ACPI tables list
> only specific GPIOs, and so we no longer need to blindly read the
> direction of all GPIOs.
>
Do you avoid this problem on new ACPI tables because only pins
that are able to be read are exposed?
>
> >This is basically a revert of commit 72d320006177 ("gpio: set up
> >initial state from .get_direction()").
>
> I would be in favor of either reverting that patch, or moving the
> code into gpiochip_add_pin_range().
If it's in gpiochip_add_pin_range() would we still read the
hardware when creating the pin ranges? I don't want to have to
describe pin ranges of "valid" pins that won't cause the system
to blow up if we touch them, because those pins are never used by
Linux so reading them is not useful.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ 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.