linux/net/mac80211/chan.c
<<
>>
Prefs
   1/*
   2 * mac80211 - channel management
   3 */
   4
   5#include <linux/nl80211.h>
   6#include <linux/export.h>
   7#include <linux/rtnetlink.h>
   8#include <net/cfg80211.h>
   9#include "ieee80211_i.h"
  10#include "driver-ops.h"
  11
  12static void ieee80211_change_chanctx(struct ieee80211_local *local,
  13                                     struct ieee80211_chanctx *ctx,
  14                                     const struct cfg80211_chan_def *chandef)
  15{
  16        if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
  17                return;
  18
  19        WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
  20
  21        ctx->conf.def = *chandef;
  22        drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
  23
  24        if (!local->use_chanctx) {
  25                local->_oper_chandef = *chandef;
  26                ieee80211_hw_config(local, 0);
  27        }
  28}
  29
  30static struct ieee80211_chanctx *
  31ieee80211_find_chanctx(struct ieee80211_local *local,
  32                       const struct cfg80211_chan_def *chandef,
  33                       enum ieee80211_chanctx_mode mode)
  34{
  35        struct ieee80211_chanctx *ctx;
  36
  37        lockdep_assert_held(&local->chanctx_mtx);
  38
  39        if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
  40                return NULL;
  41
  42        list_for_each_entry(ctx, &local->chanctx_list, list) {
  43                const struct cfg80211_chan_def *compat;
  44
  45                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
  46                        continue;
  47
  48                compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
  49                if (!compat)
  50                        continue;
  51
  52                ieee80211_change_chanctx(local, ctx, compat);
  53
  54                return ctx;
  55        }
  56
  57        return NULL;
  58}
  59
  60static bool ieee80211_is_radar_required(struct ieee80211_local *local)
  61{
  62        struct ieee80211_sub_if_data *sdata;
  63
  64        rcu_read_lock();
  65        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
  66                if (sdata->radar_required) {
  67                        rcu_read_unlock();
  68                        return true;
  69                }
  70        }
  71        rcu_read_unlock();
  72
  73        return false;
  74}
  75
  76static struct ieee80211_chanctx *
  77ieee80211_new_chanctx(struct ieee80211_local *local,
  78                      const struct cfg80211_chan_def *chandef,
  79                      enum ieee80211_chanctx_mode mode)
  80{
  81        struct ieee80211_chanctx *ctx;
  82        u32 changed;
  83        int err;
  84
  85        lockdep_assert_held(&local->chanctx_mtx);
  86
  87        ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
  88        if (!ctx)
  89                return ERR_PTR(-ENOMEM);
  90
  91        ctx->conf.def = *chandef;
  92        ctx->conf.rx_chains_static = 1;
  93        ctx->conf.rx_chains_dynamic = 1;
  94        ctx->mode = mode;
  95        ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
  96        if (!local->use_chanctx)
  97                local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
  98
  99        /* acquire mutex to prevent idle from changing */
 100        mutex_lock(&local->mtx);
 101        /* turn idle off *before* setting channel -- some drivers need that */
 102        changed = ieee80211_idle_off(local);
 103        if (changed)
 104                ieee80211_hw_config(local, changed);
 105
 106        if (!local->use_chanctx) {
 107                local->_oper_chandef = *chandef;
 108                ieee80211_hw_config(local, 0);
 109        } else {
 110                err = drv_add_chanctx(local, ctx);
 111                if (err) {
 112                        kfree(ctx);
 113                        ctx = ERR_PTR(err);
 114
 115                        ieee80211_recalc_idle(local);
 116                        goto out;
 117                }
 118        }
 119
 120        /* and keep the mutex held until the new chanctx is on the list */
 121        list_add_rcu(&ctx->list, &local->chanctx_list);
 122
 123 out:
 124        mutex_unlock(&local->mtx);
 125
 126        return ctx;
 127}
 128
 129static void ieee80211_free_chanctx(struct ieee80211_local *local,
 130                                   struct ieee80211_chanctx *ctx)
 131{
 132        bool check_single_channel = false;
 133        lockdep_assert_held(&local->chanctx_mtx);
 134
 135        WARN_ON_ONCE(ctx->refcount != 0);
 136
 137        if (!local->use_chanctx) {
 138                struct cfg80211_chan_def *chandef = &local->_oper_chandef;
 139                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 140                chandef->center_freq1 = chandef->chan->center_freq;
 141                chandef->center_freq2 = 0;
 142
 143                /* NOTE: Disabling radar is only valid here for
 144                 * single channel context. To be sure, check it ...
 145                 */
 146                if (local->hw.conf.radar_enabled)
 147                        check_single_channel = true;
 148                local->hw.conf.radar_enabled = false;
 149
 150                ieee80211_hw_config(local, 0);
 151        } else {
 152                drv_remove_chanctx(local, ctx);
 153        }
 154
 155        list_del_rcu(&ctx->list);
 156        kfree_rcu(ctx, rcu_head);
 157
 158        /* throw a warning if this wasn't the only channel context. */
 159        WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
 160
 161        mutex_lock(&local->mtx);
 162        ieee80211_recalc_idle(local);
 163        mutex_unlock(&local->mtx);
 164}
 165
 166static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
 167                                        struct ieee80211_chanctx *ctx)
 168{
 169        struct ieee80211_local *local = sdata->local;
 170        int ret;
 171
 172        lockdep_assert_held(&local->chanctx_mtx);
 173
 174        ret = drv_assign_vif_chanctx(local, sdata, ctx);
 175        if (ret)
 176                return ret;
 177
 178        rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
 179        ctx->refcount++;
 180
 181        ieee80211_recalc_txpower(sdata);
 182        sdata->vif.bss_conf.idle = false;
 183
 184        if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
 185            sdata->vif.type != NL80211_IFTYPE_MONITOR)
 186                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
 187
 188        return 0;
 189}
 190
 191static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
 192                                              struct ieee80211_chanctx *ctx)
 193{
 194        struct ieee80211_chanctx_conf *conf = &ctx->conf;
 195        struct ieee80211_sub_if_data *sdata;
 196        const struct cfg80211_chan_def *compat = NULL;
 197
 198        lockdep_assert_held(&local->chanctx_mtx);
 199
 200        rcu_read_lock();
 201        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 202
 203                if (!ieee80211_sdata_running(sdata))
 204                        continue;
 205                if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
 206                        continue;
 207
 208                if (!compat)
 209                        compat = &sdata->vif.bss_conf.chandef;
 210
 211                compat = cfg80211_chandef_compatible(
 212                                &sdata->vif.bss_conf.chandef, compat);
 213                if (!compat)
 214                        break;
 215        }
 216        rcu_read_unlock();
 217
 218        if (WARN_ON_ONCE(!compat))
 219                return;
 220
 221        ieee80211_change_chanctx(local, ctx, compat);
 222}
 223
 224static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
 225                                           struct ieee80211_chanctx *ctx)
 226{
 227        struct ieee80211_local *local = sdata->local;
 228
 229        lockdep_assert_held(&local->chanctx_mtx);
 230
 231        ctx->refcount--;
 232        rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
 233
 234        sdata->vif.bss_conf.idle = true;
 235
 236        if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
 237            sdata->vif.type != NL80211_IFTYPE_MONITOR)
 238                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
 239
 240        drv_unassign_vif_chanctx(local, sdata, ctx);
 241
 242        if (ctx->refcount > 0) {
 243                ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
 244                ieee80211_recalc_smps_chanctx(local, ctx);
 245                ieee80211_recalc_radar_chanctx(local, ctx);
 246        }
 247}
 248
 249static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 250{
 251        struct ieee80211_local *local = sdata->local;
 252        struct ieee80211_chanctx_conf *conf;
 253        struct ieee80211_chanctx *ctx;
 254
 255        lockdep_assert_held(&local->chanctx_mtx);
 256
 257        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
 258                                         lockdep_is_held(&local->chanctx_mtx));
 259        if (!conf)
 260                return;
 261
 262        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 263
 264        ieee80211_unassign_vif_chanctx(sdata, ctx);
 265        if (ctx->refcount == 0)
 266                ieee80211_free_chanctx(local, ctx);
 267}
 268
 269void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
 270                                    struct ieee80211_chanctx *chanctx)
 271{
 272        bool radar_enabled;
 273
 274        lockdep_assert_held(&local->chanctx_mtx);
 275
 276        radar_enabled = ieee80211_is_radar_required(local);
 277
 278        if (radar_enabled == chanctx->conf.radar_enabled)
 279                return;
 280
 281        chanctx->conf.radar_enabled = radar_enabled;
 282        local->radar_detect_enabled = chanctx->conf.radar_enabled;
 283
 284        if (!local->use_chanctx) {
 285                local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
 286                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 287        }
 288
 289        drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
 290}
 291
 292void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
 293                                   struct ieee80211_chanctx *chanctx)
 294{
 295        struct ieee80211_sub_if_data *sdata;
 296        u8 rx_chains_static, rx_chains_dynamic;
 297
 298        lockdep_assert_held(&local->chanctx_mtx);
 299
 300        rx_chains_static = 1;
 301        rx_chains_dynamic = 1;
 302
 303        rcu_read_lock();
 304        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 305                u8 needed_static, needed_dynamic;
 306
 307                if (!ieee80211_sdata_running(sdata))
 308                        continue;
 309
 310                if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
 311                                                &chanctx->conf)
 312                        continue;
 313
 314                switch (sdata->vif.type) {
 315                case NL80211_IFTYPE_P2P_DEVICE:
 316                        continue;
 317                case NL80211_IFTYPE_STATION:
 318                        if (!sdata->u.mgd.associated)
 319                                continue;
 320                        break;
 321                case NL80211_IFTYPE_AP_VLAN:
 322                        continue;
 323                case NL80211_IFTYPE_AP:
 324                case NL80211_IFTYPE_ADHOC:
 325                case NL80211_IFTYPE_WDS:
 326                case NL80211_IFTYPE_MESH_POINT:
 327                        break;
 328                default:
 329                        WARN_ON_ONCE(1);
 330                }
 331
 332                switch (sdata->smps_mode) {
 333                default:
 334                        WARN_ONCE(1, "Invalid SMPS mode %d\n",
 335                                  sdata->smps_mode);
 336                        /* fall through */
 337                case IEEE80211_SMPS_OFF:
 338                        needed_static = sdata->needed_rx_chains;
 339                        needed_dynamic = sdata->needed_rx_chains;
 340                        break;
 341                case IEEE80211_SMPS_DYNAMIC:
 342                        needed_static = 1;
 343                        needed_dynamic = sdata->needed_rx_chains;
 344                        break;
 345                case IEEE80211_SMPS_STATIC:
 346                        needed_static = 1;
 347                        needed_dynamic = 1;
 348                        break;
 349                }
 350
 351                rx_chains_static = max(rx_chains_static, needed_static);
 352                rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
 353        }
 354        rcu_read_unlock();
 355
 356        if (!local->use_chanctx) {
 357                if (rx_chains_static > 1)
 358                        local->smps_mode = IEEE80211_SMPS_OFF;
 359                else if (rx_chains_dynamic > 1)
 360                        local->smps_mode = IEEE80211_SMPS_DYNAMIC;
 361                else
 362                        local->smps_mode = IEEE80211_SMPS_STATIC;
 363                ieee80211_hw_config(local, 0);
 364        }
 365
 366        if (rx_chains_static == chanctx->conf.rx_chains_static &&
 367            rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
 368                return;
 369
 370        chanctx->conf.rx_chains_static = rx_chains_static;
 371        chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
 372        drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
 373}
 374
 375int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
 376                              const struct cfg80211_chan_def *chandef,
 377                              enum ieee80211_chanctx_mode mode)
 378{
 379        struct ieee80211_local *local = sdata->local;
 380        struct ieee80211_chanctx *ctx;
 381        int ret;
 382
 383        WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
 384
 385        mutex_lock(&local->chanctx_mtx);
 386        __ieee80211_vif_release_channel(sdata);
 387
 388        ctx = ieee80211_find_chanctx(local, chandef, mode);
 389        if (!ctx)
 390                ctx = ieee80211_new_chanctx(local, chandef, mode);
 391        if (IS_ERR(ctx)) {
 392                ret = PTR_ERR(ctx);
 393                goto out;
 394        }
 395
 396        sdata->vif.bss_conf.chandef = *chandef;
 397
 398        ret = ieee80211_assign_vif_chanctx(sdata, ctx);
 399        if (ret) {
 400                /* if assign fails refcount stays the same */
 401                if (ctx->refcount == 0)
 402                        ieee80211_free_chanctx(local, ctx);
 403                goto out;
 404        }
 405
 406        ieee80211_recalc_smps_chanctx(local, ctx);
 407        ieee80211_recalc_radar_chanctx(local, ctx);
 408 out:
 409        mutex_unlock(&local->chanctx_mtx);
 410        return ret;
 411}
 412
 413int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
 414                                   const struct cfg80211_chan_def *chandef,
 415                                   u32 *changed)
 416{
 417        struct ieee80211_local *local = sdata->local;
 418        struct ieee80211_chanctx_conf *conf;
 419        struct ieee80211_chanctx *ctx;
 420        int ret;
 421
 422        if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
 423                                     IEEE80211_CHAN_DISABLED))
 424                return -EINVAL;
 425
 426        mutex_lock(&local->chanctx_mtx);
 427        if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
 428                ret = 0;
 429                goto out;
 430        }
 431
 432        if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
 433            sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
 434                ret = -EINVAL;
 435                goto out;
 436        }
 437
 438        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
 439                                         lockdep_is_held(&local->chanctx_mtx));
 440        if (!conf) {
 441                ret = -EINVAL;
 442                goto out;
 443        }
 444
 445        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 446        if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
 447                ret = -EINVAL;
 448                goto out;
 449        }
 450
 451        sdata->vif.bss_conf.chandef = *chandef;
 452
 453        ieee80211_recalc_chanctx_chantype(local, ctx);
 454
 455        *changed |= BSS_CHANGED_BANDWIDTH;
 456        ret = 0;
 457 out:
 458        mutex_unlock(&local->chanctx_mtx);
 459        return ret;
 460}
 461
 462void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 463{
 464        WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
 465
 466        mutex_lock(&sdata->local->chanctx_mtx);
 467        __ieee80211_vif_release_channel(sdata);
 468        mutex_unlock(&sdata->local->chanctx_mtx);
 469}
 470
 471void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
 472{
 473        struct ieee80211_local *local = sdata->local;
 474        struct ieee80211_sub_if_data *ap;
 475        struct ieee80211_chanctx_conf *conf;
 476
 477        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
 478                return;
 479
 480        ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
 481
 482        mutex_lock(&local->chanctx_mtx);
 483
 484        conf = rcu_dereference_protected(ap->vif.chanctx_conf,
 485                                         lockdep_is_held(&local->chanctx_mtx));
 486        rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
 487        mutex_unlock(&local->chanctx_mtx);
 488}
 489
 490void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
 491                                         bool clear)
 492{
 493        struct ieee80211_local *local = sdata->local;
 494        struct ieee80211_sub_if_data *vlan;
 495        struct ieee80211_chanctx_conf *conf;
 496
 497        ASSERT_RTNL();
 498
 499        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
 500                return;
 501
 502        mutex_lock(&local->chanctx_mtx);
 503
 504        /*
 505         * Check that conf exists, even when clearing this function
 506         * must be called with the AP's channel context still there
 507         * as it would otherwise cause VLANs to have an invalid
 508         * channel context pointer for a while, possibly pointing
 509         * to a channel context that has already been freed.
 510         */
 511        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
 512                                lockdep_is_held(&local->chanctx_mtx));
 513        WARN_ON(!conf);
 514
 515        if (clear)
 516                conf = NULL;
 517
 518        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
 519                rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
 520
 521        mutex_unlock(&local->chanctx_mtx);
 522}
 523
 524void ieee80211_iter_chan_contexts_atomic(
 525        struct ieee80211_hw *hw,
 526        void (*iter)(struct ieee80211_hw *hw,
 527                     struct ieee80211_chanctx_conf *chanctx_conf,
 528                     void *data),
 529        void *iter_data)
 530{
 531        struct ieee80211_local *local = hw_to_local(hw);
 532        struct ieee80211_chanctx *ctx;
 533
 534        rcu_read_lock();
 535        list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
 536                if (ctx->driver_present)
 537                        iter(hw, &ctx->conf, iter_data);
 538        rcu_read_unlock();
 539}
 540EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
 541