Search code examples
cstructlinux-kernelflexible-array-member

Why more memory (than being required) is allocated to a struct with flexible array member in cfg80211 scan request?


I am learning linux wifi drivers, and was exploring the code in cfg80211 subsytem for a scan request.

I can't understand why the following struct is allocated more memory than required. Or, I can't understand the way the size of memory to be allocated is calculated in this way.

The struct is defined in include/net/cfg80211.h:

struct cfg80211_scan_request {
    struct cfg80211_ssid *ssids;
    int n_ssids;
    u32 n_channels;
    enum nl80211_bss_scan_width scan_width;
    const u8 *ie;
    size_t ie_len;
    u16 duration;
    bool duration_mandatory;
    u32 flags;

    u32 rates[NUM_NL80211_BANDS];

    struct wireless_dev *wdev;

    u8 mac_addr[ETH_ALEN] __aligned(2);
    u8 mac_addr_mask[ETH_ALEN] __aligned(2);
    u8 bssid[ETH_ALEN] __aligned(2);

    /* internal */
    struct wiphy *wiphy;
    unsigned long scan_start;
    struct cfg80211_scan_info info;
    bool notified;
    bool no_cck;

    /* keep last */
    struct ieee80211_channel *channels[0];
};

In file /net/wireless/nl80211.c, memory is being allocated to the struct as follows:

static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
    // ...
    struct cfg80211_scan_request *request;
    // ...
    request = kzalloc(sizeof(*request)
            + sizeof(*request->ssids) * n_ssids
            + sizeof(*request->channels) * n_channels
            + ie_len, GFP_KERNEL);
    // ...
}

I have a doubt that more memory is being allocated than required. Or, the way size is calculated in kzalloc doesn't make sense to me.

This is what I expect the code to be like:

static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
    // ...
    struct cfg80211_scan_request *request;
    // ...
    request = kzalloc(sizeof(*request)
            + sizeof(*request->channels) * n_channels, 
              GFP_KERNEL);
    // ...
}

Why that much size of memory is allocated then? What I'm understanding is kzalloc(struct + flexible_array_member + extra_len_but_why).


Solution

  • Presumably the code after the kzalloc sets the values of the pointers requests->ssids and requests->ie so that they point into the newly allocated memory, just after the struct.

    That way, only one block of memory needs to be allocated for the three arrays.

    EDIT: I found the function in question, and indeed, just after the kzalloc call shown in the OP, we find the following, which sets up the ssids and ie pointers:

    if (n_ssids)
        request->ssids = (void *)&request->channels[n_channels];
    request->n_ssids = n_ssids;
    if (ie_len) {
        if (n_ssids)
            request->ie = (void *)(request->ssids + n_ssids);
        else
            request->ie = (void *)(request->channels + n_channels);
    }