Re: [PATCH v4 4/7] media: subdev: add subdev state locking

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Laurent,

On 2/28/22 11:14, Laurent Pinchart wrote:
> Hi Hans,
> 
> On Mon, Feb 28, 2022 at 11:05:09AM +0100, Hans Verkuil wrote:
>> On 2/16/22 14:00, Tomi Valkeinen wrote:
>>> The V4L2 subdevs have managed without centralized locking for the state
>>> (previously pad_config), as the try-state is supposedly safe (although I
>>> believe two TRY ioctls for the same fd would race), and the
>>> active-state, and its locking, is managed by the drivers internally.
>>>
>>> We now have active-state in a centralized position, and need locking.
>>> Strictly speaking the locking is only needed for new drivers that use
>>> the new state, as the current drivers continue behaving as they used to.
>>>
>>> However, active-state locking is complicated by the fact that currently
>>> the real active-state of a subdev is split into multiple parts: the new
>>> v4l2_subdev_state, subdev control state, and subdev's internal state.
>>>
>>> In the future all these three states should be combined into one state
>>> (the v4l2_subdev_state), and then a single lock for the state should be
>>> sufficient.
>>>
>>> But to solve the current split-state situation we need to share locks
>>> between the three states. This is accomplished by using the same lock
>>> management as the control handler does: we use a pointer to a mutex,
>>> allowing the driver to override the default mutex. Thus the driver can
>>> do e.g.:
>>>
>>> sd->state_lock = sd->ctrl_handler->lock;
>>>
>>> before calling v4l2_subdev_init_finalize(), resulting in sharing the
>>> same lock between the states and the controls.
>>>
>>> The locking model for active-state is such that any subdev op that gets
>>> the state as a parameter expects the state to be already locked by the
>>> caller, and expects the caller to release the lock.
>>>
>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@xxxxxxxxxxxxxxxx>
>>> ---
>>>  drivers/media/platform/rcar-vin/rcar-v4l2.c |  3 +-
>>>  drivers/media/platform/vsp1/vsp1_entity.c   |  4 +-
>>>  drivers/media/v4l2-core/v4l2-subdev.c       | 78 +++++++++++++++----
>>>  drivers/staging/media/tegra-video/vi.c      |  4 +-
>>>  include/media/v4l2-subdev.h                 | 85 ++++++++++++++++++++-
>>>  5 files changed, 155 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
>>> index da88f968c31a..3759f4619a77 100644
>>> --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
>>> +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
>>> @@ -255,6 +255,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which,
>>>  {
>>>  	struct v4l2_subdev *sd = vin_to_source(vin);
>>>  	struct v4l2_subdev_state *sd_state;
>>> +	static struct lock_class_key key;
>>>  	struct v4l2_subdev_format format = {
>>>  		.which = which,
>>>  		.pad = vin->parallel.source_pad,
>>> @@ -267,7 +268,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which,
>>>  	 * FIXME: Drop this call, drivers are not supposed to use
>>>  	 * __v4l2_subdev_state_alloc().
>>>  	 */
>>> -	sd_state = __v4l2_subdev_state_alloc(sd);
>>> +	sd_state = __v4l2_subdev_state_alloc(sd, "rvin:state->lock", &key);
>>>  	if (IS_ERR(sd_state))
>>>  		return PTR_ERR(sd_state);
>>>  
>>> diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
>>> index c82b3fb7b89a..a116a3362f9e 100644
>>> --- a/drivers/media/platform/vsp1/vsp1_entity.c
>>> +++ b/drivers/media/platform/vsp1/vsp1_entity.c
>>> @@ -613,6 +613,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
>>>  		     const char *name, unsigned int num_pads,
>>>  		     const struct v4l2_subdev_ops *ops, u32 function)
>>>  {
>>> +	static struct lock_class_key key;
>>>  	struct v4l2_subdev *subdev;
>>>  	unsigned int i;
>>>  	int ret;
>>> @@ -679,7 +680,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
>>>  	 * FIXME: Drop this call, drivers are not supposed to use
>>>  	 * __v4l2_subdev_state_alloc().
>>>  	 */
>>> -	entity->config = __v4l2_subdev_state_alloc(&entity->subdev);
>>> +	entity->config = __v4l2_subdev_state_alloc(&entity->subdev,
>>> +						   "vsp1:config->lock", &key);
>>>  	if (IS_ERR(entity->config)) {
>>>  		media_entity_cleanup(&entity->subdev.entity);
>>>  		return PTR_ERR(entity->config);
>>> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
>>> index b67bbce82612..0df9bbe1819d 100644
>>> --- a/drivers/media/v4l2-core/v4l2-subdev.c
>>> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
>>> @@ -27,8 +27,9 @@
>>>  static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
>>>  {
>>>  	struct v4l2_subdev_state *state;
>>> +	static struct lock_class_key key;
>>>  
>>> -	state = __v4l2_subdev_state_alloc(sd);
>>> +	state = __v4l2_subdev_state_alloc(sd, "fh->state->lock", &key);
>>>  	if (IS_ERR(state))
>>>  		return PTR_ERR(state);
>>>  
>>> @@ -383,18 +384,15 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
>>>  			     v4l2_subdev_get_active_state(sd);
>>>  }
>>>  
>>> -static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>>> +static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
>>> +			    struct v4l2_subdev_state *state)
>>>  {
>>>  	struct video_device *vdev = video_devdata(file);
>>>  	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
>>>  	struct v4l2_fh *vfh = file->private_data;
>>> -	struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
>>>  	bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
>>> -	struct v4l2_subdev_state *state;
>>>  	int rval;
>>>  
>>> -	state = subdev_ioctl_get_state(sd, subdev_fh, cmd, arg);
>>> -
>>>  	switch (cmd) {
>>>  	case VIDIOC_SUBDEV_QUERYCAP: {
>>>  		struct v4l2_subdev_capability *cap = arg;
>>> @@ -707,8 +705,24 @@ static long subdev_do_ioctl_lock(struct file *file, unsigned int cmd, void *arg)
>>>  
>>>  	if (lock && mutex_lock_interruptible(lock))
>>>  		return -ERESTARTSYS;
>>> -	if (video_is_registered(vdev))
>>> -		ret = subdev_do_ioctl(file, cmd, arg);
>>> +
>>> +	if (video_is_registered(vdev)) {
>>> +		struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
>>> +		struct v4l2_fh *vfh = file->private_data;
>>> +		struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
>>> +		struct v4l2_subdev_state *state;
>>> +
>>> +		state = subdev_ioctl_get_state(sd, subdev_fh, cmd, arg);
>>> +
>>> +		if (state)
>>> +			v4l2_subdev_lock_state(state);
>>> +
>>> +		ret = subdev_do_ioctl(file, cmd, arg, state);
>>> +
>>> +		if (state)
>>> +			v4l2_subdev_unlock_state(state);
>>> +	}
>>> +
>>>  	if (lock)
>>>  		mutex_unlock(lock);
>>>  	return ret;
>>> @@ -864,7 +878,7 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad,
>>>  			media_entity_to_v4l2_subdev(pad->entity);
>>>  		struct v4l2_subdev_state *state;
>>>  
>>> -		state = v4l2_subdev_get_active_state(sd);
>>> +		state = v4l2_subdev_get_locked_active_state(sd);
>>>  
>>>  		fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
>>>  		fmt->pad = pad->index;
>>> @@ -906,7 +920,9 @@ int v4l2_subdev_link_validate(struct media_link *link)
>>>  }
>>>  EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
>>>  
>>> -struct v4l2_subdev_state *__v4l2_subdev_state_alloc(struct v4l2_subdev *sd)
>>> +struct v4l2_subdev_state *
>>> +__v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name,
>>> +			  struct lock_class_key *lock_key)
>>>  {
>>>  	struct v4l2_subdev_state *state;
>>>  	int ret;
>>> @@ -915,6 +931,12 @@ struct v4l2_subdev_state *__v4l2_subdev_state_alloc(struct v4l2_subdev *sd)
>>>  	if (!state)
>>>  		return ERR_PTR(-ENOMEM);
>>>  
>>> +	__mutex_init(&state->_lock, lock_name, lock_key);
>>> +	if (sd->state_lock)
>>> +		state->lock = sd->state_lock;
>>> +	else
>>> +		state->lock = &state->_lock;
>>> +
>>>  	if (sd->entity.num_pads) {
>>>  		state->pads = kvmalloc_array(sd->entity.num_pads,
>>>  					     sizeof(*state->pads),
>>> @@ -925,7 +947,14 @@ struct v4l2_subdev_state *__v4l2_subdev_state_alloc(struct v4l2_subdev *sd)
>>>  		}
>>>  	}
>>>  
>>> +	/*
>>> +	 * There can be no race at this point, but we lock the state anyway to
>>> +	 * satisfy lockdep checks.
>>> +	 */
>>> +	v4l2_subdev_lock_state(state);
>>>  	ret = v4l2_subdev_call(sd, pad, init_cfg, state);
>>> +	v4l2_subdev_unlock_state(state);
>>> +
>>>  	if (ret < 0 && ret != -ENOIOCTLCMD)
>>>  		goto err;
>>>  
>>> @@ -946,16 +975,19 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state)
>>>  	if (!state)
>>>  		return;
>>>  
>>> +	mutex_destroy(&state->_lock);
>>> +
>>>  	kvfree(state->pads);
>>>  	kfree(state);
>>>  }
>>>  EXPORT_SYMBOL_GPL(__v4l2_subdev_state_free);
>>>  
>>> -int v4l2_subdev_init_finalize(struct v4l2_subdev *sd)
>>> +int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name,
>>> +				struct lock_class_key *key)
>>>  {
>>>  	struct v4l2_subdev_state *state;
>>>  
>>> -	state = __v4l2_subdev_state_alloc(sd);
>>> +	state = __v4l2_subdev_state_alloc(sd, name, key);
>>>  	if (IS_ERR(state))
>>>  		return PTR_ERR(state);
>>>  
>>> @@ -963,7 +995,7 @@ int v4l2_subdev_init_finalize(struct v4l2_subdev *sd)
>>>  
>>>  	return 0;
>>>  }
>>> -EXPORT_SYMBOL_GPL(v4l2_subdev_init_finalize);
>>> +EXPORT_SYMBOL_GPL(__v4l2_subdev_init_finalize);
>>>  
>>>  void v4l2_subdev_cleanup(struct v4l2_subdev *sd)
>>>  {
>>> @@ -972,6 +1004,26 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd)
>>>  }
>>>  EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup);
>>>  
>>> +struct v4l2_subdev_state *v4l2_subdev_lock_active_state(struct v4l2_subdev *sd)
>>> +{
>>> +	mutex_lock(sd->active_state->lock);
>>> +
>>> +	return sd->active_state;
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_subdev_lock_active_state);
>>
>> I don't like this function very much. First of all, call v4l2_subdev_lock_state()
>> instead of mutex_lock, that signals that the normal state lock function is used.
>>
>> The naming is poor since this suggests that the active_state is just locked
>> when it actually is also returned. So v4l2_subdev_lock_and_return_active_state()
>> is really the correct name. Long, yes, but at least it is clear what it does.
> 
> I agree the name isn't perfect. How about
> v4l2_subdev_lock_and_get_active_state() ?

That's certainly better, but then v4l2_subdev_lock_and_return_state should
also be renamed to v4l2_subdev_lock_and_get_state() to stay consistent.

> 
>> I also think this is better done as a static inline.
>>
>> But really, I wonder if we need this helper at all. Can't drivers just call
>> v4l2_subdev_lock_state(sd->active_state) and then use sd->active_state?
>>
>> I think that's much more understandable, and it avoids having confusing
>> lock helper functions. More on this below in the header.
> 
> Drivers should never access that active_state field manually, that's a
> hard rule. All accesses should go through accessors. No expection, so
> it's easy to enforce the rule.
> 
> This accessor isn't meant to stay. It is here to help the transition to
> active states, and will also allow quickly identifying through grep
> where more work is required to move drivers to the correct API.

OK. Perhaps this should be mentioned in the function comments.

Actually, I think that is a good point: make it clear in the header and
perhaps in the source implementation as well if a function is temporary,
and explain what needs to be done to eventually be able to remove it.

This probably also ties in with a v4l2-subdev-legacy.h header.

> 
>>> +
>>> +void v4l2_subdev_lock_state(struct v4l2_subdev_state *state)
>>> +{
>>> +	mutex_lock(state->lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_subdev_lock_state);
>>> +
>>> +void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state)
>>> +{
>>> +	mutex_unlock(state->lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_subdev_unlock_state);
>>
>> To be honest, I think these two functions could also be moved to the header
>> as a static inline. It will be a bit more efficient
> 
> Note that I expect in the future a switch to ww_mutex, so this will
> become more complex. That being said, we can start with inline
> functions, and then rework that.
> 
>> and for developers reading
>> the header it will be easier to understand what is going on.
> 
> Driver developers shouldn't care how this is implemented (and should
> very very much *never* touch this mutex directly, or do anything funky
> with it).

If things go wrong, they really do care about what mutex is involved :-)

> 
>>> +
>>>  #endif /* CONFIG_MEDIA_CONTROLLER */
>>>  
>>>  void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
>>> diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
>>> index 07d368f345cd..8e184aa4c252 100644
>>> --- a/drivers/staging/media/tegra-video/vi.c
>>> +++ b/drivers/staging/media/tegra-video/vi.c
>>> @@ -491,6 +491,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
>>>  				      struct v4l2_pix_format *pix)
>>>  {
>>>  	const struct tegra_video_format *fmtinfo;
>>> +	static struct lock_class_key key;
>>>  	struct v4l2_subdev *subdev;
>>>  	struct v4l2_subdev_format fmt;
>>>  	struct v4l2_subdev_state *sd_state;
>>> @@ -511,7 +512,8 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
>>>  	 * FIXME: Drop this call, drivers are not supposed to use
>>>  	 * __v4l2_subdev_state_alloc().
>>>  	 */
>>> -	sd_state = __v4l2_subdev_state_alloc(subdev);
>>> +	sd_state = __v4l2_subdev_state_alloc(subdev, "tegra:state->lock",
>>> +					     &key);
>>>  	if (IS_ERR(sd_state))
>>>  		return PTR_ERR(sd_state);
>>>  	/*
>>> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
>>> index 1bbe4383966c..8d089a2dbd32 100644
>>> --- a/include/media/v4l2-subdev.h
>>> +++ b/include/media/v4l2-subdev.h
>>> @@ -658,6 +658,8 @@ struct v4l2_subdev_pad_config {
>>>  /**
>>>   * struct v4l2_subdev_state - Used for storing subdev state information.
>>>   *
>>> + * @_lock: default for 'lock'
>>> + * @lock: mutex for the state. May be replaced by the user.
>>>   * @pads: &struct v4l2_subdev_pad_config array
>>>   *
>>>   * This structure only needs to be passed to the pad op if the 'which' field
>>> @@ -665,6 +667,9 @@ struct v4l2_subdev_pad_config {
>>>   * %V4L2_SUBDEV_FORMAT_ACTIVE it is safe to pass %NULL.
>>>   */
>>>  struct v4l2_subdev_state {
>>> +	/* lock for the struct v4l2_subdev_state fields */
>>> +	struct mutex _lock;
>>> +	struct mutex *lock;
>>>  	struct v4l2_subdev_pad_config *pads;
>>>  };
>>>  
>>> @@ -888,6 +893,9 @@ struct v4l2_subdev_platform_data {
>>>   * @subdev_notifier: A sub-device notifier implicitly registered for the sub-
>>>   *		     device using v4l2_async_register_subdev_sensor().
>>>   * @pdata: common part of subdevice platform data
>>> + * @state_lock: A pointer to a lock used for all the subdev's states, set by the
>>> + *		driver. This is	optional. If NULL, each state instance will get
>>> + *		a lock of its own.
>>>   * @active_state: Active state for the subdev (NULL for subdevs tracking the
>>>   *		  state internally). Initialized by calling
>>>   *		  v4l2_subdev_init_finalize().
>>> @@ -922,6 +930,7 @@ struct v4l2_subdev {
>>>  	struct v4l2_async_notifier *notifier;
>>>  	struct v4l2_async_notifier *subdev_notifier;
>>>  	struct v4l2_subdev_platform_data *pdata;
>>> +	struct mutex *state_lock;
>>>  
>>>  	/*
>>>  	 * The fields below are private, and should only be accessed via
>>> @@ -1144,12 +1153,16 @@ int v4l2_subdev_link_validate(struct media_link *link);
>>>   * __v4l2_subdev_state_alloc - allocate v4l2_subdev_state
>>>   *
>>>   * @sd: pointer to &struct v4l2_subdev for which the state is being allocated.
>>> + * @lock_name: name of the state lock
>>> + * @key: lock_class_key for the lock
>>>   *
>>>   * Must call __v4l2_subdev_state_free() when state is no longer needed.
>>>   *
>>>   * Not to be called directly by the drivers.
>>>   */
>>> -struct v4l2_subdev_state *__v4l2_subdev_state_alloc(struct v4l2_subdev *sd);
>>> +struct v4l2_subdev_state *__v4l2_subdev_state_alloc(struct v4l2_subdev *sd,
>>> +						    const char *lock_name,
>>> +						    struct lock_class_key *key);
>>>  
>>>  /**
>>>   * __v4l2_subdev_state_free - free a v4l2_subdev_state
>>> @@ -1174,7 +1187,16 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state);
>>>   *
>>>   * The user must call v4l2_subdev_cleanup() when the subdev is being removed.
>>>   */
>>> -int v4l2_subdev_init_finalize(struct v4l2_subdev *sd);
>>> +#define v4l2_subdev_init_finalize(sd)                                          \
>>> +	({                                                                     \
>>> +		static struct lock_class_key __key;                            \
>>> +		const char *name = KBUILD_BASENAME                             \
>>> +			":" __stringify(__LINE__) ":sd->active_state->lock";   \
>>> +		__v4l2_subdev_init_finalize(sd, name, &__key);                 \
>>> +	})
>>> +
>>> +int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name,
>>> +				struct lock_class_key *key);
>>>  
>>>  /**
>>>   * v4l2_subdev_cleanup() - Releases the resources allocated by the subdevice
>>> @@ -1191,14 +1213,71 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd);
>>>   * @sd: The subdevice
>>>   *
>>>   * Returns the active state for the subdevice, or NULL if the subdev does not
>>> - * support active state.
>>> + * support active state. If the state is not NULL, calls
>>> + * lockdep_assert_not_held() to issue a warning if the state is locked.
>>> + *
>>> + * This function is to be used e.g. when getting the active state for the sole
>>> + * purpose of passing it forward, without accessing the state fields.
>>>   */
>>>  static inline struct v4l2_subdev_state *
>>>  v4l2_subdev_get_active_state(struct v4l2_subdev *sd)
>>>  {
>>> +	if (sd->active_state)
>>> +		lockdep_assert_not_held(sd->active_state->lock);
>>> +	return sd->active_state;
>>> +}
>>> +
>>> +/**
>>> + * v4l2_subdev_get_locked_active_state() - Checks that the active subdev state
>>> + *					   is locked and returns it
>>> + *
>>> + * @sd: The subdevice
>>> + *
>>> + * Returns the active state for the subdevice, or NULL if the subdev does not
>>> + * support active state. If the state is not NULL, calls lockdep_assert_held()
>>> + * to issue a warning if the state is not locked.
>>> + *
>>> + * This function is to be used when the caller knows that the active state is
>>> + * already locked.
>>> + */
>>> +static inline struct v4l2_subdev_state *
>>> +v4l2_subdev_get_locked_active_state(struct v4l2_subdev *sd)
>>> +{
>>> +	if (sd->active_state)
>>> +		lockdep_assert_held(sd->active_state->lock);
>>>  	return sd->active_state;
>>>  }
>>
>> Do we really need these two functions? I can't help feeling that this is
>> overkill and that is becomes quite confusing to have all these similarly
>> names functions.
>>
>> It's a bit of a grey area admittedly, but it does confuse me a bit.
> 
> As mentioned above, I think there will be room for simplication after
> the transition. It may take a while though. In the meantime, being able
> to catch locking issues is useful in my opinion.

OK.

Regards,

	Hans

> 
>>> +/**
>>> + * v4l2_subdev_lock_active_state() - Locks and returns the active subdev state
>>> + *				     for the subdevice
>>> + * @sd: The subdevice
>>> + *
>>> + * Returns the locked active state for the subdevice, or NULL if the subdev
>>> + * does not support active state.
>>> + *
>>> + * The state must be unlocked with v4l2_subdev_unlock_state() after use.
>>> + */
>>> +struct v4l2_subdev_state *v4l2_subdev_lock_active_state(struct v4l2_subdev *sd);
>>> +
>>> +/**
>>> + * v4l2_subdev_lock_state() - Locks the subdev state
>>> + * @state: The subdevice state
>>> + *
>>> + * Locks the given subdev state.
>>> + *
>>> + * The state must be unlocked with v4l2_subdev_unlock_state() after use.
>>> + */
>>> +void v4l2_subdev_lock_state(struct v4l2_subdev_state *state);
>>> +
>>> +/**
>>> + * v4l2_subdev_unlock_state() - Unlocks the subdev state
>>> + * @state: The subdevice state
>>> + *
>>> + * Unlocks the given subdev state.
>>> + */
>>> +void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state);
>>> +
>>>  #endif /* CONFIG_MEDIA_CONTROLLER */
>>>  
>>>  /**
> 



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux