Search Linux Wireless

Re: [PATCH v7 1/5] mac80211: implement multi-vif in-place reservations

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

 



On Thu, 2014-05-29 at 09:34 +0200, Michal Kazior wrote:
> Multi-vif in-place reservations happen when
> it is impossible to allocate more channel contexts
> as indicated by interface combinations.
> 
> Such reservations are not finalized until all
> assigned interfaces are ready.
> 
> This still doesn't handle all possible cases
> (i.e. degradation of number of channels) properly.
> 
> Signed-off-by: Michal Kazior <michal.kazior@xxxxxxxxx>
> ---


> diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
> index 3702d64..01379a17 100644
> --- a/net/mac80211/chan.c
> +++ b/net/mac80211/chan.c
[...]
> @@ -898,8 +920,16 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
>  	list_del(&sdata->reserved_chanctx_list);
>  	sdata->reserved_chanctx = NULL;
>  
> -	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
> -		ieee80211_free_chanctx(sdata->local, ctx);
> +	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
> +		if (ctx->replaces) {
> +			WARN_ON(ctx->replaces->replaced_by != ctx);
> +			ctx->replaces->replaced_by = NULL;
> +			list_del_rcu(&ctx->list);
> +			kfree_rcu(ctx, rcu_head);

Couldn't this go into the ieee80211_free_chanctx()?

[...]
> @@ -911,39 +941,71 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
>  {
>  	struct ieee80211_local *local = sdata->local;
>  	struct ieee80211_chanctx_conf *conf;
> -	struct ieee80211_chanctx *new_ctx, *curr_ctx;
> -	int ret = 0;
> +	struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
>  
> -	mutex_lock(&local->chanctx_mtx);
> +	lockdep_assert_held(&local->chanctx_mtx);
> +
> +	if (local->use_chanctx && !local->ops->switch_vif_chanctx)
> +		return -ENOTSUPP;

Do we really need to fail here for all reservations (ie. even when it's
not an in-place reservation)?

[...]
>  		} else {
> -			ret = -EBUSY;
> -			goto out;

> +			if (curr_ctx->replaced_by ||
> +			    !list_empty(&curr_ctx->reserved_vifs)) {
> +				/*
> +				 * Another vif already requested this context
> +				 * for an in-place reservation. Find another

If !curr_ctx->replaced_by, then this is not an in-place reservation,
right? the ->reserved_vifs will switch from another context to this one.
The comment is a bit misleading.


> +				 * one hoping all vifs assigned to it will also
> +				 * switch soon enough.
> +				 *
> +				 * TODO: This needs a little more work as some
> +				 * cases may fail which could otherwise succeed
> +				 * provided some channel context juggling was
> +				 * performed.
> +				 */

This TODO seems fair enough, but could you provide at least one example
of such case?


> +				list_for_each_entry(ctx, &local->chanctx_list,
> +						    list) {
> +					if (ctx->replaced_by)
> +						continue;
> +
> +					if (ctx->replaces)
> +						continue;
> +
> +					if (!list_empty(&ctx->reserved_vifs))
> +						continue;
> +
> +					curr_ctx = ctx;
> +					break;
> +				}

I'm probably missing something, because I don't get this.  Do you just
try to find *any* other existing context and "steal" it? Even if the
other context you find has a completely unrelated chandef?


> +			}
> +
> +			/*
> +			 * If that's true then all available contexts are all
> +			 * in-place reserved already.
> +			 */
> +			if (curr_ctx->replaced_by)
> +				return -EBUSY;

What about the reserved_vifs case? You may also get here if
curr_ctx->replaced_by is not true, but curr_ctx->reserved_vifs is not
empty.

[...]
> -int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
> -				       u32 *changed)
> +static int
> +ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
>  {
>  	struct ieee80211_local *local = sdata->local;
> -	struct ieee80211_chanctx *ctx;
> -	struct ieee80211_chanctx *old_ctx;
> -	struct ieee80211_chanctx_conf *conf;
> -	int ret;
> -	u32 tmp_changed = *changed;
> -
> -	/* TODO: need to recheck if the chandef is usable etc.? */
> +	struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
> +	struct ieee80211_chanctx *old_ctx, *new_ctx;
> +	const struct cfg80211_chan_def *chandef;
> +	u32 changed = 0;
> +	int err;
>  
>  	lockdep_assert_held(&local->mtx);
> +	lockdep_assert_held(&local->chanctx_mtx);
>  
> -	mutex_lock(&local->chanctx_mtx);
> +	new_ctx = sdata->reserved_chanctx;
> +	old_ctx = ieee80211_vif_get_chanctx(sdata);
>  
> -	ctx = sdata->reserved_chanctx;
> -	if (WARN_ON(!ctx)) {
> -		ret = -EINVAL;
> -		goto out;
> -	}
> +	if (WARN_ON(!sdata->reserved_ready))
> +		return -EBUSY;
>  
> -	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
> -					 lockdep_is_held(&local->chanctx_mtx));
> -	if (!conf) {
> -		ret = -EINVAL;
> -		goto out;
> +	if (WARN_ON(!new_ctx))
> +		return -EINVAL;
> +
> +	if (WARN_ON(!old_ctx))
> +		return -EINVAL;

You already WARN_ON !new_ctx and !old_ctx in
ieee80211_vif_use_reserved_context().  I know it doesn't hurt, but do
you have to double-check here?


> +
> +	if (WARN_ON(new_ctx->replaced_by))
> +		return -EINVAL;
> +
> +	if (WARN_ON(old_ctx->replaced_by && new_ctx->replaces))
> +		return -EINVAL;

What if new_ctx is going to replace something else?

[...]
> +static int
> +ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
> +{
> +	struct ieee80211_sub_if_data *sdata, *sdata_tmp;
> +	struct ieee80211_chanctx *new_ctx = NULL, *ctx, *ctx_tmp;

I prefer if declarations with assignments are in lines of their own, but
maybe it's just me.


> +	struct ieee80211_vif_chanctx_switch *vif_chsw = NULL;
> +	const struct cfg80211_chan_def *chandef;
> +	int i, err, n_ctx = 0, n_vifs = 0;
> +
> +	lockdep_assert_held(&local->mtx);
> +	lockdep_assert_held(&local->chanctx_mtx);
> +
> +	/*
> +	 * If there are 2 independant pairs of channel contexts performing

Typo, independent.

> +	 * cross-switch of their vifs this code will still wait until both are
> +	 * ready even though it could be possible to switch one before the
> +	 * other is ready.
> +	 *
> +	 * For practical reasons and code simplicity just do a single huge
> +	 * switch.
> +	 */
> +
> +	list_for_each_entry(ctx, &local->chanctx_list, list) {
> +		if (!(ctx->replaces && !ctx->replaced_by))
> +			continue;

if (!ctx->replaces || ctx->replaced_by) looks more natural to me. (Same
thing for some other cases in this patch).


> +
> +		if (!local->use_chanctx)
> +			new_ctx = ctx;
> +
> +		n_ctx++;
> +
> +		list_for_each_entry(sdata, &ctx->replaces->assigned_vifs,
> +				    assigned_chanctx_list) {
> +			if (!sdata->reserved_chanctx) {
> +				wiphy_info(local->hw.wiphy,
> +					   "channel context reservation cannot be finalized because some interfaces aren't switching\n");
> +				err = -EBUSY;
> +				goto err;
> +			}

Hadn't we decided to disconnect the vifs that didn't follow? Can't
remember anymore. :) But now you're just canceling the whole switch?


> +
> +			if (!sdata->reserved_ready)
> +				return -EAGAIN;
> +		}

Shouldn't this be above the previous if? If not everyone switched yet,
can't we give more time for others to join the switch?


> +
> +		list_for_each_entry(sdata, &ctx->reserved_vifs,
> +				    reserved_chanctx_list) {
> +			if (!ieee80211_vif_has_in_place_reservation(sdata))
> +				continue;
> +
> +			if (!sdata->reserved_ready)
> +				return -EAGAIN;
> +
> +			n_vifs++;
> +
> +			if (sdata->reserved_radar_required)
> +				ctx->conf.radar_enabled = true;
> +		}
> +	}
> +
> +	if (WARN_ON(n_ctx == 0) ||
> +	    WARN_ON(n_vifs == 0) ||
> +	    WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
> +	    WARN_ON(!new_ctx && !local->use_chanctx)) {

Can this last WARN ever happen? Only if there's no chanctx at all in the
chanctx_list (which should never happen)?


> +		err = -EINVAL;
> +		goto err;
> +	}
> +
> +	if (local->use_chanctx) {
> +		vif_chsw = kzalloc(sizeof(*vif_chsw) * n_vifs, GFP_KERNEL);
> +		if (vif_chsw) {
> +			err = -ENOMEM;
> +			goto err;
> +		}
> +
> +		i = 0;
> +		list_for_each_entry(ctx, &local->chanctx_list, list) {
> +			if (!(ctx->replaces && !ctx->replaced_by))
> +				continue;
> +
> +			list_for_each_entry(sdata, &ctx->reserved_vifs,
> +					    reserved_chanctx_list) {
> +				if (!ieee80211_vif_has_in_place_reservation(
> +						sdata))
> +					continue;
> +
> +				vif_chsw[i].vif = &sdata->vif;
> +				vif_chsw[i].old_ctx = &ctx->replaces->conf;
> +				vif_chsw[i].new_ctx = &ctx->conf;
> +
> +				i++;
> +			}
> +		}
> +
> +		err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
> +					     CHANCTX_SWMODE_SWAP_CONTEXTS);
> +		kfree(vif_chsw);
> +
> +		if (err)
> +			goto err;
>  	} else {
> -		ret = ieee80211_assign_vif_chanctx(sdata, ctx);
> -		if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
> -			ieee80211_free_chanctx(local, old_ctx);
> -		if (ret) {
> -			/* if assign fails refcount stays the same */
> -			if (ieee80211_chanctx_refcount(local, ctx) == 0)
> -				ieee80211_free_chanctx(local, ctx);
> -			goto out;
> +		chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx,
> +							     NULL);
> +		if (WARN_ON(!chandef)) {
> +			err = -EINVAL;
> +			goto err;
>  		}
>  
> -		if (sdata->vif.type == NL80211_IFTYPE_AP)
> -			__ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
> +		local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
> +		local->_oper_chandef = *chandef;
> +		ieee80211_hw_config(local, 0);
>  	}
>  
> -	*changed = tmp_changed;
> +	i = 0;
> +	list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
> +		if (!(ctx->replaces && !ctx->replaced_by))
> +			continue;
>  
> -	ieee80211_recalc_chanctx_chantype(local, ctx);
> -	ieee80211_recalc_smps_chanctx(local, ctx);
> -	ieee80211_recalc_radar_chanctx(local, ctx);
> -	ieee80211_recalc_chanctx_min_def(local, ctx);
> -out:
> -	mutex_unlock(&local->chanctx_mtx);
> -	return ret;
> +		list_for_each_entry(sdata, &ctx->reserved_vifs,
> +				    reserved_chanctx_list) {
> +			u32 changed = 0;
> +
> +			if (!ieee80211_vif_has_in_place_reservation(sdata))
> +				continue;
> +
> +			rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
> +
> +			if (sdata->vif.type == NL80211_IFTYPE_AP)
> +				__ieee80211_vif_copy_chanctx_to_vlans(sdata,
> +								      false);
> +
> +			sdata->radar_required = sdata->reserved_radar_required;
> +
> +			if (sdata->vif.bss_conf.chandef.width !=
> +			    sdata->reserved_chandef.width)
> +				changed = BSS_CHANGED_BANDWIDTH;
> +
> +			sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
> +			if (changed)
> +				ieee80211_bss_info_change_notify(sdata,
> +								 changed);
> +
> +			ieee80211_recalc_txpower(sdata);
> +		}
> +
> +		ieee80211_recalc_chanctx_chantype(local, ctx);
> +		ieee80211_recalc_smps_chanctx(local, ctx);
> +		ieee80211_recalc_radar_chanctx(local, ctx);
> +		ieee80211_recalc_chanctx_min_def(local, ctx);
> +
> +		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
> +					 reserved_chanctx_list) {
> +			list_del(&sdata->reserved_chanctx_list);
> +			list_move(&sdata->assigned_chanctx_list,
> +				  &new_ctx->assigned_vifs);
> +			sdata->reserved_chanctx = NULL;
> +		}
> +
> +		list_del_rcu(&ctx->replaces->list);
> +		kfree_rcu(ctx->replaces, rcu_head);
> +		ctx->replaces = NULL;
> +
> +		/*
> +		 * Some simple reservations depending on the in-place switch
> +		 * might've been ready before and were deferred. Retry them now
> +		 * but don't propagate the error up the call stack as the
> +		 * directly requested reservation has been already handled
> +		 * successfully at this point.
> +		 */
> +		list_for_each_entry(sdata, &ctx->reserved_vifs,
> +				    reserved_chanctx_list) {
> +			if (WARN_ON(ieee80211_vif_has_in_place_reservation(
> +					sdata)))
> +				continue;
> +
> +			if (!sdata->reserved_ready)
> +				continue;
> +
> +			err = ieee80211_vif_use_reserved_assign(sdata);
> +			if (WARN_ON(err && err != -EAGAIN)) {
 
Do we really want to WARN on other error cases here? It could be some
kind of -EBUSY or so and just disconnecting would be okay?


> +				sdata_info(sdata,
> +					   "failed to finalize reservation (err=%d)\n",
> +					   err);
> +				ieee80211_vif_unreserve_chanctx(sdata);
> +				cfg80211_stop_iface(local->hw.wiphy,
> +						    &sdata->wdev,
> +						    GFP_KERNEL);
> +			}
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	list_for_each_entry(ctx, &local->chanctx_list, list) {
> +		if (!(ctx->replaces && !ctx->replaced_by))
> +			continue;
> +
> +		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
> +					 reserved_chanctx_list)
> +			ieee80211_vif_unreserve_chanctx(sdata);
> +	}
> +
> +	return err;
> +}

This function is huge, any way to break it down? Or at least add
comments before each block explaining what they do...


> +int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
> +{
> +	struct ieee80211_local *local = sdata->local;
> +	struct ieee80211_chanctx *new_ctx;
> +	struct ieee80211_chanctx *old_ctx;
> +	int err;
> +
> +	lockdep_assert_held(&local->mtx);
> +	lockdep_assert_held(&local->chanctx_mtx);
> +
> +	new_ctx = sdata->reserved_chanctx;
> +	old_ctx = ieee80211_vif_get_chanctx(sdata);
> +
> +	if (WARN_ON(!new_ctx))
> +		return -EINVAL;
> +
> +	if (WARN_ON(!old_ctx))
> +		return -EINVAL;
> +
> +	if (WARN_ON(sdata->reserved_ready))
> +		return -EINVAL;
> +
> +	sdata->reserved_ready = true;
> +	if (!(old_ctx->replaced_by && new_ctx->replaces)) {

Isn't !old->ctx->replaced_by enough here? Do you really care if the
new_ctx will replace something else at this point?


> +		err = ieee80211_vif_use_reserved_assign(sdata);
> +		if (err && err != -EAGAIN)
> +			return err;
> +	}
> +
> +	/*
> +	 * In-place reservation may need to be finalized now either if:
> +	 *  - sdata is taking part in the swapping itself and is the last one
> +	 *  - sdata has switched with a simple reservation to an existing
> +	 *    context readying the in-place switching old_ctx
> +	 */
> +
> +	if (old_ctx->replaced_by || new_ctx->replaces) {

Same thing here... if new_ctx replaces something *else* shouldn't we use
_assign() instead?


> +		err = ieee80211_vif_use_reserved_switch(local);
> +		if (err && err != -EAGAIN)
> +			return err;
> +	}
> +
> +	return 0;
>  }
[...]

--
Luca.

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux