<section id="ps-client">
<title>support for powersaving clients</title>
!Pinclude/net/mac80211.h AP support for powersaving clients
- </section>
!Finclude/net/mac80211.h ieee80211_get_buffered_bc
!Finclude/net/mac80211.h ieee80211_beacon_get
!Finclude/net/mac80211.h ieee80211_sta_eosp
!Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni
!Finclude/net/mac80211.h ieee80211_sta_set_buffered
!Finclude/net/mac80211.h ieee80211_sta_block_awake
+ </section>
</chapter>
<chapter id="multi-iface">
<title>RX A-MPDU aggregation</title>
!Pnet/mac80211/agg-rx.c RX A-MPDU aggregation
!Cnet/mac80211/agg-rx.c
- </sect1>
!Finclude/net/mac80211.h ieee80211_ampdu_mlme_action
+ </sect1>
</chapter>
<chapter id="smps">
.owner = THIS_MODULE,
};
-int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
- int write)
+int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len)
{
- unsigned long from = (unsigned long)addr;
- unsigned long npages;
- unsigned off;
- int err;
- int i;
-
- err = -EFAULT;
- if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len))
- goto out;
-
- off = from & ~PAGE_MASK;
- npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
- if (npages > ALG_MAX_PAGES)
- npages = ALG_MAX_PAGES;
+ size_t off;
+ ssize_t n;
+ int npages, i;
- err = get_user_pages_fast(from, npages, write, sgl->pages);
- if (err < 0)
- goto out;
+ n = iov_iter_get_pages(iter, sgl->pages, len, ALG_MAX_PAGES, &off);
+ if (n < 0)
+ return n;
- npages = err;
- err = -EINVAL;
+ npages = PAGE_ALIGN(off + n);
if (WARN_ON(npages == 0))
- goto out;
-
- err = 0;
+ return -EINVAL;
sg_init_table(sgl->sg, npages);
- for (i = 0; i < npages; i++) {
+ for (i = 0, len = n; i < npages; i++) {
int plen = min_t(int, len, PAGE_SIZE - off);
sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
off = 0;
len -= plen;
- err += plen;
}
-
-out:
- return err;
+ return n;
}
EXPORT_SYMBOL_GPL(af_alg_make_sg);
struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk);
struct hash_ctx *ctx = ask->private;
- unsigned long iovlen;
- const struct iovec *iov;
long copied = 0;
int err;
ctx->more = 0;
- for (iov = msg->msg_iter.iov, iovlen = msg->msg_iter.nr_segs; iovlen > 0;
- iovlen--, iov++) {
- unsigned long seglen = iov->iov_len;
- char __user *from = iov->iov_base;
+ while (iov_iter_count(&msg->msg_iter)) {
+ int len = iov_iter_count(&msg->msg_iter);
- while (seglen) {
- int len = min_t(unsigned long, seglen, limit);
- int newlen;
+ if (len > limit)
+ len = limit;
- newlen = af_alg_make_sg(&ctx->sgl, from, len, 0);
- if (newlen < 0) {
- err = copied ? 0 : newlen;
- goto unlock;
- }
-
- ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL,
- newlen);
-
- err = af_alg_wait_for_completion(
- crypto_ahash_update(&ctx->req),
- &ctx->completion);
+ len = af_alg_make_sg(&ctx->sgl, &msg->msg_iter, len);
+ if (len < 0) {
+ err = copied ? 0 : len;
+ goto unlock;
+ }
- af_alg_free_sg(&ctx->sgl);
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL, len);
- if (err)
- goto unlock;
+ err = af_alg_wait_for_completion(crypto_ahash_update(&ctx->req),
+ &ctx->completion);
+ af_alg_free_sg(&ctx->sgl);
+ if (err)
+ goto unlock;
- seglen -= newlen;
- from += newlen;
- copied += newlen;
- }
+ copied += len;
+ iov_iter_advance(&msg->msg_iter, len);
}
err = 0;
&ctx->req));
struct skcipher_sg_list *sgl;
struct scatterlist *sg;
- unsigned long iovlen;
- const struct iovec *iov;
int err = -EAGAIN;
int used;
long copied = 0;
lock_sock(sk);
- for (iov = msg->msg_iter.iov, iovlen = msg->msg_iter.nr_segs; iovlen > 0;
- iovlen--, iov++) {
- unsigned long seglen = iov->iov_len;
- char __user *from = iov->iov_base;
-
- while (seglen) {
- sgl = list_first_entry(&ctx->tsgl,
- struct skcipher_sg_list, list);
- sg = sgl->sg;
-
- while (!sg->length)
- sg++;
-
- if (!ctx->used) {
- err = skcipher_wait_for_data(sk, flags);
- if (err)
- goto unlock;
- }
+ while (iov_iter_count(&msg->msg_iter)) {
+ sgl = list_first_entry(&ctx->tsgl,
+ struct skcipher_sg_list, list);
+ sg = sgl->sg;
- used = min_t(unsigned long, ctx->used, seglen);
+ while (!sg->length)
+ sg++;
- used = af_alg_make_sg(&ctx->rsgl, from, used, 1);
- err = used;
- if (err < 0)
+ used = ctx->used;
+ if (!used) {
+ err = skcipher_wait_for_data(sk, flags);
+ if (err)
goto unlock;
+ }
+
+ used = min_t(unsigned long, used, iov_iter_count(&msg->msg_iter));
+
+ used = af_alg_make_sg(&ctx->rsgl, &msg->msg_iter, used);
+ err = used;
+ if (err < 0)
+ goto unlock;
- if (ctx->more || used < ctx->used)
- used -= used % bs;
+ if (ctx->more || used < ctx->used)
+ used -= used % bs;
- err = -EINVAL;
- if (!used)
- goto free;
+ err = -EINVAL;
+ if (!used)
+ goto free;
- ablkcipher_request_set_crypt(&ctx->req, sg,
- ctx->rsgl.sg, used,
- ctx->iv);
+ ablkcipher_request_set_crypt(&ctx->req, sg,
+ ctx->rsgl.sg, used,
+ ctx->iv);
- err = af_alg_wait_for_completion(
+ err = af_alg_wait_for_completion(
ctx->enc ?
crypto_ablkcipher_encrypt(&ctx->req) :
crypto_ablkcipher_decrypt(&ctx->req),
&ctx->completion);
free:
- af_alg_free_sg(&ctx->rsgl);
+ af_alg_free_sg(&ctx->rsgl);
- if (err)
- goto unlock;
+ if (err)
+ goto unlock;
- copied += used;
- from += used;
- seglen -= used;
- skcipher_pull_sgl(sk, used);
- }
+ copied += used;
+ skcipher_pull_sgl(sk, used);
+ iov_iter_advance(&msg->msg_iter, used);
}
err = 0;
{ USB_DEVICE(0x13d3, 0x3393) },
{ USB_DEVICE(0x13d3, 0x3402) },
{ USB_DEVICE(0x13d3, 0x3408) },
+ { USB_DEVICE(0x13d3, 0x3423) },
{ USB_DEVICE(0x13d3, 0x3432) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
#define USB_REQ_DFU_DNLOAD 1
#define BULK_SIZE 4096
#define FW_HDR_SIZE 20
+#define TIMEGAP_USEC_MIN 50
+#define TIMEGAP_USEC_MAX 100
static int ath3k_load_firmware(struct usb_device *udev,
const struct firmware *firmware)
pipe = usb_sndbulkpipe(udev, 0x02);
while (count) {
+ /* workaround the compatibility issue with xHCI controller*/
+ usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
+
size = min_t(uint, count, BULK_SIZE);
memcpy(send_buf, firmware->data + sent, size);
pipe = usb_sndbulkpipe(udev, 0x02);
while (count) {
+ /* workaround the compatibility issue with xHCI controller*/
+ usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
+
size = min_t(uint, count, BULK_SIZE);
memcpy(send_buf, firmware->data + sent, size);
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#define VERSION "0.6"
+#define VERSION "0.7"
static bool disable_scofix;
static bool force_scofix;
#define BTUSB_BCM_PATCHRAM 0x400
#define BTUSB_MARVELL 0x800
#define BTUSB_SWAVE 0x1000
+#define BTUSB_INTEL_NEW 0x2000
+#define BTUSB_AMP 0x4000
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+ /* Generic Bluetooth AMP device */
+ { USB_DEVICE_INFO(0xe0, 0x01, 0x04), .driver_info = BTUSB_AMP },
+
/* Apple-specific (Broadcom) devices */
{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
{ USB_DEVICE(0x13d3, 0x3404),
.driver_info = BTUSB_BCM_PATCHRAM },
+ /* Broadcom BCM20702B0 (Dynex/Insignia) */
+ { USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
+
/* Foxconn - Hon Hai */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
.driver_info = BTUSB_BCM_PATCHRAM },
+ /* Lite-On Technology - Broadcom based */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x04ca, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
+
/* Broadcom devices with vendor specific id */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
.driver_info = BTUSB_BCM_PATCHRAM },
/* ASUSTek Computer - Broadcom based */
- { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* Belkin F8065bf - Broadcom based */
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x16d3, 0x0002),
.driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC },
- /* Intel Bluetooth device */
+ /* Marvell Bluetooth devices */
+ { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
+ { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
+
+ /* Intel Bluetooth devices */
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
+ { USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_NEW },
- /* Marvell device */
- { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
- { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
+ /* Other Intel Bluetooth devices */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01),
+ .driver_info = BTUSB_IGNORE },
{ } /* Terminating entry */
};
#define BTUSB_ISOC_RUNNING 2
#define BTUSB_SUSPENDING 3
#define BTUSB_DID_ISO_RESUME 4
+#define BTUSB_BOOTLOADER 5
+#define BTUSB_DOWNLOADING 6
+#define BTUSB_FIRMWARE_LOADED 7
+#define BTUSB_FIRMWARE_FAILED 8
+#define BTUSB_BOOTING 9
struct btusb_data {
struct hci_dev *hdev;
struct usb_endpoint_descriptor *isoc_rx_ep;
__u8 cmdreq_type;
+ __u8 cmdreq;
unsigned int sco_num;
int isoc_altsetting;
int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
};
+static int btusb_wait_on_bit_timeout(void *word, int bit, unsigned long timeout,
+ unsigned mode)
+{
+ might_sleep();
+ if (!test_bit(bit, word))
+ return 0;
+ return out_of_line_wait_on_bit_timeout(word, bit, bit_wait_timeout,
+ mode, timeout);
+}
+
static inline void btusb_free_frags(struct btusb_data *data)
{
unsigned long flags;
}
dr->bRequestType = data->cmdreq_type;
- dr->bRequest = 0;
+ dr->bRequest = data->cmdreq;
dr->wIndex = 0;
dr->wValue = 0;
dr->wLength = __cpu_to_le16(skb->len);
u8 fw_patch_num;
} __packed;
+struct intel_boot_params {
+ __u8 status;
+ __u8 otp_format;
+ __u8 otp_content;
+ __u8 otp_patch;
+ __le16 dev_revid;
+ __u8 secure_boot;
+ __u8 key_from_hdr;
+ __u8 key_type;
+ __u8 otp_lock;
+ __u8 api_lock;
+ __u8 debug_lock;
+ bdaddr_t otp_bdaddr;
+ __u8 min_fw_build_nn;
+ __u8 min_fw_build_cw;
+ __u8 min_fw_build_yy;
+ __u8 limited_cce;
+ __u8 unlocked_state;
+} __packed;
+
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
struct intel_version *ver)
{
return 0;
}
+static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
+{
+ struct sk_buff *skb;
+ struct hci_event_hdr *hdr;
+ struct hci_ev_cmd_complete *evt;
+
+ skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr = (struct hci_event_hdr *)skb_put(skb, sizeof(*hdr));
+ hdr->evt = HCI_EV_CMD_COMPLETE;
+ hdr->plen = sizeof(*evt) + 1;
+
+ evt = (struct hci_ev_cmd_complete *)skb_put(skb, sizeof(*evt));
+ evt->ncmd = 0x01;
+ evt->opcode = cpu_to_le16(opcode);
+
+ *skb_put(skb, 1) = 0x00;
+
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+
+ return hci_recv_frame(hdev, skb);
+}
+
+static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
+ int count)
+{
+ /* When the device is in bootloader mode, then it can send
+ * events via the bulk endpoint. These events are treated the
+ * same way as the ones received from the interrupt endpoint.
+ */
+ if (test_bit(BTUSB_BOOTLOADER, &data->flags))
+ return btusb_recv_intr(data, buffer, count);
+
+ return btusb_recv_bulk(data, buffer, count);
+}
+
+static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btusb_data *data = hci_get_drvdata(hdev);
+
+ if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
+ struct hci_event_hdr *hdr = (void *)skb->data;
+
+ /* When the firmware loading completes the device sends
+ * out a vendor specific event indicating the result of
+ * the firmware loading.
+ */
+ if (skb->len == 7 && hdr->evt == 0xff && hdr->plen == 0x05 &&
+ skb->data[2] == 0x06) {
+ if (skb->data[3] != 0x00)
+ test_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
+
+ if (test_and_clear_bit(BTUSB_DOWNLOADING,
+ &data->flags) &&
+ test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
+ smp_mb__after_atomic();
+ wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
+ }
+ }
+
+ /* When switching to the operational firmware the device
+ * sends a vendor specific event indicating that the bootup
+ * completed.
+ */
+ if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 &&
+ skb->data[2] == 0x02) {
+ if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
+ smp_mb__after_atomic();
+ wake_up_bit(&data->flags, BTUSB_BOOTING);
+ }
+ }
+ }
+
+ return hci_recv_frame(hdev, skb);
+}
+
+static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btusb_data *data = hci_get_drvdata(hdev);
+ struct urb *urb;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
+ struct hci_command_hdr *cmd = (void *)skb->data;
+ __u16 opcode = le16_to_cpu(cmd->opcode);
+
+ /* When in bootloader mode and the command 0xfc09
+ * is received, it needs to be send down the
+ * bulk endpoint. So allocate a bulk URB instead.
+ */
+ if (opcode == 0xfc09)
+ urb = alloc_bulk_urb(hdev, skb);
+ else
+ urb = alloc_ctrl_urb(hdev, skb);
+
+ /* When the 0xfc01 command is issued to boot into
+ * the operational firmware, it will actually not
+ * send a command complete event. To keep the flow
+ * control working inject that event here.
+ */
+ if (opcode == 0xfc01)
+ inject_cmd_complete(hdev, opcode);
+ } else {
+ urb = alloc_ctrl_urb(hdev, skb);
+ }
+ if (IS_ERR(urb))
+ return PTR_ERR(urb);
+
+ hdev->stat.cmd_tx++;
+ return submit_or_queue_tx_urb(hdev, urb);
+
+ case HCI_ACLDATA_PKT:
+ urb = alloc_bulk_urb(hdev, skb);
+ if (IS_ERR(urb))
+ return PTR_ERR(urb);
+
+ hdev->stat.acl_tx++;
+ return submit_or_queue_tx_urb(hdev, urb);
+
+ case HCI_SCODATA_PKT:
+ if (hci_conn_num(hdev, SCO_LINK) < 1)
+ return -ENODEV;
+
+ urb = alloc_isoc_urb(hdev, skb);
+ if (IS_ERR(urb))
+ return PTR_ERR(urb);
+
+ hdev->stat.sco_tx++;
+ return submit_tx_urb(hdev, urb);
+ }
+
+ return -EILSEQ;
+}
+
+static int btusb_intel_secure_send(struct hci_dev *hdev, u8 fragment_type,
+ u32 plen, const void *param)
+{
+ while (plen > 0) {
+ struct sk_buff *skb;
+ u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen;
+
+ cmd_param[0] = fragment_type;
+ memcpy(cmd_param + 1, param, fragment_len);
+
+ skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1,
+ cmd_param, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ kfree_skb(skb);
+
+ plen -= fragment_len;
+ param += fragment_len;
+ }
+
+ return 0;
+}
+
+static void btusb_intel_version_info(struct hci_dev *hdev,
+ struct intel_version *ver)
+{
+ const char *variant;
+
+ switch (ver->fw_variant) {
+ case 0x06:
+ variant = "Bootloader";
+ break;
+ case 0x23:
+ variant = "Firmware";
+ break;
+ default:
+ return;
+ }
+
+ BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name,
+ variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
+ ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy);
+}
+
+static int btusb_setup_intel_new(struct hci_dev *hdev)
+{
+ static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x08, 0x04, 0x00 };
+ struct btusb_data *data = hci_get_drvdata(hdev);
+ struct sk_buff *skb;
+ struct intel_version *ver;
+ struct intel_boot_params *params;
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ char fwname[64];
+ ktime_t calltime, delta, rettime;
+ unsigned long long duration;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ calltime = ktime_get();
+
+ /* Read the Intel version information to determine if the device
+ * is in bootloader mode or if it already has operational firmware
+ * loaded.
+ */
+ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Reading Intel version information failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ if (skb->len != sizeof(*ver)) {
+ BT_ERR("%s: Intel version event size mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EILSEQ;
+ }
+
+ ver = (struct intel_version *)skb->data;
+ if (ver->status) {
+ BT_ERR("%s: Intel version command failure (%02x)",
+ hdev->name, ver->status);
+ err = -bt_to_errno(ver->status);
+ kfree_skb(skb);
+ return err;
+ }
+
+ /* The hardware platform number has a fixed value of 0x37 and
+ * for now only accept this single value.
+ */
+ if (ver->hw_platform != 0x37) {
+ BT_ERR("%s: Unsupported Intel hardware platform (%u)",
+ hdev->name, ver->hw_platform);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* At the moment only the hardware variant iBT 3.0 (LnP/SfP) is
+ * supported by this firmware loading method. This check has been
+ * put in place to ensure correct forward compatibility options
+ * when newer hardware variants come along.
+ */
+ if (ver->hw_variant != 0x0b) {
+ BT_ERR("%s: Unsupported Intel hardware variant (%u)",
+ hdev->name, ver->hw_variant);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ btusb_intel_version_info(hdev, ver);
+
+ /* The firmware variant determines if the device is in bootloader
+ * mode or is running operational firmware. The value 0x06 identifies
+ * the bootloader and the value 0x23 identifies the operational
+ * firmware.
+ *
+ * When the operational firmware is already present, then only
+ * the check for valid Bluetooth device address is needed. This
+ * determines if the device will be added as configured or
+ * unconfigured controller.
+ *
+ * It is not possible to use the Secure Boot Parameters in this
+ * case since that command is only available in bootloader mode.
+ */
+ if (ver->fw_variant == 0x23) {
+ kfree_skb(skb);
+ clear_bit(BTUSB_BOOTLOADER, &data->flags);
+ btusb_check_bdaddr_intel(hdev);
+ return 0;
+ }
+
+ /* If the device is not in bootloader mode, then the only possible
+ * choice is to return an error and abort the device initialization.
+ */
+ if (ver->fw_variant != 0x06) {
+ BT_ERR("%s: Unsupported Intel firmware variant (%u)",
+ hdev->name, ver->fw_variant);
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ kfree_skb(skb);
+
+ /* Read the secure boot parameters to identify the operating
+ * details of the bootloader.
+ */
+ skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Reading Intel boot parameters failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ if (skb->len != sizeof(*params)) {
+ BT_ERR("%s: Intel boot parameters size mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EILSEQ;
+ }
+
+ params = (struct intel_boot_params *)skb->data;
+ if (params->status) {
+ BT_ERR("%s: Intel boot parameters command failure (%02x)",
+ hdev->name, params->status);
+ err = -bt_to_errno(params->status);
+ kfree_skb(skb);
+ return err;
+ }
+
+ BT_INFO("%s: Device revision is %u", hdev->name,
+ le16_to_cpu(params->dev_revid));
+
+ BT_INFO("%s: Secure boot is %s", hdev->name,
+ params->secure_boot ? "enabled" : "disabled");
+
+ BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
+ params->min_fw_build_nn, params->min_fw_build_cw,
+ 2000 + params->min_fw_build_yy);
+
+ /* It is required that every single firmware fragment is acknowledged
+ * with a command complete event. If the boot parameters indicate
+ * that this bootloader does not send them, then abort the setup.
+ */
+ if (params->limited_cce != 0x00) {
+ BT_ERR("%s: Unsupported Intel firmware loading method (%u)",
+ hdev->name, params->limited_cce);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* If the OTP has no valid Bluetooth device address, then there will
+ * also be no valid address for the operational firmware.
+ */
+ if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) {
+ BT_INFO("%s: No device address configured", hdev->name);
+ set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ }
+
+ /* With this Intel bootloader only the hardware variant and device
+ * revision information are used to select the right firmware.
+ *
+ * Currently this bootloader support is limited to hardware variant
+ * iBT 3.0 (LnP/SfP) which is identified by the value 11 (0x0b).
+ */
+ snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.sfi",
+ le16_to_cpu(params->dev_revid));
+
+ err = request_firmware(&fw, fwname, &hdev->dev);
+ if (err < 0) {
+ BT_ERR("%s: Failed to load Intel firmware file (%d)",
+ hdev->name, err);
+ kfree_skb(skb);
+ return err;
+ }
+
+ BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
+
+ kfree_skb(skb);
+
+ if (fw->size < 644) {
+ BT_ERR("%s: Invalid size of firmware file (%zu)",
+ hdev->name, fw->size);
+ err = -EBADF;
+ goto done;
+ }
+
+ set_bit(BTUSB_DOWNLOADING, &data->flags);
+
+ /* Start the firmware download transaction with the Init fragment
+ * represented by the 128 bytes of CSS header.
+ */
+ err = btusb_intel_secure_send(hdev, 0x00, 128, fw->data);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send firmware header (%d)",
+ hdev->name, err);
+ goto done;
+ }
+
+ /* Send the 256 bytes of public key information from the firmware
+ * as the PKey fragment.
+ */
+ err = btusb_intel_secure_send(hdev, 0x03, 256, fw->data + 128);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send firmware public key (%d)",
+ hdev->name, err);
+ goto done;
+ }
+
+ /* Send the 256 bytes of signature information from the firmware
+ * as the Sign fragment.
+ */
+ err = btusb_intel_secure_send(hdev, 0x02, 256, fw->data + 388);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send firmware signature (%d)",
+ hdev->name, err);
+ goto done;
+ }
+
+ fw_ptr = fw->data + 644;
+
+ while (fw_ptr - fw->data < fw->size) {
+ struct hci_command_hdr *cmd = (void *)fw_ptr;
+ u8 cmd_len;
+
+ cmd_len = sizeof(*cmd) + cmd->plen;
+
+ /* Send each command from the firmware data buffer as
+ * a single Data fragment.
+ */
+ err = btusb_intel_secure_send(hdev, 0x01, cmd_len, fw_ptr);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send firmware data (%d)",
+ hdev->name, err);
+ goto done;
+ }
+
+ fw_ptr += cmd_len;
+ }
+
+ set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
+
+ BT_INFO("%s: Waiting for firmware download to complete", hdev->name);
+
+ /* Before switching the device into operational mode and with that
+ * booting the loaded firmware, wait for the bootloader notification
+ * that all fragments have been successfully received.
+ *
+ * When the event processing receives the notification, then the
+ * BTUSB_DOWNLOADING flag will be cleared.
+ *
+ * The firmware loading should not take longer than 5 seconds
+ * and thus just timeout if that happens and fail the setup
+ * of this device.
+ */
+ err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
+ msecs_to_jiffies(5000),
+ TASK_INTERRUPTIBLE);
+ if (err == 1) {
+ BT_ERR("%s: Firmware loading interrupted", hdev->name);
+ err = -EINTR;
+ goto done;
+ }
+
+ if (err) {
+ BT_ERR("%s: Firmware loading timeout", hdev->name);
+ err = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (test_bit(BTUSB_FIRMWARE_FAILED, &data->flags)) {
+ BT_ERR("%s: Firmware loading failed", hdev->name);
+ err = -ENOEXEC;
+ goto done;
+ }
+
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+
+ BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration);
+
+done:
+ release_firmware(fw);
+
+ if (err < 0)
+ return err;
+
+ calltime = ktime_get();
+
+ set_bit(BTUSB_BOOTING, &data->flags);
+
+ skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ kfree_skb(skb);
+
+ /* The bootloader will not indicate when the device is ready. This
+ * is done by the operational firmware sending bootup notification.
+ *
+ * Booting into operational firmware should not take longer than
+ * 1 second. However if that happens, then just fail the setup
+ * since something went wrong.
+ */
+ BT_INFO("%s: Waiting for device to boot", hdev->name);
+
+ err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
+ msecs_to_jiffies(1000),
+ TASK_INTERRUPTIBLE);
+
+ if (err == 1) {
+ BT_ERR("%s: Device boot interrupted", hdev->name);
+ return -EINTR;
+ }
+
+ if (err) {
+ BT_ERR("%s: Device boot timeout", hdev->name);
+ return -ETIMEDOUT;
+ }
+
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+
+ BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
+
+ clear_bit(BTUSB_BOOTLOADER, &data->flags);
+
+ return 0;
+}
+
+static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code)
+{
+ struct sk_buff *skb;
+ u8 type = 0x00;
+
+ BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code);
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Reset after hardware error failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return;
+ }
+ kfree_skb(skb);
+
+ skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Retrieving Intel exception info failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return;
+ }
+
+ if (skb->len != 13) {
+ BT_ERR("%s: Exception info size mismatch", hdev->name);
+ kfree_skb(skb);
+ return;
+ }
+
+ if (skb->data[0] != 0x00) {
+ BT_ERR("%s: Exception info command failure (%02x)",
+ hdev->name, skb->data[0]);
+ kfree_skb(skb);
+ return;
+ }
+
+ BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1));
+
+ kfree_skb(skb);
+}
+
static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep)
return -ENODEV;
- data->cmdreq_type = USB_TYPE_CLASS;
+ if (id->driver_info & BTUSB_AMP) {
+ data->cmdreq_type = USB_TYPE_CLASS | 0x01;
+ data->cmdreq = 0x2b;
+ } else {
+ data->cmdreq_type = USB_TYPE_CLASS;
+ data->cmdreq = 0x00;
+ }
data->udev = interface_to_usbdev(intf);
data->intf = intf;
init_usb_anchor(&data->isoc_anchor);
spin_lock_init(&data->rxlock);
- data->recv_event = hci_recv_frame;
- data->recv_bulk = btusb_recv_bulk;
+ if (id->driver_info & BTUSB_INTEL_NEW) {
+ data->recv_event = btusb_recv_event_intel;
+ data->recv_bulk = btusb_recv_bulk_intel;
+ set_bit(BTUSB_BOOTLOADER, &data->flags);
+ } else {
+ data->recv_event = hci_recv_frame;
+ data->recv_bulk = btusb_recv_bulk;
+ }
hdev = hci_alloc_dev();
if (!hdev)
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, data);
+ if (id->driver_info & BTUSB_AMP)
+ hdev->dev_type = HCI_AMP;
+ else
+ hdev->dev_type = HCI_BREDR;
+
data->hdev = hdev;
SET_HCIDEV_DEV(hdev, &intf->dev);
if (id->driver_info & BTUSB_INTEL) {
hdev->setup = btusb_setup_intel;
hdev->set_bdaddr = btusb_set_bdaddr_intel;
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ }
+
+ if (id->driver_info & BTUSB_INTEL_NEW) {
+ hdev->send = btusb_send_frame_intel;
+ hdev->setup = btusb_setup_intel_new;
+ hdev->hw_error = btusb_hw_error_intel;
+ hdev->set_bdaddr = btusb_set_bdaddr_intel;
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
}
if (id->driver_info & BTUSB_MARVELL)
if (id->driver_info & BTUSB_INTEL_BOOT)
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
- if (id->driver_info & BTUSB_ATH3012)
+ if (id->driver_info & BTUSB_ATH3012) {
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ }
- /* Interface numbers are hardcoded in the specification */
- data->isoc = usb_ifnum_to_if(data->udev, 1);
+ if (id->driver_info & BTUSB_AMP) {
+ /* AMP controllers do not support SCO packets */
+ data->isoc = NULL;
+ } else {
+ /* Interface numbers are hardcoded in the specification */
+ data->isoc = usb_ifnum_to_if(data->udev, 1);
+ }
if (!reset)
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
else if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
- btusb_free_frags(data);
hci_free_dev(hdev);
}
#include <linux/slab.h>
#include <linux/inet.h>
#include <linux/string.h>
+#include <linux/mlx4/driver.h>
#include "mlx4_ib.h"
enum ib_mtu tmp;
struct mlx4_cmd_mailbox *mailbox;
int err = 0;
+ int is_bonded = mlx4_is_bonded(mdev->dev);
mailbox = mlx4_alloc_cmd_mailbox(mdev->dev);
if (IS_ERR(mailbox))
props->state = IB_PORT_DOWN;
props->phys_state = state_to_phys_state(props->state);
props->active_mtu = IB_MTU_256;
+ if (is_bonded)
+ rtnl_lock(); /* required to get upper dev */
spin_lock_bh(&iboe->lock);
ndev = iboe->netdevs[port - 1];
+ if (ndev && is_bonded)
+ ndev = netdev_master_upper_dev_get(ndev);
if (!ndev)
goto out_unlock;
props->phys_state = state_to_phys_state(props->state);
out_unlock:
spin_unlock_bh(&iboe->lock);
+ if (is_bonded)
+ rtnl_unlock();
out:
mlx4_free_cmd_mailbox(mdev->dev, mailbox);
return err;
struct mlx4_ib_steering {
struct list_head list;
- u64 reg_id;
+ struct mlx4_flow_reg_id reg_id;
union ib_gid gid;
};
struct ib_flow_attr *flow_attr,
int domain)
{
- int err = 0, i = 0;
+ int err = 0, i = 0, j = 0;
struct mlx4_ib_flow *mflow;
enum mlx4_net_trans_promisc_mode type[2];
+ struct mlx4_dev *dev = (to_mdev(qp->device))->dev;
+ int is_bonded = mlx4_is_bonded(dev);
memset(type, 0, sizeof(type));
while (i < ARRAY_SIZE(type) && type[i]) {
err = __mlx4_ib_create_flow(qp, flow_attr, domain, type[i],
- &mflow->reg_id[i]);
+ &mflow->reg_id[i].id);
if (err)
goto err_create_flow;
i++;
+ if (is_bonded) {
+ flow_attr->port = 2;
+ err = __mlx4_ib_create_flow(qp, flow_attr,
+ domain, type[j],
+ &mflow->reg_id[j].mirror);
+ flow_attr->port = 1;
+ if (err)
+ goto err_create_flow;
+ j++;
+ }
+
}
if (i < ARRAY_SIZE(type) && flow_attr->type == IB_FLOW_ATTR_NORMAL) {
- err = mlx4_ib_tunnel_steer_add(qp, flow_attr, &mflow->reg_id[i]);
+ err = mlx4_ib_tunnel_steer_add(qp, flow_attr,
+ &mflow->reg_id[i].id);
if (err)
goto err_create_flow;
i++;
+ if (is_bonded) {
+ flow_attr->port = 2;
+ err = mlx4_ib_tunnel_steer_add(qp, flow_attr,
+ &mflow->reg_id[j].mirror);
+ flow_attr->port = 1;
+ if (err)
+ goto err_create_flow;
+ j++;
+ }
+ /* function to create mirror rule */
}
return &mflow->ibflow;
err_create_flow:
while (i) {
- (void)__mlx4_ib_destroy_flow(to_mdev(qp->device)->dev, mflow->reg_id[i]);
+ (void)__mlx4_ib_destroy_flow(to_mdev(qp->device)->dev,
+ mflow->reg_id[i].id);
i--;
}
+
+ while (j) {
+ (void)__mlx4_ib_destroy_flow(to_mdev(qp->device)->dev,
+ mflow->reg_id[j].mirror);
+ j--;
+ }
err_free:
kfree(mflow);
return ERR_PTR(err);
struct mlx4_ib_dev *mdev = to_mdev(flow_id->qp->device);
struct mlx4_ib_flow *mflow = to_mflow(flow_id);
- while (i < ARRAY_SIZE(mflow->reg_id) && mflow->reg_id[i]) {
- err = __mlx4_ib_destroy_flow(mdev->dev, mflow->reg_id[i]);
+ while (i < ARRAY_SIZE(mflow->reg_id) && mflow->reg_id[i].id) {
+ err = __mlx4_ib_destroy_flow(mdev->dev, mflow->reg_id[i].id);
if (err)
ret = err;
+ if (mflow->reg_id[i].mirror) {
+ err = __mlx4_ib_destroy_flow(mdev->dev,
+ mflow->reg_id[i].mirror);
+ if (err)
+ ret = err;
+ }
i++;
}
{
int err;
struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);
+ struct mlx4_dev *dev = mdev->dev;
struct mlx4_ib_qp *mqp = to_mqp(ibqp);
- u64 reg_id;
struct mlx4_ib_steering *ib_steering = NULL;
enum mlx4_protocol prot = (gid->raw[1] == 0x0e) ?
MLX4_PROT_IB_IPV4 : MLX4_PROT_IB_IPV6;
+ struct mlx4_flow_reg_id reg_id;
if (mdev->dev->caps.steering_mode ==
MLX4_STEERING_MODE_DEVICE_MANAGED) {
err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, mqp->port,
!!(mqp->flags &
MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK),
- prot, ®_id);
+ prot, ®_id.id);
if (err)
goto err_malloc;
+ reg_id.mirror = 0;
+ if (mlx4_is_bonded(dev)) {
+ err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, 2,
+ !!(mqp->flags &
+ MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK),
+ prot, ®_id.mirror);
+ if (err)
+ goto err_add;
+ }
+
err = add_gid_entry(ibqp, gid);
if (err)
goto err_add;
err_add:
mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw,
- prot, reg_id);
+ prot, reg_id.id);
+ if (reg_id.mirror)
+ mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw,
+ prot, reg_id.mirror);
err_malloc:
kfree(ib_steering);
{
int err;
struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);
+ struct mlx4_dev *dev = mdev->dev;
struct mlx4_ib_qp *mqp = to_mqp(ibqp);
struct net_device *ndev;
struct mlx4_ib_gid_entry *ge;
- u64 reg_id = 0;
+ struct mlx4_flow_reg_id reg_id = {0, 0};
+
enum mlx4_protocol prot = (gid->raw[1] == 0x0e) ?
MLX4_PROT_IB_IPV4 : MLX4_PROT_IB_IPV6;
}
err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw,
- prot, reg_id);
+ prot, reg_id.id);
if (err)
return err;
+ if (mlx4_is_bonded(dev)) {
+ err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw,
+ prot, reg_id.mirror);
+ if (err)
+ return err;
+ }
+
mutex_lock(&mqp->mutex);
ge = find_gid_entry(mqp, gid->raw);
if (ge) {
union ib_gid *gids;
int err;
struct mlx4_dev *dev = gw->dev->dev;
+ int is_bonded = mlx4_is_bonded(dev);
if (!gw->dev->ib_active)
return;
if (err)
pr_warn("set port command failed\n");
else
- mlx4_ib_dispatch_event(gw->dev, gw->port, IB_EVENT_GID_CHANGE);
+ if ((gw->port == 1) || !is_bonded)
+ mlx4_ib_dispatch_event(gw->dev,
+ is_bonded ? 1 : gw->port,
+ IB_EVENT_GID_CHANGE);
mlx4_free_cmd_mailbox(dev, mailbox);
kfree(gw);
* don't want the bond IP based gids in the table since
* flows that select port by gid may get the down port.
*/
- if (port_state == IB_PORT_DOWN) {
+ if (port_state == IB_PORT_DOWN &&
+ !mlx4_is_bonded(ibdev->dev)) {
reset_gid_table(ibdev, port);
mlx4_ib_set_default_gid(ibdev,
curr_netdev,
int err;
struct mlx4_ib_iboe *iboe;
int ib_num_ports = 0;
+ int num_req_counters;
pr_info_once("%s", mlx4_ib_version);
MLX4_INIT_DOORBELL_LOCK(&ibdev->uar_lock);
ibdev->dev = dev;
+ ibdev->bond_next_port = 0;
strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX);
ibdev->ib_dev.owner = THIS_MODULE;
ibdev->ib_dev.node_type = RDMA_NODE_IB_CA;
ibdev->ib_dev.local_dma_lkey = dev->caps.reserved_lkey;
ibdev->num_ports = num_ports;
- ibdev->ib_dev.phys_port_cnt = ibdev->num_ports;
+ ibdev->ib_dev.phys_port_cnt = mlx4_is_bonded(dev) ?
+ 1 : ibdev->num_ports;
ibdev->ib_dev.num_comp_vectors = dev->caps.num_comp_vectors;
ibdev->ib_dev.dma_device = &dev->persist->pdev->dev;
if (init_node_data(ibdev))
goto err_map;
- for (i = 0; i < ibdev->num_ports; ++i) {
+ num_req_counters = mlx4_is_bonded(dev) ? 1 : ibdev->num_ports;
+ for (i = 0; i < num_req_counters; ++i) {
mutex_init(&ibdev->qp1_proxy_lock[i]);
if (mlx4_ib_port_link_layer(&ibdev->ib_dev, i + 1) ==
IB_LINK_LAYER_ETHERNET) {
ibdev->counters[i] = -1;
}
}
+ if (mlx4_is_bonded(dev))
+ for (i = 1; i < ibdev->num_ports ; ++i)
+ ibdev->counters[i] = ibdev->counters[0];
+
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
ib_num_ports++;
return;
}
+static void handle_bonded_port_state_event(struct work_struct *work)
+{
+ struct ib_event_work *ew =
+ container_of(work, struct ib_event_work, work);
+ struct mlx4_ib_dev *ibdev = ew->ib_dev;
+ enum ib_port_state bonded_port_state = IB_PORT_NOP;
+ int i;
+ struct ib_event ibev;
+
+ kfree(ew);
+ spin_lock_bh(&ibdev->iboe.lock);
+ for (i = 0; i < MLX4_MAX_PORTS; ++i) {
+ struct net_device *curr_netdev = ibdev->iboe.netdevs[i];
+
+ enum ib_port_state curr_port_state =
+ (netif_running(curr_netdev) &&
+ netif_carrier_ok(curr_netdev)) ?
+ IB_PORT_ACTIVE : IB_PORT_DOWN;
+
+ bonded_port_state = (bonded_port_state != IB_PORT_ACTIVE) ?
+ curr_port_state : IB_PORT_ACTIVE;
+ }
+ spin_unlock_bh(&ibdev->iboe.lock);
+
+ ibev.device = &ibdev->ib_dev;
+ ibev.element.port_num = 1;
+ ibev.event = (bonded_port_state == IB_PORT_ACTIVE) ?
+ IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
+
+ ib_dispatch_event(&ibev);
+}
+
static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
enum mlx4_dev_event event, unsigned long param)
{
struct ib_event_work *ew;
int p = 0;
+ if (mlx4_is_bonded(dev) &&
+ ((event == MLX4_DEV_EVENT_PORT_UP) ||
+ (event == MLX4_DEV_EVENT_PORT_DOWN))) {
+ ew = kmalloc(sizeof(*ew), GFP_ATOMIC);
+ if (!ew)
+ return;
+ INIT_WORK(&ew->work, handle_bonded_port_state_event);
+ ew->ib_dev = ibdev;
+ queue_work(wq, &ew->work);
+ return;
+ }
+
if (event == MLX4_DEV_EVENT_PORT_MGMT_CHANGE)
eqe = (struct mlx4_eqe *)param;
else
}
ibev.device = ibdev_ptr;
- ibev.element.port_num = (u8) p;
+ ibev.element.port_num = mlx4_is_bonded(ibdev->dev) ? 1 : (u8)p;
ib_dispatch_event(&ibev);
}
.add = mlx4_ib_add,
.remove = mlx4_ib_remove,
.event = mlx4_ib_event,
- .protocol = MLX4_PROT_IB_IPV6
+ .protocol = MLX4_PROT_IB_IPV6,
+ .flags = MLX4_INTFF_BONDING
};
static int __init mlx4_ib_init(void)
struct mlx4_fmr mfmr;
};
+#define MAX_REGS_PER_FLOW 2
+
+struct mlx4_flow_reg_id {
+ u64 id;
+ u64 mirror;
+};
+
struct mlx4_ib_flow {
struct ib_flow ibflow;
/* translating DMFS verbs sniffer rule to FW API requires two reg IDs */
- u64 reg_id[2];
+ struct mlx4_flow_reg_id reg_id[MAX_REGS_PER_FLOW];
};
struct mlx4_ib_wq {
struct mlx4_ib_qp *qp1_proxy[MLX4_MAX_PORTS];
/* lock when destroying qp1_proxy and getting netdev events */
struct mutex qp1_proxy_lock[MLX4_MAX_PORTS];
+ u8 bond_next_port;
};
struct ib_event_work {
return container_of(ibah, struct mlx4_ib_ah, ibah);
}
+static inline u8 mlx4_ib_bond_next_port(struct mlx4_ib_dev *dev)
+{
+ dev->bond_next_port = (dev->bond_next_port + 1) % dev->num_ports;
+
+ return dev->bond_next_port + 1;
+}
+
int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev);
void mlx4_ib_close_sriov(struct mlx4_ib_dev *dev);
#include <rdma/ib_addr.h>
#include <rdma/ib_mad.h>
+#include <linux/mlx4/driver.h>
#include <linux/mlx4/qp.h>
#include "mlx4_ib.h"
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
-static inline u64 mlx4_mac_to_u64(u8 *addr)
-{
- u64 mac = 0;
- int i;
-
- for (i = 0; i < ETH_ALEN; i++) {
- mac <<= 8;
- mac |= addr[i];
- }
- return mac;
-}
static const __be32 mlx4_ib_opcode[] = {
[IB_WR_SEND] = cpu_to_be32(MLX4_OPCODE_SEND),
goto out;
}
+ if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT)) {
+ if ((cur_state == IB_QPS_RESET) && (new_state == IB_QPS_INIT)) {
+ if ((ibqp->qp_type == IB_QPT_RC) ||
+ (ibqp->qp_type == IB_QPT_UD) ||
+ (ibqp->qp_type == IB_QPT_UC) ||
+ (ibqp->qp_type == IB_QPT_RAW_PACKET) ||
+ (ibqp->qp_type == IB_QPT_XRC_INI)) {
+ attr->port_num = mlx4_ib_bond_next_port(dev);
+ }
+ } else {
+ /* no sense in changing port_num
+ * when ports are bonded */
+ attr_mask &= ~IB_QP_PORT;
+ }
+ }
+
if ((attr_mask & IB_QP_PORT) &&
(attr->port_num == 0 || attr->port_num > dev->num_ports)) {
pr_debug("qpn 0x%x: invalid port number (%d) specified "
err = __mlx4_ib_modify_qp(ibqp, attr, attr_mask, cur_state, new_state);
+ if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT))
+ attr->port_num = 1;
+
out:
mutex_unlock(&qp->mutex);
return err;
to_copy = size - bytes_copied;
if (is_iovec) {
- struct iovec *iov = (struct iovec *)src;
+ struct msghdr *msg = (struct msghdr *)src;
int err;
/* The iovec will track bytes_copied internally. */
- err = memcpy_fromiovec((u8 *)va + page_offset,
- iov, to_copy);
+ err = memcpy_from_msg((u8 *)va + page_offset,
+ msg, to_copy);
if (err != 0) {
if (kernel_if->host)
kunmap(kernel_if->u.h.page[page_index]);
*/
static int qp_memcpy_to_queue_iov(struct vmci_queue *queue,
u64 queue_offset,
- const void *src,
+ const void *msg,
size_t src_offset, size_t size)
{
* We ignore src_offset because src is really a struct iovec * and will
* maintain offset internally.
*/
- return __qp_memcpy_to_queue(queue, queue_offset, src, size, true);
+ return __qp_memcpy_to_queue(queue, queue_offset, msg, size, true);
}
/*
* of bytes enqueued or < 0 on error.
*/
ssize_t vmci_qpair_enquev(struct vmci_qp *qpair,
- void *iov,
+ struct msghdr *msg,
size_t iov_size,
int buf_type)
{
ssize_t result;
- if (!qpair || !iov)
+ if (!qpair)
return VMCI_ERROR_INVALID_ARGS;
qp_lock(qpair);
result = qp_enqueue_locked(qpair->produce_q,
qpair->consume_q,
qpair->produce_q_size,
- iov, iov_size,
+ msg, iov_size,
qp_memcpy_to_queue_iov);
if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY &&
}
new_active->delay = 0;
- new_active->link = BOND_LINK_UP;
+ bond_set_slave_link_state(new_active, BOND_LINK_UP);
if (BOND_MODE(bond) == BOND_MODE_8023AD)
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
kfree(slave);
}
+static void bond_fill_ifbond(struct bonding *bond, struct ifbond *info)
+{
+ info->bond_mode = BOND_MODE(bond);
+ info->miimon = bond->params.miimon;
+ info->num_slaves = bond->slave_cnt;
+}
+
+static void bond_fill_ifslave(struct slave *slave, struct ifslave *info)
+{
+ strcpy(info->slave_name, slave->dev->name);
+ info->link = slave->link;
+ info->state = bond_slave_state(slave);
+ info->link_failure_count = slave->link_failure_count;
+}
+
+static void bond_netdev_notify(struct slave *slave, struct net_device *dev)
+{
+ struct bonding *bond = slave->bond;
+ struct netdev_bonding_info bonding_info;
+
+ rtnl_lock();
+ /* make sure that slave is still valid */
+ if (dev->priv_flags & IFF_BONDING) {
+ bond_fill_ifslave(slave, &bonding_info.slave);
+ bond_fill_ifbond(bond, &bonding_info.master);
+ netdev_bonding_info_change(slave->dev, &bonding_info);
+ }
+ rtnl_unlock();
+}
+
+static void bond_netdev_notify_work(struct work_struct *_work)
+{
+ struct netdev_notify_work *w =
+ container_of(_work, struct netdev_notify_work, work.work);
+
+ bond_netdev_notify(w->slave, w->dev);
+ dev_put(w->dev);
+}
+
+void bond_queue_slave_event(struct slave *slave)
+{
+ struct netdev_notify_work *nnw = kzalloc(sizeof(*nnw), GFP_ATOMIC);
+
+ if (!nnw)
+ return;
+
+ INIT_DELAYED_WORK(&nnw->work, bond_netdev_notify_work);
+ nnw->slave = slave;
+ nnw->dev = slave->dev;
+
+ if (queue_delayed_work(slave->bond->wq, &nnw->work, 0))
+ dev_hold(slave->dev);
+ else
+ kfree(nnw);
+}
+
/* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
if (bond->params.miimon) {
if (bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS) {
if (bond->params.updelay) {
- new_slave->link = BOND_LINK_BACK;
+ bond_set_slave_link_state(new_slave,
+ BOND_LINK_BACK);
new_slave->delay = bond->params.updelay;
} else {
- new_slave->link = BOND_LINK_UP;
+ bond_set_slave_link_state(new_slave,
+ BOND_LINK_UP);
}
} else {
- new_slave->link = BOND_LINK_DOWN;
+ bond_set_slave_link_state(new_slave, BOND_LINK_DOWN);
}
} else if (bond->params.arp_interval) {
- new_slave->link = (netif_carrier_ok(slave_dev) ?
- BOND_LINK_UP : BOND_LINK_DOWN);
+ bond_set_slave_link_state(new_slave,
+ (netif_carrier_ok(slave_dev) ?
+ BOND_LINK_UP : BOND_LINK_DOWN));
} else {
- new_slave->link = BOND_LINK_UP;
+ bond_set_slave_link_state(new_slave, BOND_LINK_UP);
}
if (new_slave->link != BOND_LINK_DOWN)
new_slave->link != BOND_LINK_DOWN ? "an up" : "a down");
/* enslave is successful */
+ bond_queue_slave_event(new_slave);
return 0;
/* Undo stages on error */
static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
{
struct bonding *bond = netdev_priv(bond_dev);
-
- info->bond_mode = BOND_MODE(bond);
- info->miimon = bond->params.miimon;
- info->num_slaves = bond->slave_cnt;
-
+ bond_fill_ifbond(bond, info);
return 0;
}
bond_for_each_slave(bond, slave, iter) {
if (i++ == (int)info->slave_id) {
res = 0;
- strcpy(info->slave_name, slave->dev->name);
- info->link = slave->link;
- info->state = bond_slave_state(slave);
- info->link_failure_count = slave->link_failure_count;
+ bond_fill_ifslave(slave, info);
break;
}
}
if (link_state)
continue;
- slave->link = BOND_LINK_FAIL;
+ bond_set_slave_link_state(slave, BOND_LINK_FAIL);
slave->delay = bond->params.downdelay;
if (slave->delay) {
netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n",
case BOND_LINK_FAIL:
if (link_state) {
/* recovered before downdelay expired */
- slave->link = BOND_LINK_UP;
+ bond_set_slave_link_state(slave, BOND_LINK_UP);
slave->last_link_up = jiffies;
netdev_info(bond->dev, "link status up again after %d ms for interface %s\n",
(bond->params.downdelay - slave->delay) *
if (!link_state)
continue;
- slave->link = BOND_LINK_BACK;
+ bond_set_slave_link_state(slave, BOND_LINK_BACK);
slave->delay = bond->params.updelay;
if (slave->delay) {
/*FALLTHRU*/
case BOND_LINK_BACK:
if (!link_state) {
- slave->link = BOND_LINK_DOWN;
+ bond_set_slave_link_state(slave,
+ BOND_LINK_DOWN);
netdev_info(bond->dev, "link status down again after %d ms for interface %s\n",
(bond->params.updelay - slave->delay) *
bond->params.miimon,
continue;
case BOND_LINK_UP:
- slave->link = BOND_LINK_UP;
+ bond_set_slave_link_state(slave, BOND_LINK_UP);
slave->last_link_up = jiffies;
primary = rtnl_dereference(bond->primary_slave);
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- slave->link = BOND_LINK_DOWN;
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN);
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
BOND_MODE(bond) == BOND_MODE_8023AD)
struct slave *current_arp_slave;
current_arp_slave = rtnl_dereference(bond->current_arp_slave);
- slave->link = BOND_LINK_UP;
+ bond_set_slave_link_state(slave, BOND_LINK_UP);
if (current_arp_slave) {
bond_set_slave_inactive_flags(
current_arp_slave,
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- slave->link = BOND_LINK_DOWN;
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN);
bond_set_slave_inactive_flags(slave,
BOND_SLAVE_NOTIFY_NOW);
* up when it is actually down
*/
if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
- slave->link = BOND_LINK_DOWN;
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN);
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
if (!new_slave)
goto check_state;
- new_slave->link = BOND_LINK_BACK;
+ bond_set_slave_link_state(new_slave, BOND_LINK_BACK);
bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER);
bond_arp_send_all(bond, new_slave);
new_slave->last_link_up = jiffies;
struct adapter *adap;
struct net_device *netdev; /* associated net device */
rspq_handler_t handler;
+#ifdef CONFIG_NET_RX_BUSY_POLL
+#define CXGB_POLL_STATE_IDLE 0
+#define CXGB_POLL_STATE_NAPI BIT(0) /* NAPI owns this poll */
+#define CXGB_POLL_STATE_POLL BIT(1) /* poll owns this poll */
+#define CXGB_POLL_STATE_NAPI_YIELD BIT(2) /* NAPI yielded this poll */
+#define CXGB_POLL_STATE_POLL_YIELD BIT(3) /* poll yielded this poll */
+#define CXGB_POLL_YIELD (CXGB_POLL_STATE_NAPI_YIELD | \
+ CXGB_POLL_STATE_POLL_YIELD)
+#define CXGB_POLL_LOCKED (CXGB_POLL_STATE_NAPI | \
+ CXGB_POLL_STATE_POLL)
+#define CXGB_POLL_USER_PEND (CXGB_POLL_STATE_POLL | \
+ CXGB_POLL_STATE_POLL_YIELD)
+ unsigned int bpoll_state;
+ spinlock_t bpoll_lock; /* lock for busy poll */
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
};
struct sge_eth_stats { /* Ethernet queue statistics */
return netdev2pinfo(dev)->adapter;
}
+#ifdef CONFIG_NET_RX_BUSY_POLL
+static inline void cxgb_busy_poll_init_lock(struct sge_rspq *q)
+{
+ spin_lock_init(&q->bpoll_lock);
+ q->bpoll_state = CXGB_POLL_STATE_IDLE;
+}
+
+static inline bool cxgb_poll_lock_napi(struct sge_rspq *q)
+{
+ bool rc = true;
+
+ spin_lock(&q->bpoll_lock);
+ if (q->bpoll_state & CXGB_POLL_LOCKED) {
+ q->bpoll_state |= CXGB_POLL_STATE_NAPI_YIELD;
+ rc = false;
+ } else {
+ q->bpoll_state = CXGB_POLL_STATE_NAPI;
+ }
+ spin_unlock(&q->bpoll_lock);
+ return rc;
+}
+
+static inline bool cxgb_poll_unlock_napi(struct sge_rspq *q)
+{
+ bool rc = false;
+
+ spin_lock(&q->bpoll_lock);
+ if (q->bpoll_state & CXGB_POLL_STATE_POLL_YIELD)
+ rc = true;
+ q->bpoll_state = CXGB_POLL_STATE_IDLE;
+ spin_unlock(&q->bpoll_lock);
+ return rc;
+}
+
+static inline bool cxgb_poll_lock_poll(struct sge_rspq *q)
+{
+ bool rc = true;
+
+ spin_lock_bh(&q->bpoll_lock);
+ if (q->bpoll_state & CXGB_POLL_LOCKED) {
+ q->bpoll_state |= CXGB_POLL_STATE_POLL_YIELD;
+ rc = false;
+ } else {
+ q->bpoll_state |= CXGB_POLL_STATE_POLL;
+ }
+ spin_unlock_bh(&q->bpoll_lock);
+ return rc;
+}
+
+static inline bool cxgb_poll_unlock_poll(struct sge_rspq *q)
+{
+ bool rc = false;
+
+ spin_lock_bh(&q->bpoll_lock);
+ if (q->bpoll_state & CXGB_POLL_STATE_POLL_YIELD)
+ rc = true;
+ q->bpoll_state = CXGB_POLL_STATE_IDLE;
+ spin_unlock_bh(&q->bpoll_lock);
+ return rc;
+}
+
+static inline bool cxgb_poll_busy_polling(struct sge_rspq *q)
+{
+ return q->bpoll_state & CXGB_POLL_USER_PEND;
+}
+#else
+static inline void cxgb_busy_poll_init_lock(struct sge_rspq *q)
+{
+}
+
+static inline bool cxgb_poll_lock_napi(struct sge_rspq *q)
+{
+ return true;
+}
+
+static inline bool cxgb_poll_unlock_napi(struct sge_rspq *q)
+{
+ return false;
+}
+
+static inline bool cxgb_poll_lock_poll(struct sge_rspq *q)
+{
+ return false;
+}
+
+static inline bool cxgb_poll_unlock_poll(struct sge_rspq *q)
+{
+ return false;
+}
+
+static inline bool cxgb_poll_busy_polling(struct sge_rspq *q)
+{
+ return false;
+}
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
void t4_os_portmod_changed(const struct adapter *adap, int port_id);
void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
int t4_sge_init(struct adapter *adap);
void t4_sge_start(struct adapter *adap);
void t4_sge_stop(struct adapter *adap);
+int cxgb_busy_poll(struct napi_struct *napi);
extern int dbfifo_int_thresh;
#define for_each_port(adapter, iter) \
for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
- if (q && q->handler)
+ if (q && q->handler) {
napi_disable(&q->napi);
+ local_bh_disable();
+ while (!cxgb_poll_lock_napi(q))
+ mdelay(1);
+ local_bh_enable();
+ }
+
}
}
if (!q)
continue;
- if (q->handler)
+ if (q->handler) {
+ cxgb_busy_poll_init_lock(q);
napi_enable(&q->napi);
+ }
/* 0-increment GTS to start the timer and enable interrupts */
t4_write_reg(adap, MYPF_REG(SGE_PF_GTS_A),
SEINTARM_V(q->intr_params) |
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cxgb_netpoll,
#endif
+#ifdef CONFIG_NET_RX_BUSY_POLL
+ .ndo_busy_poll = cxgb_busy_poll,
+#endif
+
};
void t4_fatal_err(struct adapter *adap)
state, &reset);
/* Cleaning up */
- if (fw != NULL)
- release_firmware(fw);
+ release_firmware(fw);
t4_free_mem(card_fw);
if (ret < 0)
#include <linux/export.h>
#include <net/ipv6.h>
#include <net/tcp.h>
+#ifdef CONFIG_NET_RX_BUSY_POLL
+#include <net/busy_poll.h>
+#endif /* CONFIG_NET_RX_BUSY_POLL */
#include "cxgb4.h"
#include "t4_regs.h"
#include "t4_values.h"
skb->truesize += skb->data_len;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(skb, rxq->rspq.idx);
+ skb_mark_napi_id(skb, &rxq->rspq.napi);
if (rxq->rspq.netdev->features & NETIF_F_RXHASH)
skb_set_hash(skb, (__force u32)pkt->rsshdr.hash_val,
PKT_HASH_TYPE_L3);
csum_ok = pkt->csum_calc && !pkt->err_vec &&
(q->netdev->features & NETIF_F_RXCSUM);
if ((pkt->l2info & htonl(RXF_TCP_F)) &&
+ !(cxgb_poll_busy_polling(q)) &&
(q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) {
do_gro(rxq, si, pkt);
return 0;
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(pkt->vlan));
rxq->stats.vlan_ex++;
}
+ skb_mark_napi_id(skb, &q->napi);
netif_receive_skb(skb);
return 0;
}
return budget - budget_left;
}
+#ifdef CONFIG_NET_RX_BUSY_POLL
+int cxgb_busy_poll(struct napi_struct *napi)
+{
+ struct sge_rspq *q = container_of(napi, struct sge_rspq, napi);
+ unsigned int params, work_done;
+ u32 val;
+
+ if (!cxgb_poll_lock_poll(q))
+ return LL_FLUSH_BUSY;
+
+ work_done = process_responses(q, 4);
+ params = QINTR_TIMER_IDX(TIMERREG_COUNTER0_X) | QINTR_CNT_EN;
+ q->next_intr_params = params;
+ val = CIDXINC_V(work_done) | SEINTARM_V(params);
+
+ /* If we don't have access to the new User GTS (T5+), use the old
+ * doorbell mechanism; otherwise use the new BAR2 mechanism.
+ */
+ if (unlikely(!q->bar2_addr))
+ t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS_A),
+ val | INGRESSQID_V((u32)q->cntxt_id));
+ else {
+ writel(val | INGRESSQID_V(q->bar2_qid),
+ q->bar2_addr + SGE_UDB_GTS);
+ wmb();
+ }
+
+ cxgb_poll_unlock_poll(q);
+ return work_done;
+}
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
/**
* napi_rx_handler - the NAPI handler for Rx processing
* @napi: the napi instance
{
unsigned int params;
struct sge_rspq *q = container_of(napi, struct sge_rspq, napi);
- int work_done = process_responses(q, budget);
+ int work_done;
u32 val;
+ if (!cxgb_poll_lock_napi(q))
+ return budget;
+
+ work_done = process_responses(q, budget);
if (likely(work_done < budget)) {
int timer_index;
q->bar2_addr + SGE_UDB_GTS);
wmb();
}
+ cxgb_poll_unlock_napi(q);
return work_done;
}
goto err;
netif_napi_add(dev, &iq->napi, napi_rx_handler, 64);
+ napi_hash_add(&iq->napi);
iq->cur_desc = iq->desc;
iq->cidx = 0;
iq->gen = 1;
rq->cntxt_id, fl_id, 0xffff);
dma_free_coherent(adap->pdev_dev, (rq->size + 1) * rq->iqe_len,
rq->desc, rq->phys_addr);
+ napi_hash_del(&rq->napi);
netif_napi_del(&rq->napi);
rq->netdev = NULL;
rq->cntxt_id = rq->abs_id = 0;
/* GTS register */
#define SGE_TIMERREGS 6
+#define TIMERREG_COUNTER0_X 0
/* T5 and later support a new BAR2-based doorbell mechanism for Egress Queues.
* The User Doorbells are each 128 bytes in length with a Simple Doorbell at
}
for (i = 0; i < fep->num_rx_queues; i++)
- if (fep->rx_queue[i])
- kfree(fep->rx_queue[i]);
-
+ kfree(fep->rx_queue[i]);
for (i = 0; i < fep->num_tx_queues; i++)
- if (fep->tx_queue[i])
- kfree(fep->tx_queue[i]);
+ kfree(fep->tx_queue[i]);
}
static int fec_enet_alloc_queue(struct net_device *ndev)
fep->stats.collisions++;
/* unmap */
- dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp),
- skb->len, DMA_TO_DEVICE);
+ if (fep->mapped_as_page[dirtyidx])
+ dma_unmap_page(fep->dev, CBDR_BUFADDR(bdp),
+ CBDR_DATLEN(bdp), DMA_TO_DEVICE);
+ else
+ dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp),
+ CBDR_DATLEN(bdp), DMA_TO_DEVICE);
/*
* Free the sk buffer associated with this last transmit.
*/
- dev_kfree_skb(skb);
- fep->tx_skbuff[dirtyidx] = NULL;
+ if (skb) {
+ dev_kfree_skb(skb);
+ fep->tx_skbuff[dirtyidx] = NULL;
+ }
/*
* Update pointer to next buffer descriptor to be transmitted.
* Since we have freed up a buffer, the ring is no longer
* full.
*/
- if (!fep->tx_free++)
+ if (++fep->tx_free >= MAX_SKB_FRAGS)
do_wake = 1;
has_tx_work = 1;
}
cbd_t __iomem *bdp;
int curidx;
u16 sc;
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ skb_frag_t *frag;
+ int len;
#ifdef CONFIG_FS_ENET_MPC5121_FEC
if (((unsigned long)skb->data) & 0x3) {
*/
bdp = fep->cur_tx;
- if (!fep->tx_free || (CBDR_SC(bdp) & BD_ENET_TX_READY)) {
+ if (fep->tx_free <= nr_frags || (CBDR_SC(bdp) & BD_ENET_TX_READY)) {
netif_stop_queue(dev);
spin_unlock(&fep->tx_lock);
}
curidx = bdp - fep->tx_bd_base;
- /*
- * Clear all of the status flags.
- */
- CBDC_SC(bdp, BD_ENET_TX_STATS);
-
- /*
- * Save skb pointer.
- */
- fep->tx_skbuff[curidx] = skb;
-
- fep->stats.tx_bytes += skb->len;
+ len = skb->len;
+ fep->stats.tx_bytes += len;
+ if (nr_frags)
+ len -= skb->data_len;
+ fep->tx_free -= nr_frags + 1;
/*
* Push the data cache so the CPM does not get stale memory data.
*/
CBDW_BUFADDR(bdp, dma_map_single(fep->dev,
- skb->data, skb->len, DMA_TO_DEVICE));
- CBDW_DATLEN(bdp, skb->len);
+ skb->data, len, DMA_TO_DEVICE));
+ CBDW_DATLEN(bdp, len);
+
+ fep->mapped_as_page[curidx] = 0;
+ frag = skb_shinfo(skb)->frags;
+ while (nr_frags) {
+ CBDC_SC(bdp,
+ BD_ENET_TX_STATS | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+ CBDS_SC(bdp, BD_ENET_TX_READY);
+
+ if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
+ bdp++, curidx++;
+ else
+ bdp = fep->tx_bd_base, curidx = 0;
- /*
- * If this was the last BD in the ring, start at the beginning again.
- */
- if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
- fep->cur_tx++;
- else
- fep->cur_tx = fep->tx_bd_base;
+ len = skb_frag_size(frag);
+ CBDW_BUFADDR(bdp, skb_frag_dma_map(fep->dev, frag, 0, len,
+ DMA_TO_DEVICE));
+ CBDW_DATLEN(bdp, len);
- if (!--fep->tx_free)
- netif_stop_queue(dev);
+ fep->tx_skbuff[curidx] = NULL;
+ fep->mapped_as_page[curidx] = 1;
+
+ frag++;
+ nr_frags--;
+ }
/* Trigger transmission start */
sc = BD_ENET_TX_READY | BD_ENET_TX_INTR |
* yay for hw reuse :) */
if (skb->len <= 60)
sc |= BD_ENET_TX_PAD;
+ CBDC_SC(bdp, BD_ENET_TX_STATS);
CBDS_SC(bdp, sc);
+ /* Save skb pointer. */
+ fep->tx_skbuff[curidx] = skb;
+
+ /* If this was the last BD in the ring, start at the beginning again. */
+ if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
+ bdp++;
+ else
+ bdp = fep->tx_bd_base;
+ fep->cur_tx = bdp;
+
+ if (fep->tx_free < MAX_SKB_FRAGS)
+ netif_stop_queue(dev);
+
skb_tx_timestamp(skb);
(*fep->ops->tx_kickstart)(dev);
}
fpi->rx_ring = 32;
- fpi->tx_ring = 32;
+ fpi->tx_ring = 64;
fpi->rx_copybreak = 240;
fpi->napi_weight = 17;
fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
privsize = sizeof(*fep) +
sizeof(struct sk_buff **) *
- (fpi->rx_ring + fpi->tx_ring);
+ (fpi->rx_ring + fpi->tx_ring) +
+ sizeof(char) * fpi->tx_ring;
ndev = alloc_etherdev(privsize);
if (!ndev) {
fep->rx_skbuff = (struct sk_buff **)&fep[1];
fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring;
+ fep->mapped_as_page = (char *)(fep->rx_skbuff + fpi->rx_ring +
+ fpi->tx_ring);
spin_lock_init(&fep->lock);
spin_lock_init(&fep->tx_lock);
netif_carrier_off(ndev);
+ ndev->features |= NETIF_F_SG;
+
ret = register_netdev(ndev);
if (ret)
goto out_free_bd;
void __iomem *ring_base;
struct sk_buff **rx_skbuff;
struct sk_buff **tx_skbuff;
+ char *mapped_as_page;
cbd_t __iomem *rx_bd_base; /* Address of Rx and Tx buffers. */
cbd_t __iomem *tx_bd_base;
cbd_t __iomem *dirty_tx; /* ring entries to be free()ed. */
err = emac_check_deps(dev, deps) ? 0 : -ENODEV;
for (i = 0; i < EMAC_DEP_COUNT; i++) {
of_node_put(deps[i].node);
- if (err && deps[i].ofdev)
+ if (err)
of_dev_put(deps[i].ofdev);
}
if (err == 0) {
.verify = NULL,
.wrapper = mlx4_CMD_EPERM_wrapper
},
+ {
+ .opcode = MLX4_CMD_VIRT_PORT_MAP,
+ .has_inbox = false,
+ .has_outbox = false,
+ .out_is_imm = false,
+ .encode_slave_id = false,
+ .verify = NULL,
+ .wrapper = mlx4_CMD_EPERM_wrapper
+ },
};
static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
iounmap(mdev->uar_map);
mlx4_uar_free(dev, &mdev->priv_uar);
mlx4_pd_free(dev, mdev->priv_pdn);
+ if (mdev->nb.notifier_call)
+ unregister_netdevice_notifier(&mdev->nb);
kfree(mdev);
}
if (mlx4_en_init_netdev(mdev, i, &mdev->profile.prof[i]))
mdev->pndev[i] = NULL;
}
+ /* register notifier */
+ mdev->nb.notifier_call = mlx4_en_netdev_event;
+ if (register_netdevice_notifier(&mdev->nb)) {
+ mdev->nb.notifier_call = NULL;
+ mlx4_err(mdev, "Failed to create notifier\n");
+ }
return mdev;
/* Detach the netdev so tasks would not attempt to access it */
mutex_lock(&mdev->state_lock);
mdev->pndev[priv->port] = NULL;
+ mdev->upper[priv->port] = NULL;
mutex_unlock(&mdev->state_lock);
mlx4_en_free_resources(priv);
return ret;
}
+ if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_TX))
+ en_info(priv, "Turn %s TX vlan strip offload\n",
+ (features & NETIF_F_HW_VLAN_CTAG_TX) ? "ON" : "OFF");
+
if (features & NETIF_F_LOOPBACK)
priv->ctrl_flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
else
#endif
};
+struct mlx4_en_bond {
+ struct work_struct work;
+ struct mlx4_en_priv *priv;
+ int is_bonded;
+ struct mlx4_port_map port_map;
+};
+
+static void mlx4_en_bond_work(struct work_struct *work)
+{
+ struct mlx4_en_bond *bond = container_of(work,
+ struct mlx4_en_bond,
+ work);
+ int err = 0;
+ struct mlx4_dev *dev = bond->priv->mdev->dev;
+
+ if (bond->is_bonded) {
+ if (!mlx4_is_bonded(dev)) {
+ err = mlx4_bond(dev);
+ if (err)
+ en_err(bond->priv, "Fail to bond device\n");
+ }
+ if (!err) {
+ err = mlx4_port_map_set(dev, &bond->port_map);
+ if (err)
+ en_err(bond->priv, "Fail to set port map [%d][%d]: %d\n",
+ bond->port_map.port1,
+ bond->port_map.port2,
+ err);
+ }
+ } else if (mlx4_is_bonded(dev)) {
+ err = mlx4_unbond(dev);
+ if (err)
+ en_err(bond->priv, "Fail to unbond device\n");
+ }
+ dev_put(bond->priv->dev);
+ kfree(bond);
+}
+
+static int mlx4_en_queue_bond_work(struct mlx4_en_priv *priv, int is_bonded,
+ u8 v2p_p1, u8 v2p_p2)
+{
+ struct mlx4_en_bond *bond = NULL;
+
+ bond = kzalloc(sizeof(*bond), GFP_ATOMIC);
+ if (!bond)
+ return -ENOMEM;
+
+ INIT_WORK(&bond->work, mlx4_en_bond_work);
+ bond->priv = priv;
+ bond->is_bonded = is_bonded;
+ bond->port_map.port1 = v2p_p1;
+ bond->port_map.port2 = v2p_p2;
+ dev_hold(priv->dev);
+ queue_work(priv->mdev->workqueue, &bond->work);
+ return 0;
+}
+
+int mlx4_en_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+ u8 port = 0;
+ struct mlx4_en_dev *mdev;
+ struct mlx4_dev *dev;
+ int i, num_eth_ports = 0;
+ bool do_bond = true;
+ struct mlx4_en_priv *priv;
+ u8 v2p_port1 = 0;
+ u8 v2p_port2 = 0;
+
+ if (!net_eq(dev_net(ndev), &init_net))
+ return NOTIFY_DONE;
+
+ mdev = container_of(this, struct mlx4_en_dev, nb);
+ dev = mdev->dev;
+
+ /* Go into this mode only when two network devices set on two ports
+ * of the same mlx4 device are slaves of the same bonding master
+ */
+ mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) {
+ ++num_eth_ports;
+ if (!port && (mdev->pndev[i] == ndev))
+ port = i;
+ mdev->upper[i] = mdev->pndev[i] ?
+ netdev_master_upper_dev_get(mdev->pndev[i]) : NULL;
+ /* condition not met: network device is a slave */
+ if (!mdev->upper[i])
+ do_bond = false;
+ if (num_eth_ports < 2)
+ continue;
+ /* condition not met: same master */
+ if (mdev->upper[i] != mdev->upper[i-1])
+ do_bond = false;
+ }
+ /* condition not met: 2 salves */
+ do_bond = (num_eth_ports == 2) ? do_bond : false;
+
+ /* handle only events that come with enough info */
+ if ((do_bond && (event != NETDEV_BONDING_INFO)) || !port)
+ return NOTIFY_DONE;
+
+ priv = netdev_priv(ndev);
+ if (do_bond) {
+ struct netdev_notifier_bonding_info *notifier_info = ptr;
+ struct netdev_bonding_info *bonding_info =
+ ¬ifier_info->bonding_info;
+
+ /* required mode 1, 2 or 4 */
+ if ((bonding_info->master.bond_mode != BOND_MODE_ACTIVEBACKUP) &&
+ (bonding_info->master.bond_mode != BOND_MODE_XOR) &&
+ (bonding_info->master.bond_mode != BOND_MODE_8023AD))
+ do_bond = false;
+
+ /* require exactly 2 slaves */
+ if (bonding_info->master.num_slaves != 2)
+ do_bond = false;
+
+ /* calc v2p */
+ if (do_bond) {
+ if (bonding_info->master.bond_mode ==
+ BOND_MODE_ACTIVEBACKUP) {
+ /* in active-backup mode virtual ports are
+ * mapped to the physical port of the active
+ * slave */
+ if (bonding_info->slave.state ==
+ BOND_STATE_BACKUP) {
+ if (port == 1) {
+ v2p_port1 = 2;
+ v2p_port2 = 2;
+ } else {
+ v2p_port1 = 1;
+ v2p_port2 = 1;
+ }
+ } else { /* BOND_STATE_ACTIVE */
+ if (port == 1) {
+ v2p_port1 = 1;
+ v2p_port2 = 1;
+ } else {
+ v2p_port1 = 2;
+ v2p_port2 = 2;
+ }
+ }
+ } else { /* Active-Active */
+ /* in active-active mode a virtual port is
+ * mapped to the native physical port if and only
+ * if the physical port is up */
+ __s8 link = bonding_info->slave.link;
+
+ if (port == 1)
+ v2p_port2 = 2;
+ else
+ v2p_port1 = 1;
+ if ((link == BOND_LINK_UP) ||
+ (link == BOND_LINK_FAIL)) {
+ if (port == 1)
+ v2p_port1 = 1;
+ else
+ v2p_port2 = 2;
+ } else { /* BOND_LINK_DOWN || BOND_LINK_BACK */
+ if (port == 1)
+ v2p_port1 = 2;
+ else
+ v2p_port2 = 1;
+ }
+ }
+ }
+ }
+
+ mlx4_en_queue_bond_work(priv, do_bond,
+ v2p_port1, v2p_port2);
+
+ return NOTIFY_DONE;
+}
+
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
struct mlx4_en_port_profile *prof)
{
}
mdev->pndev[port] = dev;
+ mdev->upper[port] = NULL;
netif_carrier_off(dev);
mlx4_en_set_default_moderation(priv);
context->mtu_msgmax = 0xff;
if (!is_tx && !rss)
context->rq_size_stride = ilog2(size) << 3 | (ilog2(stride) - 4);
- if (is_tx)
+ if (is_tx) {
context->sq_size_stride = ilog2(size) << 3 | (ilog2(stride) - 4);
- else
+ if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)
+ context->params2 |= MLX4_QP_BIT_FPP;
+
+ } else {
context->sq_size_stride = ilog2(TXBB_SIZE) - 4;
+ }
context->usr_page = cpu_to_be32(mdev->priv_uar.index);
context->local_qpn = cpu_to_be32(qpn);
context->pri_path.ackto = 1 & 0x07;
if (mlx4_alloc_pages(priv, &ring->page_alloc[i],
frag_info, GFP_KERNEL | __GFP_COLD))
goto out;
+
+ en_dbg(DRV, priv, " frag %d allocator: - size:%d frags:%d\n",
+ i, ring->page_alloc[i].page_size,
+ atomic_read(&ring->page_alloc[i].page->_count));
}
return 0;
(eff_mtu > buf_size + frag_sizes[i]) ?
frag_sizes[i] : eff_mtu - buf_size;
priv->frag_info[i].frag_prefix_size = buf_size;
- priv->frag_info[i].frag_stride = ALIGN(frag_sizes[i],
- SMP_CACHE_BYTES);
+ priv->frag_info[i].frag_stride =
+ ALIGN(priv->frag_info[i].frag_size,
+ SMP_CACHE_BYTES);
buf_size += priv->frag_info[i].frag_size;
i++;
}
[17] = "Asymmetric EQs support",
[18] = "More than 80 VFs support",
[19] = "Performance optimized for limited rule configuration flow steering support",
- [20] = "Recoverable error events support"
+ [20] = "Recoverable error events support",
+ [21] = "Port Remap support"
};
int i;
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_EQE_STRIDE;
MLX4_GET(dev_cap->bmme_flags, outbox,
QUERY_DEV_CAP_BMME_FLAGS_OFFSET);
+ if (dev_cap->bmme_flags & MLX4_FLAG_PORT_REMAP)
+ dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_PORT_REMAP;
MLX4_GET(field, outbox, QUERY_DEV_CAP_CONFIG_DEV_OFFSET);
if (field & 0x20)
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_CONFIG_DEV;
field &= 0x7f;
MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_BF_OFFSET);
- /* For guests, disable mw type 2 */
+ /* For guests, disable mw type 2 and port remap*/
MLX4_GET(bmme_flags, outbox->buf, QUERY_DEV_CAP_BMME_FLAGS_OFFSET);
bmme_flags &= ~MLX4_BMME_FLAG_TYPE_2_WIN;
+ bmme_flags &= ~MLX4_FLAG_PORT_REMAP;
MLX4_PUT(outbox->buf, bmme_flags, QUERY_DEV_CAP_BMME_FLAGS_OFFSET);
/* turn off device-managed steering capability if not enabled */
__be32 rsvd1[3];
__be16 vxlan_udp_dport;
__be16 rsvd2;
- __be32 rsvd3[27];
- __be16 rsvd4;
- u8 rsvd5;
+ __be32 rsvd3;
+ __be32 roce_flags;
+ __be32 rsvd4[25];
+ __be16 rsvd5;
+ u8 rsvd6;
u8 rx_checksum_val;
};
#define MLX4_VXLAN_UDP_DPORT (1 << 0)
+#define MLX4_DISABLE_RX_PORT BIT(18)
static int mlx4_CONFIG_DEV_set(struct mlx4_dev *dev, struct mlx4_config_dev *config_dev)
{
int mlx4_config_dev_retrieval(struct mlx4_dev *dev,
struct mlx4_config_dev_params *params)
{
- struct mlx4_config_dev config_dev;
+ struct mlx4_config_dev config_dev = {0};
int err;
u8 csum_mask;
}
EXPORT_SYMBOL_GPL(mlx4_config_vxlan_port);
+#define CONFIG_DISABLE_RX_PORT BIT(15)
+int mlx4_disable_rx_port_check(struct mlx4_dev *dev, bool dis)
+{
+ struct mlx4_config_dev config_dev;
+
+ memset(&config_dev, 0, sizeof(config_dev));
+ config_dev.update_flags = cpu_to_be32(MLX4_DISABLE_RX_PORT);
+ if (dis)
+ config_dev.roce_flags =
+ cpu_to_be32(CONFIG_DISABLE_RX_PORT);
+
+ return mlx4_CONFIG_DEV_set(dev, &config_dev);
+}
+
+int mlx4_virt2phy_port_map(struct mlx4_dev *dev, u32 port1, u32 port2)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ struct {
+ __be32 v_port1;
+ __be32 v_port2;
+ } *v2p;
+ int err;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return -ENOMEM;
+
+ v2p = mailbox->buf;
+ v2p->v_port1 = cpu_to_be32(port1);
+ v2p->v_port2 = cpu_to_be32(port2);
+
+ err = mlx4_cmd(dev, mailbox->dma, 0,
+ MLX4_SET_PORT_VIRT2PHY, MLX4_CMD_VIRT_PORT_MAP,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+
int mlx4_SET_ICM_SIZE(struct mlx4_dev *dev, u64 icm_size, u64 *aux_pages)
{
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/errno.h>
#include "mlx4.h"
struct mlx4_device_context {
struct list_head list;
+ struct list_head bond_list;
struct mlx4_interface *intf;
void *context;
};
}
EXPORT_SYMBOL_GPL(mlx4_unregister_interface);
+int mlx4_do_bond(struct mlx4_dev *dev, bool enable)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ struct mlx4_device_context *dev_ctx = NULL, *temp_dev_ctx;
+ unsigned long flags;
+ int ret;
+ LIST_HEAD(bond_list);
+
+ if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP))
+ return -ENOTSUPP;
+
+ ret = mlx4_disable_rx_port_check(dev, enable);
+ if (ret) {
+ mlx4_err(dev, "Fail to %s rx port check\n",
+ enable ? "enable" : "disable");
+ return ret;
+ }
+ if (enable) {
+ dev->flags |= MLX4_FLAG_BONDED;
+ } else {
+ ret = mlx4_virt2phy_port_map(dev, 1, 2);
+ if (ret) {
+ mlx4_err(dev, "Fail to reset port map\n");
+ return ret;
+ }
+ dev->flags &= ~MLX4_FLAG_BONDED;
+ }
+
+ spin_lock_irqsave(&priv->ctx_lock, flags);
+ list_for_each_entry_safe(dev_ctx, temp_dev_ctx, &priv->ctx_list, list) {
+ if (dev_ctx->intf->flags & MLX4_INTFF_BONDING) {
+ list_add_tail(&dev_ctx->bond_list, &bond_list);
+ list_del(&dev_ctx->list);
+ }
+ }
+ spin_unlock_irqrestore(&priv->ctx_lock, flags);
+
+ list_for_each_entry(dev_ctx, &bond_list, bond_list) {
+ dev_ctx->intf->remove(dev, dev_ctx->context);
+ dev_ctx->context = dev_ctx->intf->add(dev);
+
+ spin_lock_irqsave(&priv->ctx_lock, flags);
+ list_add_tail(&dev_ctx->list, &priv->ctx_list);
+ spin_unlock_irqrestore(&priv->ctx_lock, flags);
+
+ mlx4_dbg(dev, "Inrerface for protocol %d restarted with when bonded mode is %s\n",
+ dev_ctx->intf->protocol, enable ?
+ "enabled" : "disabled");
+ }
+ return 0;
+}
+
void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type,
unsigned long param)
{
if (mlx4_is_master(dev))
dev_cap->function_caps |= MLX4_FUNC_CAP_EQE_CQE_STRIDE;
} else {
- mlx4_dbg(dev, "Disabling CQE stride cacheLine unsupported\n");
+ if (cache_line_size() != 32 && cache_line_size() != 64)
+ mlx4_dbg(dev, "Disabling CQE stride, cacheLine size unsupported\n");
dev_cap->flags2 &= ~MLX4_DEV_CAP_FLAG2_CQE_STRIDE;
dev_cap->flags2 &= ~MLX4_DEV_CAP_FLAG2_EQE_STRIDE;
}
return err ? err : count;
}
+int mlx4_bond(struct mlx4_dev *dev)
+{
+ int ret = 0;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ mutex_lock(&priv->bond_mutex);
+
+ if (!mlx4_is_bonded(dev))
+ ret = mlx4_do_bond(dev, true);
+ else
+ ret = 0;
+
+ mutex_unlock(&priv->bond_mutex);
+ if (ret)
+ mlx4_err(dev, "Failed to bond device: %d\n", ret);
+ else
+ mlx4_dbg(dev, "Device is bonded\n");
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mlx4_bond);
+
+int mlx4_unbond(struct mlx4_dev *dev)
+{
+ int ret = 0;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ mutex_lock(&priv->bond_mutex);
+
+ if (mlx4_is_bonded(dev))
+ ret = mlx4_do_bond(dev, false);
+
+ mutex_unlock(&priv->bond_mutex);
+ if (ret)
+ mlx4_err(dev, "Failed to unbond device: %d\n", ret);
+ else
+ mlx4_dbg(dev, "Device is unbonded\n");
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mlx4_unbond);
+
+
+int mlx4_port_map_set(struct mlx4_dev *dev, struct mlx4_port_map *v2p)
+{
+ u8 port1 = v2p->port1;
+ u8 port2 = v2p->port2;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ int err;
+
+ if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP))
+ return -ENOTSUPP;
+
+ mutex_lock(&priv->bond_mutex);
+
+ /* zero means keep current mapping for this port */
+ if (port1 == 0)
+ port1 = priv->v2p.port1;
+ if (port2 == 0)
+ port2 = priv->v2p.port2;
+
+ if ((port1 < 1) || (port1 > MLX4_MAX_PORTS) ||
+ (port2 < 1) || (port2 > MLX4_MAX_PORTS) ||
+ (port1 == 2 && port2 == 1)) {
+ /* besides boundary checks cross mapping makes
+ * no sense and therefore not allowed */
+ err = -EINVAL;
+ } else if ((port1 == priv->v2p.port1) &&
+ (port2 == priv->v2p.port2)) {
+ err = 0;
+ } else {
+ err = mlx4_virt2phy_port_map(dev, port1, port2);
+ if (!err) {
+ mlx4_dbg(dev, "port map changed: [%d][%d]\n",
+ port1, port2);
+ priv->v2p.port1 = port1;
+ priv->v2p.port2 = port2;
+ } else {
+ mlx4_err(dev, "Failed to change port mape: %d\n", err);
+ }
+ }
+
+ mutex_unlock(&priv->bond_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx4_port_map_set);
+
static int mlx4_load_fw(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
spin_lock_init(&priv->ctx_lock);
mutex_init(&priv->port_mutex);
+ mutex_init(&priv->bond_mutex);
INIT_LIST_HEAD(&priv->pgdir_list);
mutex_init(&priv->pgdir_mutex);
goto err_port;
}
+ priv->v2p.port1 = 1;
+ priv->v2p.port2 = 2;
+
err = mlx4_register_device(dev);
if (err)
goto err_port;
int reserved_mtts;
int fs_hash_mode;
u8 virt2phys_pkey[MLX4_MFUNC_MAX][MLX4_MAX_PORTS][MLX4_MAX_PORT_PKEYS];
+ struct mlx4_port_map v2p; /* cached port mapping configuration */
+ struct mutex bond_mutex; /* for bond mode */
__be64 slave_node_guids[MLX4_MFUNC_MAX];
atomic_t opreq_count;
/* Returns the VF index of slave */
int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave);
int mlx4_config_mad_demux(struct mlx4_dev *dev);
+int mlx4_do_bond(struct mlx4_dev *dev, bool enable);
enum mlx4_zone_flags {
MLX4_ZONE_ALLOW_ALLOC_FROM_LOWER_PRIO = 1UL << 0,
struct pci_dev *pdev;
struct mutex state_lock;
struct net_device *pndev[MLX4_MAX_PORTS + 1];
+ struct net_device *upper[MLX4_MAX_PORTS + 1];
u32 port_cnt;
bool device_up;
struct mlx4_en_profile profile;
unsigned long overflow_period;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info;
+ struct notifier_block nb;
};
struct hwtstamp_config ts_config,
netdev_features_t new_features);
+int mlx4_en_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr);
+
/*
* Functions for time stamping
*/
if (err)
return err;
- mpt_entry->start = cpu_to_be64(mr->iova);
- mpt_entry->length = cpu_to_be64(mr->size);
- mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift);
-
- mpt_entry->pd_flags &= cpu_to_be32(MLX4_MPT_PD_MASK |
- MLX4_MPT_PD_FLAG_EN_INV);
- mpt_entry->flags &= cpu_to_be32(MLX4_MPT_FLAG_FREE |
- MLX4_MPT_FLAG_SW_OWNS);
+ mpt_entry->start = cpu_to_be64(iova);
+ mpt_entry->length = cpu_to_be64(size);
+ mpt_entry->entity_size = cpu_to_be32(page_shift);
+ mpt_entry->flags &= ~(cpu_to_be32(MLX4_MPT_FLAG_FREE |
+ MLX4_MPT_FLAG_SW_OWNS));
if (mr->mtt.order < 0) {
mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL);
mpt_entry->mtt_addr = 0;
for (i = 0; i < ARRAY_SIZE(states) - 1; i++) {
context->flags &= cpu_to_be32(~(0xf << 28));
context->flags |= cpu_to_be32(states[i + 1] << 28);
+ if (states[i + 1] != MLX4_QP_STATE_RTR)
+ context->params2 &= ~MLX4_QP_BIT_FPP;
err = mlx4_qp_modify(dev, mtt, states[i], states[i + 1],
context, 0, 0, qp);
if (err) {
/* Make sure that the PD bits related to the slave id are zeros. */
pd = mr_get_pd(inbox->buf);
pd_slave = (pd >> 17) & 0x7f;
- if (pd_slave != 0 && pd_slave != slave) {
+ if (pd_slave != 0 && --pd_slave != slave) {
err = -EPERM;
goto ex_abort;
}
qp_type = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff;
optpar = be32_to_cpu(*(__be32 *) inbox->buf);
+ if (slave != mlx4_master_func_num(dev))
+ qp_ctx->params2 &= ~MLX4_QP_BIT_FPP;
+
switch (qp_type) {
case MLX4_QP_ST_RC:
case MLX4_QP_ST_XRC:
}
static const struct pci_device_id mlx5_core_pci_table[] = {
- { PCI_VDEVICE(MELLANOX, 4113) }, /* Connect-IB */
- { PCI_VDEVICE(MELLANOX, 4114) }, /* Connect-IB VF */
- { PCI_VDEVICE(MELLANOX, 4115) }, /* ConnectX-4 */
- { PCI_VDEVICE(MELLANOX, 4116) }, /* ConnectX-4 VF */
- { PCI_VDEVICE(MELLANOX, 4117) }, /* ConnectX-4LX */
- { PCI_VDEVICE(MELLANOX, 4118) }, /* ConnectX-4LX VF */
+ { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */
+ { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */
+ { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */
+ { PCI_VDEVICE(MELLANOX, 0x1014) }, /* ConnectX-4 VF */
+ { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */
+ { PCI_VDEVICE(MELLANOX, 0x1016) }, /* ConnectX-4LX VF */
{ 0, }
};
mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
#endif
myri10ge_free_slices(mgp);
- if (mgp->msix_vectors != NULL)
- kfree(mgp->msix_vectors);
+ kfree(mgp->msix_vectors);
dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd),
mgp->cmd, mgp->cmd_bus);
static void
netxen_free_sds_rings(struct netxen_recv_context *recv_ctx)
{
- if (recv_ctx->sds_rings != NULL)
- kfree(recv_ctx->sds_rings);
-
+ kfree(recv_ctx->sds_rings);
recv_ctx->sds_rings = NULL;
}
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+#include "cpsw.h"
+
#define AM33XX_CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id))
#define AM33XX_CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4)
quit:
if (gbe_dev->hw_stats)
devm_kfree(dev, gbe_dev->hw_stats);
- if (gbe_dev->ale)
- cpsw_ale_destroy(gbe_dev->ale);
+ cpsw_ale_destroy(gbe_dev->ale);
if (gbe_dev->ss_regs)
devm_iounmap(dev, gbe_dev->ss_regs);
- if (interfaces)
- of_node_put(interfaces);
+ of_node_put(interfaces);
devm_kfree(dev, gbe_dev);
return ret;
}
* This is abitrary. It is intended to make sure the
* transceiver settles.
*/
- tlan_set_timer(dev, (HZ/20), TLAN_TIMER_PHY_PUP);
+ tlan_set_timer(dev, msecs_to_jiffies(50), TLAN_TIMER_PHY_PUP);
}
* transceiver. The TLAN docs say both 50 ms and
* 500 ms, so do the longer, just in case.
*/
- tlan_set_timer(dev, (HZ/20), TLAN_TIMER_PHY_RESET);
+ tlan_set_timer(dev, msecs_to_jiffies(500), TLAN_TIMER_PHY_RESET);
}
* I don't remember why I wait this long.
* I've changed this to 50ms, as it seems long enough.
*/
- tlan_set_timer(dev, (HZ/20), TLAN_TIMER_PHY_START_LINK);
+ tlan_set_timer(dev, msecs_to_jiffies(50), TLAN_TIMER_PHY_START_LINK);
}
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN
| TLAN_NET_CFG_PHY_EN;
tlan_dio_write16(dev->base_addr, TLAN_NET_CONFIG, data);
- tlan_set_timer(dev, (40*HZ/1000), TLAN_TIMER_PHY_PDOWN);
+ tlan_set_timer(dev, msecs_to_jiffies(40), TLAN_TIMER_PHY_PDOWN);
return;
} else if (priv->phy_num == 0) {
control = 0;
(priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10) &&
(priv->phy_num != 0)) {
priv->phy_num = 0;
- tlan_set_timer(dev, (400*HZ/1000), TLAN_TIMER_PHY_PDOWN);
+ tlan_set_timer(dev, msecs_to_jiffies(400), TLAN_TIMER_PHY_PDOWN);
return;
}
/* Wait for 100 ms. No reason in partiticular.
*/
- tlan_set_timer(dev, (HZ/10), TLAN_TIMER_FINISH_RESET);
+ tlan_set_timer(dev, msecs_to_jiffies(100), TLAN_TIMER_FINISH_RESET);
}
/* set to external PHY */
priv->phy_num = 1;
/* restart autonegotiation */
- tlan_set_timer(dev, 4 * HZ / 10,
+ tlan_set_timer(dev, msecs_to_jiffies(400),
TLAN_TIMER_PHY_PDOWN);
return;
}
#define CC2520_FREG_MASK 0x3F
/* status byte values */
-#define CC2520_STATUS_XOSC32M_STABLE (1 << 7)
-#define CC2520_STATUS_RSSI_VALID (1 << 6)
-#define CC2520_STATUS_TX_UNDERFLOW (1 << 3)
+#define CC2520_STATUS_XOSC32M_STABLE BIT(7)
+#define CC2520_STATUS_RSSI_VALID BIT(6)
+#define CC2520_STATUS_TX_UNDERFLOW BIT(3)
/* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */
#define CC2520_MINCHANNEL 11
u8 rssi;
int ret;
- ret = cc2520_read_register(priv , CC2520_RSSISTAT, &status);
+ ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status);
if (ret)
return ret;
if (status != RSSI_VALID)
return -EINVAL;
- ret = cc2520_read_register(priv , CC2520_RSSI, &rssi);
+ ret = cc2520_read_register(priv, CC2520_RSSI, &rssi);
if (ret)
return ret;
u32 data)
{
size_t start, offset, plen;
- __wsum delta;
if (skb->remcsum_offload)
return vh;
return NULL;
}
- delta = remcsum_adjust((void *)vh + hdrlen,
- NAPI_GRO_CB(skb)->csum, start, offset);
-
- /* Adjust skb->csum since we changed the packet */
- skb->csum = csum_add(skb->csum, delta);
- NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
+ skb_gro_remcsum_process(skb, (void *)vh + hdrlen, start, offset);
skb->remcsum_offload = 1;
size_t hdrlen, u32 data)
{
size_t start, offset, plen;
- __wsum delta;
if (skb->remcsum_offload) {
/* Already processed in GRO path */
vh = (struct vxlanhdr *)(udp_hdr(skb) + 1);
- if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
- __skb_checksum_complete(skb);
-
- delta = remcsum_adjust((void *)vh + hdrlen,
- skb->csum, start, offset);
-
- /* Adjust skb->csum since we changed the packet */
- skb->csum = csum_add(skb->csum, delta);
+ skb_remcsum_process(skb, (void *)vh + hdrlen, start, offset);
return vh;
}
if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
arg.key_flags = WMI_KEY_PAIRWISE;
break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ /* this one needs to be done in software */
+ return 1;
default:
ath10k_warn(ar, "cipher %d is not supported\n", key->cipher);
return -EOPNOTSUPP;
int ath10k_mac_register(struct ath10k *ar)
{
+ static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+ };
struct ieee80211_supported_band *band;
struct ieee80211_sta_vht_cap vht_cap;
struct ieee80211_sta_ht_cap ht_cap;
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_AP_LINK_PS |
- IEEE80211_HW_SPECTRUM_MGMT;
+ IEEE80211_HW_SPECTRUM_MGMT |
+ IEEE80211_HW_SW_CRYPTO_CONTROL;
ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
goto err_free;
}
+ ar->hw->wiphy->cipher_suites = cipher_suites;
+ ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
ret = ieee80211_register_hw(ar->hw);
if (ret) {
ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
}
/* Validate device capabilities */
- if (csio_hw_validate_caps(hw, mbp))
+ rv = csio_hw_validate_caps(hw, mbp);
+ if (rv != 0)
goto out;
/* Don't config device capabilities if already configured */
}
/* Validate device capabilities */
- if (csio_hw_validate_caps(hw, mbp))
+ rv = csio_hw_validate_caps(hw, mbp);
+ if (rv != 0)
goto bye;
/*
* Note that we're operating with parameters
#define FW_FNAME_T5 "cxgb4/t5fw.bin"
#define FW_CFG_NAME_T5 "cxgb4/t5-config.txt"
-#define T5FW_VERSION_MAJOR 0x01
-#define T5FW_VERSION_MINOR 0x0B
-#define T5FW_VERSION_MICRO 0x1B
-#define T5FW_VERSION_BUILD 0x00
-
#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision))
#define CHELSIO_CHIP_FPGA 0x100
#define CHELSIO_CHIP_VERSION(code) (((code) >> 12) & 0xf)
{ PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) }
#include "t4fw_api.h"
+#include "t4fw_version.h"
#define FW_VERSION(chip) ( \
FW_HDR_FW_VER_MAJOR_G(chip##FW_VERSION_MAJOR) | \
}
#define CSIO_ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\
- FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_ANEG)
+ FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_40G |\
+ FW_PORT_CAP_ANEG)
/*
* csio_mb_port- FW PORT command helper
struct vhost_net_virtqueue {
struct vhost_virtqueue vq;
- /* hdr is used to store the virtio header.
- * Since each iovec has >= 1 byte length, we never need more than
- * header length entries to store the header. */
- struct iovec hdr[sizeof(struct virtio_net_hdr_mrg_rxbuf)];
size_t vhost_hlen;
size_t sock_hlen;
/* vhost zerocopy support fields below: */
sock_flag(sock->sk, SOCK_ZEROCOPY);
}
-/* Pop first len bytes from iovec. Return number of segments used. */
-static int move_iovec_hdr(struct iovec *from, struct iovec *to,
- size_t len, int iov_count)
-{
- int seg = 0;
- size_t size;
-
- while (len && seg < iov_count) {
- size = min(from->iov_len, len);
- to->iov_base = from->iov_base;
- to->iov_len = size;
- from->iov_len -= size;
- from->iov_base += size;
- len -= size;
- ++from;
- ++to;
- ++seg;
- }
- return seg;
-}
-/* Copy iovec entries for len bytes from iovec. */
-static void copy_iovec_hdr(const struct iovec *from, struct iovec *to,
- size_t len, int iovcount)
-{
- int seg = 0;
- size_t size;
-
- while (len && seg < iovcount) {
- size = min(from->iov_len, len);
- to->iov_base = from->iov_base;
- to->iov_len = size;
- len -= size;
- ++from;
- ++to;
- ++seg;
- }
-}
-
/* In case of DMA done not in order in lower device driver for some reason.
* upend_idx is used to track end of used idx, done_idx is used to track head
* of used idx. Once lower device DMA done contiguously, we will signal KVM
{
struct vhost_net_virtqueue *nvq = &net->vqs[VHOST_NET_VQ_TX];
struct vhost_virtqueue *vq = &nvq->vq;
- unsigned out, in, s;
+ unsigned out, in;
int head;
struct msghdr msg = {
.msg_name = NULL,
break;
}
/* Skip header. TODO: support TSO. */
- s = move_iovec_hdr(vq->iov, nvq->hdr, hdr_size, out);
len = iov_length(vq->iov, out);
iov_iter_init(&msg.msg_iter, WRITE, vq->iov, out, len);
+ iov_iter_advance(&msg.msg_iter, hdr_size);
/* Sanity check */
- if (!len) {
+ if (!iov_iter_count(&msg.msg_iter)) {
vq_err(vq, "Unexpected header len for TX: "
"%zd expected %zd\n",
- iov_length(nvq->hdr, s), hdr_size);
+ len, hdr_size);
break;
}
+ len = iov_iter_count(&msg.msg_iter);
zcopy_used = zcopy && len >= VHOST_GOODCOPY_LEN
&& (nvq->upend_idx + 1) % UIO_MAXIOV !=
.msg_controllen = 0,
.msg_flags = MSG_DONTWAIT,
};
- struct virtio_net_hdr_mrg_rxbuf hdr = {
- .hdr.flags = 0,
- .hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE
+ struct virtio_net_hdr hdr = {
+ .flags = 0,
+ .gso_type = VIRTIO_NET_HDR_GSO_NONE
};
size_t total_len = 0;
int err, mergeable;
size_t vhost_hlen, sock_hlen;
size_t vhost_len, sock_len;
struct socket *sock;
+ struct iov_iter fixup;
mutex_lock(&vq->mutex);
sock = vq->private_data;
break;
}
/* We don't need to be notified again. */
- if (unlikely((vhost_hlen)))
- /* Skip header. TODO: support TSO. */
- move_iovec_hdr(vq->iov, nvq->hdr, vhost_hlen, in);
- else
- /* Copy the header for use in VIRTIO_NET_F_MRG_RXBUF:
- * needed because recvmsg can modify msg_iov. */
- copy_iovec_hdr(vq->iov, nvq->hdr, sock_hlen, in);
- iov_iter_init(&msg.msg_iter, READ, vq->iov, in, sock_len);
+ iov_iter_init(&msg.msg_iter, READ, vq->iov, in, vhost_len);
+ fixup = msg.msg_iter;
+ if (unlikely((vhost_hlen))) {
+ /* We will supply the header ourselves
+ * TODO: support TSO.
+ */
+ iov_iter_advance(&msg.msg_iter, vhost_hlen);
+ } else {
+ /* It'll come from socket; we'll need to patch
+ * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF
+ */
+ iov_iter_advance(&fixup, sizeof(hdr));
+ }
err = sock->ops->recvmsg(NULL, sock, &msg,
sock_len, MSG_DONTWAIT | MSG_TRUNC);
/* Userspace might have consumed the packet meanwhile:
vhost_discard_vq_desc(vq, headcount);
continue;
}
+ /* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */
if (unlikely(vhost_hlen) &&
- memcpy_toiovecend(nvq->hdr, (unsigned char *)&hdr, 0,
- vhost_hlen)) {
+ copy_to_iter(&hdr, sizeof(hdr), &fixup) != sizeof(hdr)) {
vq_err(vq, "Unable to write vnet_hdr at addr %p\n",
vq->iov->iov_base);
break;
}
- /* TODO: Should check and handle checksum. */
+ /* Supply (or replace) ->num_buffers if VIRTIO_NET_F_MRG_RXBUF
+ * TODO: Should check and handle checksum.
+ */
if (likely(mergeable) &&
- memcpy_toiovecend(nvq->hdr, (unsigned char *)&headcount,
- offsetof(typeof(hdr), num_buffers),
- sizeof hdr.num_buffers)) {
+ copy_to_iter(&headcount, 2, &fixup) != 2) {
vq_err(vq, "Failed num_buffers write");
vhost_discard_vq_desc(vq, headcount);
break;
req_size, vq->iov[0].iov_len);
break;
}
- ret = memcpy_fromiovecend(req, &vq->iov[0], 0, req_size);
+ ret = copy_from_user(req, vq->iov[0].iov_base, req_size);
if (unlikely(ret)) {
vq_err(vq, "Faulted on virtio_scsi_cmd_req\n");
break;
struct vring_desc desc;
unsigned int i = 0, count, found = 0;
u32 len = vhost32_to_cpu(vq, indirect->len);
+ struct iov_iter from;
int ret;
/* Sanity check */
vq_err(vq, "Translation failure %d in indirect.\n", ret);
return ret;
}
+ iov_iter_init(&from, READ, vq->indirect, ret, len);
/* We will use the result as an address to read from, so most
* architectures only need a compiler barrier here. */
i, count);
return -EINVAL;
}
- if (unlikely(memcpy_fromiovec((unsigned char *)&desc,
- vq->indirect, sizeof desc))) {
+ if (unlikely(copy_from_iter(&desc, sizeof(desc), &from) !=
+ sizeof(desc))) {
vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n",
i, (size_t)vhost64_to_cpu(vq, indirect->addr) + i * sizeof desc);
return -EINVAL;
_debug("- range %u-%u%s",
offset, to, msg->msg_flags ? " [more]" : "");
- iov_iter_init(&msg->msg_iter, WRITE,
- (struct iovec *) iov, 1, to - offset);
+ iov_iter_kvec(&msg->msg_iter, WRITE | ITER_KVEC,
+ iov, 1, to - offset);
/* have to change the state *before* sending the last
* packet as RxRPC might give us the reply before it
msg.msg_name = NULL;
msg.msg_namelen = 0;
- iov_iter_init(&msg.msg_iter, WRITE, (struct iovec *)iov, 1,
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iov, 1,
call->request_size);
msg.msg_control = NULL;
msg.msg_controllen = 0;
void afs_send_empty_reply(struct afs_call *call)
{
struct msghdr msg;
- struct iovec iov[1];
+ struct kvec iov[1];
_enter("");
iov[0].iov_len = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
- iov_iter_init(&msg.msg_iter, WRITE, iov, 0, 0); /* WTF? */
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iov, 0, 0); /* WTF? */
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len)
{
struct msghdr msg;
- struct iovec iov[1];
+ struct kvec iov[1];
int n;
_enter("");
iov[0].iov_len = len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
- iov_iter_init(&msg.msg_iter, WRITE, iov, 1, len);
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iov, 1, len);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
int af_alg_release(struct socket *sock);
int af_alg_accept(struct sock *sk, struct socket *newsock);
-int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
- int write);
+int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len);
void af_alg_free_sg(struct af_alg_sgl *sgl);
int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con);
u8 mic[8];
} __packed;
+/* Management MIC information element (IEEE 802.11w) for GMAC and CMAC-256 */
+struct ieee80211_mmie_16 {
+ u8 element_id;
+ u8 length;
+ __le16 key_id;
+ u8 sequence_number[6];
+ u8 mic[16];
+} __packed;
+
struct ieee80211_vendor_ie {
u8 element_id;
u8 len;
WLAN_KEY_LEN_WEP40 = 5,
WLAN_KEY_LEN_WEP104 = 13,
WLAN_KEY_LEN_CCMP = 16,
+ WLAN_KEY_LEN_CCMP_256 = 32,
WLAN_KEY_LEN_TKIP = 32,
WLAN_KEY_LEN_AES_CMAC = 16,
WLAN_KEY_LEN_SMS4 = 32,
+ WLAN_KEY_LEN_GCMP = 16,
+ WLAN_KEY_LEN_GCMP_256 = 32,
+ WLAN_KEY_LEN_BIP_CMAC_256 = 32,
+ WLAN_KEY_LEN_BIP_GMAC_128 = 16,
+ WLAN_KEY_LEN_BIP_GMAC_256 = 32,
};
#define IEEE80211_WEP_IV_LEN 4
#define IEEE80211_CCMP_HDR_LEN 8
#define IEEE80211_CCMP_MIC_LEN 8
#define IEEE80211_CCMP_PN_LEN 6
+#define IEEE80211_CCMP_256_HDR_LEN 8
+#define IEEE80211_CCMP_256_MIC_LEN 16
+#define IEEE80211_CCMP_256_PN_LEN 6
#define IEEE80211_TKIP_IV_LEN 8
#define IEEE80211_TKIP_ICV_LEN 4
#define IEEE80211_CMAC_PN_LEN 6
+#define IEEE80211_GMAC_PN_LEN 6
+#define IEEE80211_GCMP_HDR_LEN 8
+#define IEEE80211_GCMP_MIC_LEN 16
+#define IEEE80211_GCMP_PN_LEN 6
/* Public action codes */
enum ieee80211_pub_actioncode {
#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
+#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
+#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
#define WLAN_CIPHER_SUITE_SMS4 0x00147201
/*master notify fw on finish for slave's flr*/
MLX4_CMD_INFORM_FLR_DONE = 0x5b,
+ MLX4_CMD_VIRT_PORT_MAP = 0x5c,
MLX4_CMD_GET_OP_REQ = 0x59,
/* TPT commands */
MLX4_CMD_TIME_CLASS_C = 60000,
};
+enum {
+ /* virtual to physical port mapping opcode modifiers */
+ MLX4_GET_PORT_VIRT2PHY = 0x0,
+ MLX4_SET_PORT_VIRT2PHY = 0x1,
+};
+
enum {
MLX4_MAILBOX_SIZE = 4096,
MLX4_ACCESS_MEM_ALIGN = 256,
MLX4_FLAG_SLAVE = 1 << 3,
MLX4_FLAG_SRIOV = 1 << 4,
MLX4_FLAG_OLD_REG_MAC = 1 << 6,
+ MLX4_FLAG_BONDED = 1 << 7
};
enum {
MLX4_DEV_CAP_FLAG2_SYS_EQS = 1LL << 17,
MLX4_DEV_CAP_FLAG2_80_VFS = 1LL << 18,
MLX4_DEV_CAP_FLAG2_FS_A0 = 1LL << 19,
- MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT = 1LL << 20
+ MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT = 1LL << 20,
+ MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21
};
enum {
MLX4_BMME_FLAG_TYPE_2_WIN = 1 << 9,
MLX4_BMME_FLAG_RESERVED_LKEY = 1 << 10,
MLX4_BMME_FLAG_FAST_REG_WR = 1 << 11,
+ MLX4_BMME_FLAG_PORT_REMAP = 1 << 24,
MLX4_BMME_FLAG_VSD_INIT2RTR = 1 << 28,
};
+enum {
+ MLX4_FLAG_PORT_REMAP = MLX4_BMME_FLAG_PORT_REMAP
+};
+
enum mlx4_event {
MLX4_EVENT_TYPE_COMP = 0x00,
MLX4_EVENT_TYPE_PATH_MIG = 0x01,
int mlx4_get_base_gid_ix(struct mlx4_dev *dev, int slave, int port);
int mlx4_config_vxlan_port(struct mlx4_dev *dev, __be16 udp_port);
+int mlx4_disable_rx_port_check(struct mlx4_dev *dev, bool dis);
+int mlx4_virt2phy_port_map(struct mlx4_dev *dev, u32 port1, u32 port2);
int mlx4_vf_smi_enabled(struct mlx4_dev *dev, int slave, int port);
int mlx4_vf_get_enable_smi_admin(struct mlx4_dev *dev, int slave, int port);
int mlx4_vf_set_enable_smi_admin(struct mlx4_dev *dev, int slave, int port,
MLX4_DEV_EVENT_SLAVE_SHUTDOWN,
};
+enum {
+ MLX4_INTFF_BONDING = 1 << 0
+};
+
struct mlx4_interface {
void * (*add) (struct mlx4_dev *dev);
void (*remove)(struct mlx4_dev *dev, void *context);
void * (*get_dev)(struct mlx4_dev *dev, void *context, u8 port);
struct list_head list;
enum mlx4_protocol protocol;
+ int flags;
};
int mlx4_register_interface(struct mlx4_interface *intf);
void mlx4_unregister_interface(struct mlx4_interface *intf);
+int mlx4_bond(struct mlx4_dev *dev);
+int mlx4_unbond(struct mlx4_dev *dev);
+static inline int mlx4_is_bonded(struct mlx4_dev *dev)
+{
+ return !!(dev->flags & MLX4_FLAG_BONDED);
+}
+
+struct mlx4_port_map {
+ u8 port1;
+ u8 port2;
+};
+
+int mlx4_port_map_set(struct mlx4_dev *dev, struct mlx4_port_map *v2p);
+
void *mlx4_get_protocol_dev(struct mlx4_dev *dev, enum mlx4_protocol proto, int port);
static inline u64 mlx4_mac_to_u64(u8 *addr)
MLX4_QP_BIT_RRE = 1 << 15,
MLX4_QP_BIT_RWE = 1 << 14,
MLX4_QP_BIT_RAE = 1 << 13,
+ MLX4_QP_BIT_FPP = 1 << 3,
MLX4_QP_BIT_RIC = 1 << 4,
};
#include <linux/netdev_features.h>
#include <linux/neighbour.h>
#include <uapi/linux/netdevice.h>
+#include <uapi/linux/if_bonding.h>
struct netpoll_info;
struct device;
#define NETDEV_RESEND_IGMP 0x0016
#define NETDEV_PRECHANGEMTU 0x0017 /* notify before mtu change happened */
#define NETDEV_CHANGEINFODATA 0x0018
+#define NETDEV_BONDING_INFO 0x0019
int register_netdevice_notifier(struct notifier_block *nb);
int unregister_netdevice_notifier(struct notifier_block *nb);
compute_pseudo(skb, proto)); \
} while (0)
+static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr,
+ int start, int offset)
+{
+ __wsum delta;
+
+ BUG_ON(!NAPI_GRO_CB(skb)->csum_valid);
+
+ delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset);
+
+ /* Adjust skb->csum since we changed the packet */
+ skb->csum = csum_add(skb->csum, delta);
+ NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
+}
+
+
static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type,
const void *daddr, const void *saddr,
struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
netdev_features_t features);
+struct netdev_bonding_info {
+ ifslave slave;
+ ifbond master;
+};
+
+struct netdev_notifier_bonding_info {
+ struct netdev_notifier_info info; /* must be first */
+ struct netdev_bonding_info bonding_info;
+};
+
+void netdev_bonding_info_change(struct net_device *dev,
+ struct netdev_bonding_info *bonding_info);
+
static inline
struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
{
#ifndef _LINUX_RHASHTABLE_H
#define _LINUX_RHASHTABLE_H
+#include <linux/compiler.h>
#include <linux/list_nulls.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
* @p: Configuration parameters
* @run_work: Deferred worker to expand/shrink asynchronously
* @mutex: Mutex to protect current/future table swapping
+ * @walkers: List of active walkers
* @being_destroyed: True if table is set up for destruction
*/
struct rhashtable {
struct rhashtable_params p;
struct work_struct run_work;
struct mutex mutex;
+ struct list_head walkers;
bool being_destroyed;
};
+/**
+ * struct rhashtable_walker - Hash table walker
+ * @list: List entry on list of walkers
+ * @resize: Resize event occured
+ */
+struct rhashtable_walker {
+ struct list_head list;
+ bool resize;
+};
+
+/**
+ * struct rhashtable_iter - Hash table iterator, fits into netlink cb
+ * @ht: Table to iterate through
+ * @p: Current pointer
+ * @walker: Associated rhashtable walker
+ * @slot: Current slot
+ * @skip: Number of entries to skip in slot
+ */
+struct rhashtable_iter {
+ struct rhashtable *ht;
+ struct rhash_head *p;
+ struct rhashtable_walker *walker;
+ unsigned int slot;
+ unsigned int skip;
+};
+
static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash)
{
return NULLS_MARKER(ht->p.nulls_base + hash);
bool (*compare)(void *, void *),
void *arg);
+int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter);
+void rhashtable_walk_exit(struct rhashtable_iter *iter);
+int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
+void *rhashtable_walk_next(struct rhashtable_iter *iter);
+void rhashtable_walk_stop(struct rhashtable_iter *iter) __releases(RCU);
+
void rhashtable_destroy(struct rhashtable *ht);
#define rht_dereference(p, ht) \
__u32 hash;
__be16 vlan_proto;
__u16 vlan_tci;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned int napi_id;
+#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
+ union {
+ unsigned int napi_id;
+ unsigned int sender_cpu;
+ };
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
}
static inline int skb_add_data(struct sk_buff *skb,
- char __user *from, int copy)
+ struct iov_iter *from, int copy)
{
const int off = skb->len;
if (skb->ip_summed == CHECKSUM_NONE) {
- int err = 0;
- __wsum csum = csum_and_copy_from_user(from, skb_put(skb, copy),
- copy, 0, &err);
- if (!err) {
+ __wsum csum = 0;
+ if (csum_and_copy_from_iter(skb_put(skb, copy), copy,
+ &csum, from) == copy) {
skb->csum = csum_block_add(skb->csum, csum, off);
return 0;
}
- } else if (!copy_from_user(skb_put(skb, copy), from, copy))
+ } else if (copy_from_iter(skb_put(skb, copy), copy, from) == copy)
return 0;
__skb_trim(skb, off);
static inline int memcpy_from_msg(void *data, struct msghdr *msg, int len)
{
- /* XXX: stripping const */
- return memcpy_fromiovec(data, (struct iovec *)msg->msg_iter.iov, len);
+ return copy_from_iter(data, len, &msg->msg_iter) == len ? 0 : -EFAULT;
}
static inline int memcpy_to_msg(struct msghdr *msg, void *data, int len)
compute_pseudo(skb, proto)); \
} while (0)
+/* Update skbuf and packet to reflect the remote checksum offload operation.
+ * When called, ptr indicates the starting point for skb->csum when
+ * ip_summed is CHECKSUM_COMPLETE. If we need create checksum complete
+ * here, skb_postpull_rcsum is done so skb->csum start is ptr.
+ */
+static inline void skb_remcsum_process(struct sk_buff *skb, void *ptr,
+ int start, int offset)
+{
+ __wsum delta;
+
+ if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE)) {
+ __skb_checksum_complete(skb);
+ skb_postpull_rcsum(skb, skb->data, ptr - (void *)skb->data);
+ }
+
+ delta = remcsum_adjust(ptr, skb->csum, start, offset);
+
+ /* Adjust skb->csum since we changed the packet */
+ skb->csum = csum_add(skb->csum, delta);
+}
+
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
void nf_conntrack_destroy(struct nf_conntrack *nfct);
static inline void nf_conntrack_put(struct nf_conntrack *nfct)
/* IPX options */
#define IPX_TYPE 1
-extern int csum_partial_copy_fromiovecend(unsigned char *kdata,
- struct iovec *iov,
- int offset,
- unsigned int len, __wsum *csump);
-extern unsigned long iov_pages(const struct iovec *iov, int offset,
- unsigned long nr_segs);
-
extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
-int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
-int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
- int offset, int len);
-int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata,
- int offset, int len);
-
#endif
ssize_t vmci_qpair_peek(struct vmci_qp *qpair, void *buf, size_t buf_size,
int mode);
ssize_t vmci_qpair_enquev(struct vmci_qp *qpair,
- void *iov, size_t iov_size, int mode);
+ struct msghdr *msg, size_t iov_size, int mode);
ssize_t vmci_qpair_dequev(struct vmci_qp *qpair,
struct msghdr *msg, size_t iov_size, int mode);
ssize_t vmci_qpair_peekv(struct vmci_qp *qpair, struct msghdr *msg, size_t iov_size,
s8 rssi;
u16 uuid_count;
u8 (*uuids)[16];
+ unsigned long scan_start;
+ unsigned long scan_duration;
};
struct hci_conn_hash {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
+ u8 present;
u8 hash192[16];
u8 rand192[16];
u8 hash256[16];
__u16 conn_info_min_age;
__u16 conn_info_max_age;
__u8 ssp_debug_mode;
+ __u8 hw_error_code;
__u32 clock;
__u16 devid_source;
struct work_struct power_on;
struct delayed_work power_off;
+ struct work_struct error_reset;
__u16 discov_timeout;
struct delayed_work discov_off;
unsigned long dev_flags;
struct delayed_work le_scan_disable;
+ struct delayed_work le_scan_restart;
__s8 adv_tx_power;
__u8 adv_data[HCI_MAX_AD_LENGTH];
int (*setup)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
void (*notify)(struct hci_dev *hdev, unsigned int evt);
+ void (*hw_error)(struct hci_dev *hdev, u8 code);
int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
};
hdev->discovery.uuid_count = 0;
kfree(hdev->discovery.uuids);
hdev->discovery.uuids = NULL;
+ hdev->discovery.scan_start = 0;
+ hdev->discovery.scan_duration = 0;
}
bool hci_discovery_active(struct hci_dev *hdev);
#define DISCOV_INTERLEAVED_TIMEOUT 5120 /* msec */
#define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04
#define DISCOV_BREDR_INQUIRY_LEN 0x08
+#define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_auth_failed(struct hci_conn *conn, u8 status);
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
-void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020
#define MGMT_READ_LOCAL_OOB_DATA_SIZE 0
struct mgmt_rp_read_local_oob_data {
- __u8 hash[16];
- __u8 rand[16];
-} __packed;
-struct mgmt_rp_read_local_oob_ext_data {
__u8 hash192[16];
__u8 rand192[16];
__u8 hash256[16];
int mode;
};
+struct netdev_notify_work {
+ struct delayed_work work;
+ struct slave *slave;
+ struct net_device *dev;
+};
+
struct slave {
struct net_device *dev; /* first - useful for panic debug */
struct bonding *bond; /* our master */
#define bond_slave_get_rtnl(dev) \
((struct slave *) rtnl_dereference(dev->rx_handler_data))
+void bond_queue_slave_event(struct slave *slave);
+
struct bond_vlan_tag {
__be16 vlan_proto;
unsigned short vlan_id;
{
if (slave->backup) {
slave->backup = 0;
+ bond_queue_slave_event(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
{
if (!slave->backup) {
slave->backup = 1;
+ bond_queue_slave_event(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
slave->backup = slave_state;
if (notify) {
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
+ bond_queue_slave_event(slave);
slave->should_notify = 0;
} else {
if (slave->should_notify)
return slave->inactive;
}
+static inline void bond_set_slave_link_state(struct slave *slave, int state)
+{
+ slave->link = state;
+ bond_queue_slave_event(slave);
+}
+
static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be32 local)
{
struct in_device *in_dev;
* @rcu_head: RCU callback used to free the struct
* @owner_nlportid: netlink portid of owner (if this should is a request
* owned by a particular socket)
+ * @delay: delay in seconds to use before starting the first scan
+ * cycle. The driver may ignore this parameter and start
+ * immediately (or at any other time), if this feature is not
+ * supported.
*/
struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids;
struct cfg80211_match_set *match_sets;
int n_match_sets;
s32 min_rssi_thold;
+ u32 delay;
u8 mac_addr[ETH_ALEN] __aligned(2);
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
* @ssid_len: Length of SSID given in @ssid.
* @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode.
* @txpower: TX power in dBm
+ * @txpower_type: TX power adjustment used to control per packet Transmit
+ * Power Control (TPC) in lower driver for the current vif. In particular
+ * TPC is enabled if value passed in %txpower_type is
+ * NL80211_TX_POWER_LIMITED (allow using less than specified from
+ * userspace), whereas TPC is disabled if %txpower_type is set to
+ * NL80211_TX_POWER_FIXED (use value configured from userspace)
* @p2p_noa_attr: P2P NoA attribute for P2P powersave
*/
struct ieee80211_bss_conf {
size_t ssid_len;
bool hidden_ssid;
int txpower;
+ enum nl80211_tx_power_setting txpower_type;
struct ieee80211_p2p_noa_attr p2p_noa_attr;
};
* @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
* that the key is pairwise rather then a shared key.
* @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
- * CCMP key if it requires CCMP encryption of management frames (MFP) to
- * be done in software.
+ * CCMP/GCMP key if it requires CCMP/GCMP encryption of management frames
+ * (MFP) to be done in software.
* @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
* if space should be prepared for the IV, but the IV
* itself should not be generated. Do not set together with
* RX, if your crypto engine can't deal with TX you can also set the
* %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
* @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the
- * driver for a CCMP key to indicate that is requires IV generation
+ * driver for a CCMP/GCMP key to indicate that is requires IV generation
* only for managment frames (MFP).
* @IEEE80211_KEY_FLAG_RESERVE_TAILROOM: This flag should be set by the
* driver for a key to indicate that sufficient tailroom must always
* be created. It is expected user-space will create vifs as
* desired (and thus have them named as desired).
*
+ * @IEEE80211_HW_SW_CRYPTO_CONTROL: The driver wants to control which of the
+ * crypto algorithms can be done in software - so don't automatically
+ * try to fall back to it if hardware crypto fails, but do so only if
+ * the driver returns 1. This also forces the driver to advertise its
+ * supported cipher suites.
+ *
* @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
* queue mapping in order to use different queues (not just one per AC)
* for different virtual interfaces. See the doc section on HW queue
IEEE80211_HW_MFP_CAPABLE = 1<<13,
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
IEEE80211_HW_NO_AUTO_VIF = 1<<15,
+ IEEE80211_HW_SW_CRYPTO_CONTROL = 1<<16,
/* free slots */
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
* added; if you return 0 then hw_key_idx must be assigned to the
* hardware key index, you are free to use the full u8 range.
*
+ * Note that in the case that the @IEEE80211_HW_SW_CRYPTO_CONTROL flag is
+ * set, mac80211 will not automatically fall back to software crypto if
+ * enabling hardware crypto failed. The set_key() call may also return the
+ * value 1 to permit this specific key/algorithm to be done in software.
+ *
* When the cmd is %DISABLE_KEY then it must succeed.
*
* Note that it is permissible to not decrypt a frame even if a key
* reverse order than in packet)
* @aes_cmac: PN data, most significant byte first (big endian,
* reverse order than in packet)
+ * @aes_gmac: PN data, most significant byte first (big endian,
+ * reverse order than in packet)
+ * @gcmp: PN data, most significant byte first (big endian,
+ * reverse order than in packet)
*/
struct ieee80211_key_seq {
union {
struct {
u8 pn[6];
} aes_cmac;
+ struct {
+ u8 pn[6];
+ } aes_gmac;
+ struct {
+ u8 pn[6];
+ } gcmp;
};
};
* ieee80211_get_key_rx_seq - get key RX sequence counter
*
* @keyconf: the parameter passed with the set key
- * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
* the value on TID 0 is also used for non-QoS frames. For
* CMAC, only TID 0 is valid.
* @seq: buffer to receive the sequence data
* ieee80211_set_key_rx_seq - set key RX sequence counter
*
* @keyconf: the parameter passed with the set key
- * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
* the value on TID 0 is also used for non-QoS frames. For
* CMAC, only TID 0 is valid.
* @seq: new sequence data
struct pingfakehdr {
struct icmphdr icmph;
- struct iovec *iov;
+ struct msghdr *msg;
sa_family_t family;
__wsum wcheck;
};
}
static inline int skb_do_copy_data_nocache(struct sock *sk, struct sk_buff *skb,
- char __user *from, char *to,
+ struct iov_iter *from, char *to,
int copy, int offset)
{
if (skb->ip_summed == CHECKSUM_NONE) {
- int err = 0;
- __wsum csum = csum_and_copy_from_user(from, to, copy, 0, &err);
- if (err)
- return err;
+ __wsum csum = 0;
+ if (csum_and_copy_from_iter(to, copy, &csum, from) != copy)
+ return -EFAULT;
skb->csum = csum_block_add(skb->csum, csum, offset);
} else if (sk->sk_route_caps & NETIF_F_NOCACHE_COPY) {
- if (!access_ok(VERIFY_READ, from, copy) ||
- __copy_from_user_nocache(to, from, copy))
+ if (copy_from_iter_nocache(to, copy, from) != copy)
return -EFAULT;
- } else if (copy_from_user(to, from, copy))
+ } else if (copy_from_iter(to, copy, from) != copy)
return -EFAULT;
return 0;
}
static inline int skb_add_data_nocache(struct sock *sk, struct sk_buff *skb,
- char __user *from, int copy)
+ struct iov_iter *from, int copy)
{
int err, offset = skb->len;
return err;
}
-static inline int skb_copy_to_page_nocache(struct sock *sk, char __user *from,
+static inline int skb_copy_to_page_nocache(struct sock *sk, struct iov_iter *from,
struct sk_buff *skb,
struct page *page,
int off, int copy)
return dopt;
}
+/* locally generated TCP pure ACKs have skb->truesize == 2
+ * (check tcp_send_ack() in net/ipv4/tcp_output.c )
+ * This is much faster than dissecting the packet to find out.
+ * (Think of GRE encapsulations, IPv4, IPv6, ...)
+ */
+static inline bool skb_is_tcp_pure_ack(const struct sk_buff *skb)
+{
+ return skb->truesize == 2;
+}
+
+static inline void skb_set_tcp_pure_ack(struct sk_buff *skb)
+{
+ skb->truesize = 2;
+}
+
#endif /* _TCP_H */
int len, int odd, struct sk_buff *skb)
{
struct msghdr *msg = from;
- /* XXX: stripping const */
- return memcpy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len);
+ return copy_from_iter(to, len, &msg->msg_iter) != len ? -EFAULT : 0;
}
/* Designate sk as UDP-Lite socket */
* %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
*
* @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
- * either a dump request on a %NL80211_ATTR_WIPHY or a specific get
- * on an %NL80211_ATTR_IFINDEX is supported.
+ * either a dump request for all interfaces or a specific get with a
+ * single %NL80211_ATTR_IFINDEX is supported.
* @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
* %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
* @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
* if passed, define which channels should be scanned; if not
* passed, all channels allowed for the current regulatory domain
* are used. Extra IEs can also be passed from the userspace by
- * using the %NL80211_ATTR_IE attribute.
+ * using the %NL80211_ATTR_IE attribute. The first cycle of the
+ * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ * is supplied.
* @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
* scheduled scan is not running. The caller may assume that as soon
* as the call returns, it is safe to start a new scheduled scan again.
* should be contained in the result as the sum of the respective counters
* over all channels.
*
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
+ * WoWLAN net-detect scan) is started, u32 in seconds.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
NL80211_ATTR_SURVEY_RADIO_STATS,
+ NL80211_ATTR_NETNS_FD,
+
+ NL80211_ATTR_SCHED_SCAN_DELAY,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
* @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
* is detected. This is a nested attribute that contains the
* same attributes used with @NL80211_CMD_START_SCHED_SCAN. It
- * specifies how the scan is performed (e.g. the interval and the
- * channels to scan) as well as the scan results that will
- * trigger a wake (i.e. the matchsets). This attribute is also
- * sent in a response to @NL80211_CMD_GET_WIPHY, indicating the
- * number of match sets supported by the driver (u32).
+ * specifies how the scan is performed (e.g. the interval, the
+ * channels to scan and the initial delay) as well as the scan
+ * results that will trigger a wake (i.e. the matchsets). This
+ * attribute is also sent in a response to
+ * @NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ * supported by the driver (u32).
* @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
* containing an array with information about what triggered the
* wake up. If no elements are present in the array, it means
TCA_FQ_FLOW_REFILL_DELAY, /* flow credit refill delay in usec */
+ TCA_FQ_ORPHAN_MASK, /* mask applied to orphaned skb hashes */
+
__TCA_FQ_MAX
};
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
- gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
+ gcd.o lcm.o list_sort.o uuid.o flex_array.o clz_ctz.o \
bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o
obj-y += string_helpers.o
+++ /dev/null
-#include <linux/uaccess.h>
-#include <linux/export.h>
-#include <linux/uio.h>
-
-/*
- * Copy iovec to kernel. Returns -EFAULT on error.
- *
- * Note: this modifies the original iovec.
- */
-
-int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
-{
- while (len > 0) {
- if (iov->iov_len) {
- int copy = min_t(unsigned int, len, iov->iov_len);
- if (copy_from_user(kdata, iov->iov_base, copy))
- return -EFAULT;
- len -= copy;
- kdata += copy;
- iov->iov_base += copy;
- iov->iov_len -= copy;
- }
- iov++;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(memcpy_fromiovec);
-
-/*
- * Copy kernel to iovec. Returns -EFAULT on error.
- */
-
-int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
- int offset, int len)
-{
- int copy;
- for (; len > 0; ++iov) {
- /* Skip over the finished iovecs */
- if (unlikely(offset >= iov->iov_len)) {
- offset -= iov->iov_len;
- continue;
- }
- copy = min_t(unsigned int, iov->iov_len - offset, len);
- if (copy_to_user(iov->iov_base + offset, kdata, copy))
- return -EFAULT;
- offset = 0;
- kdata += copy;
- len -= copy;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(memcpy_toiovecend);
-
-/*
- * Copy iovec to kernel. Returns -EFAULT on error.
- */
-
-int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
- int offset, int len)
-{
- /* No data? Done! */
- if (len == 0)
- return 0;
-
- /* Skip over the finished iovecs */
- while (offset >= iov->iov_len) {
- offset -= iov->iov_len;
- iov++;
- }
-
- while (len > 0) {
- u8 __user *base = iov->iov_base + offset;
- int copy = min_t(unsigned int, len, iov->iov_len - offset);
-
- offset = 0;
- if (copy_from_user(kdata, base, copy))
- return -EFAULT;
- len -= copy;
- kdata += copy;
- iov++;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(memcpy_fromiovecend);
{
struct rhashtable *ht;
struct bucket_table *tbl;
+ struct rhashtable_walker *walker;
ht = container_of(work, struct rhashtable, run_work);
mutex_lock(&ht->mutex);
+ if (ht->being_destroyed)
+ goto unlock;
+
tbl = rht_dereference(ht->tbl, ht);
+ list_for_each_entry(walker, &ht->walkers, list)
+ walker->resize = true;
+
if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size))
rhashtable_expand(ht);
else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size))
rhashtable_shrink(ht);
+unlock:
mutex_unlock(&ht->mutex);
}
}
EXPORT_SYMBOL_GPL(rhashtable_lookup_compare_insert);
+/**
+ * rhashtable_walk_init - Initialise an iterator
+ * @ht: Table to walk over
+ * @iter: Hash table Iterator
+ *
+ * This function prepares a hash table walk.
+ *
+ * Note that if you restart a walk after rhashtable_walk_stop you
+ * may see the same object twice. Also, you may miss objects if
+ * there are removals in between rhashtable_walk_stop and the next
+ * call to rhashtable_walk_start.
+ *
+ * For a completely stable walk you should construct your own data
+ * structure outside the hash table.
+ *
+ * This function may sleep so you must not call it from interrupt
+ * context or with spin locks held.
+ *
+ * You must call rhashtable_walk_exit if this function returns
+ * successfully.
+ */
+int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
+{
+ iter->ht = ht;
+ iter->p = NULL;
+ iter->slot = 0;
+ iter->skip = 0;
+
+ iter->walker = kmalloc(sizeof(*iter->walker), GFP_KERNEL);
+ if (!iter->walker)
+ return -ENOMEM;
+
+ mutex_lock(&ht->mutex);
+ list_add(&iter->walker->list, &ht->walkers);
+ mutex_unlock(&ht->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_init);
+
+/**
+ * rhashtable_walk_exit - Free an iterator
+ * @iter: Hash table Iterator
+ *
+ * This function frees resources allocated by rhashtable_walk_init.
+ */
+void rhashtable_walk_exit(struct rhashtable_iter *iter)
+{
+ mutex_lock(&iter->ht->mutex);
+ list_del(&iter->walker->list);
+ mutex_unlock(&iter->ht->mutex);
+ kfree(iter->walker);
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
+
+/**
+ * rhashtable_walk_start - Start a hash table walk
+ * @iter: Hash table iterator
+ *
+ * Start a hash table walk. Note that we take the RCU lock in all
+ * cases including when we return an error. So you must always call
+ * rhashtable_walk_stop to clean up.
+ *
+ * Returns zero if successful.
+ *
+ * Returns -EAGAIN if resize event occured. Note that the iterator
+ * will rewind back to the beginning and you may use it immediately
+ * by calling rhashtable_walk_next.
+ */
+int rhashtable_walk_start(struct rhashtable_iter *iter)
+{
+ rcu_read_lock();
+
+ if (iter->walker->resize) {
+ iter->slot = 0;
+ iter->skip = 0;
+ iter->walker->resize = false;
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_start);
+
+/**
+ * rhashtable_walk_next - Return the next object and advance the iterator
+ * @iter: Hash table iterator
+ *
+ * Note that you must call rhashtable_walk_stop when you are finished
+ * with the walk.
+ *
+ * Returns the next object or NULL when the end of the table is reached.
+ *
+ * Returns -EAGAIN if resize event occured. Note that the iterator
+ * will rewind back to the beginning and you may continue to use it.
+ */
+void *rhashtable_walk_next(struct rhashtable_iter *iter)
+{
+ const struct bucket_table *tbl;
+ struct rhashtable *ht = iter->ht;
+ struct rhash_head *p = iter->p;
+ void *obj = NULL;
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+
+ if (p) {
+ p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot);
+ goto next;
+ }
+
+ for (; iter->slot < tbl->size; iter->slot++) {
+ int skip = iter->skip;
+
+ rht_for_each_rcu(p, tbl, iter->slot) {
+ if (!skip)
+ break;
+ skip--;
+ }
+
+next:
+ if (!rht_is_a_nulls(p)) {
+ iter->skip++;
+ iter->p = p;
+ obj = rht_obj(ht, p);
+ goto out;
+ }
+
+ iter->skip = 0;
+ }
+
+ iter->p = NULL;
+
+out:
+ if (iter->walker->resize) {
+ iter->p = NULL;
+ iter->slot = 0;
+ iter->skip = 0;
+ iter->walker->resize = false;
+ return ERR_PTR(-EAGAIN);
+ }
+
+ return obj;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_next);
+
+/**
+ * rhashtable_walk_stop - Finish a hash table walk
+ * @iter: Hash table iterator
+ *
+ * Finish a hash table walk.
+ */
+void rhashtable_walk_stop(struct rhashtable_iter *iter)
+{
+ rcu_read_unlock();
+ iter->p = NULL;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
+
static size_t rounded_hashtable_size(struct rhashtable_params *params)
{
return max(roundup_pow_of_two(params->nelem_hint * 4 / 3),
memset(ht, 0, sizeof(*ht));
mutex_init(&ht->mutex);
memcpy(&ht->p, params, sizeof(*params));
+ INIT_LIST_HEAD(&ht->walkers);
if (params->locks_mul)
ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
static struct device *bnep_get_device(struct bnep_session *session)
{
- struct hci_conn *conn;
+ struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
- conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
- if (!conn)
+ if (!conn || !conn->hcon)
return NULL;
- return &conn->dev;
+ return &conn->hcon->dev;
}
static struct device_type bnep_type = {
if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
u8 mode = 0x01;
+
hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
sizeof(mode), &mode);
} else {
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
/* Enable Secure Connections if supported and configured */
- if (bredr_sc_enabled(hdev)) {
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
+ bredr_sc_enabled(hdev)) {
u8 support = 0x01;
+
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
sizeof(support), &support);
}
cancel_delayed_work(&hdev->service_cache);
cancel_delayed_work_sync(&hdev->le_scan_disable);
+ cancel_delayed_work_sync(&hdev->le_scan_restart);
if (test_bit(HCI_MGMT, &hdev->dev_flags))
cancel_delayed_work_sync(&hdev->rpa_expired);
hci_dev_lock(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
if (hdev->dev_type == HCI_BREDR)
mgmt_powered(hdev, 0);
hci_conn_hash_flush(hdev);
hci_dev_unlock(hdev);
+ smp_unregister(hdev);
+
hci_notify(hdev, HCI_DEV_DOWN);
if (hdev->flush)
return err;
}
-int hci_dev_reset(__u16 dev)
+static int hci_dev_do_reset(struct hci_dev *hdev)
{
- struct hci_dev *hdev;
- int ret = 0;
+ int ret;
- hdev = hci_dev_get(dev);
- if (!hdev)
- return -ENODEV;
+ BT_DBG("%s %p", hdev->name, hdev);
hci_req_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- ret = -ENETDOWN;
- goto done;
- }
-
- if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
- ret = -EBUSY;
- goto done;
- }
-
- if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
- ret = -EOPNOTSUPP;
- goto done;
- }
-
/* Drop queues */
skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q);
ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
-done:
hci_req_unlock(hdev);
- hci_dev_put(hdev);
return ret;
}
+int hci_dev_reset(__u16 dev)
+{
+ struct hci_dev *hdev;
+ int err;
+
+ hdev = hci_dev_get(dev);
+ if (!hdev)
+ return -ENODEV;
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = -ENETDOWN;
+ goto done;
+ }
+
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
+ if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ err = hci_dev_do_reset(hdev);
+
+done:
+ hci_dev_put(hdev);
+ return err;
+}
+
int hci_dev_reset_stat(__u16 dev)
{
struct hci_dev *hdev;
BT_DBG("%s", hdev->name);
hci_dev_do_close(hdev);
+}
- smp_unregister(hdev);
+static void hci_error_reset(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev, error_reset);
+
+ BT_DBG("%s", hdev->name);
+
+ if (hdev->hw_error)
+ hdev->hw_error(hdev, hdev->hw_error_code);
+ else
+ BT_ERR("%s hardware error 0x%2.2x", hdev->name,
+ hdev->hw_error_code);
+
+ if (hci_dev_do_close(hdev))
+ return;
+
+ hci_dev_do_open(hdev);
}
static void hci_discov_off(struct work_struct *work)
if (hash192 && rand192) {
memcpy(data->hash192, hash192, sizeof(data->hash192));
memcpy(data->rand192, rand192, sizeof(data->rand192));
+ if (hash256 && rand256)
+ data->present = 0x03;
} else {
memset(data->hash192, 0, sizeof(data->hash192));
memset(data->rand192, 0, sizeof(data->rand192));
+ if (hash256 && rand256)
+ data->present = 0x02;
+ else
+ data->present = 0x00;
}
if (hash256 && rand256) {
} else {
memset(data->hash256, 0, sizeof(data->hash256));
memset(data->rand256, 0, sizeof(data->rand256));
+ if (hash192 && rand192)
+ data->present = 0x01;
}
BT_DBG("%s for %pMR", hdev->name, bdaddr);
return;
}
+ hdev->discovery.scan_start = 0;
+
switch (hdev->discovery.type) {
case DISCOV_TYPE_LE:
hci_dev_lock(hdev);
BT_DBG("%s", hdev->name);
+ cancel_delayed_work_sync(&hdev->le_scan_restart);
+
hci_req_init(&req, hdev);
hci_req_add_le_scan_disable(&req);
BT_ERR("Disable LE scanning request failed: err %d", err);
}
+static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status,
+ u16 opcode)
+{
+ unsigned long timeout, duration, scan_start, now;
+
+ BT_DBG("%s", hdev->name);
+
+ if (status) {
+ BT_ERR("Failed to restart LE scan: status %d", status);
+ return;
+ }
+
+ if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
+ !hdev->discovery.scan_start)
+ return;
+
+ /* When the scan was started, hdev->le_scan_disable has been queued
+ * after duration from scan_start. During scan restart this job
+ * has been canceled, and we need to queue it again after proper
+ * timeout, to make sure that scan does not run indefinitely.
+ */
+ duration = hdev->discovery.scan_duration;
+ scan_start = hdev->discovery.scan_start;
+ now = jiffies;
+ if (now - scan_start <= duration) {
+ int elapsed;
+
+ if (now >= scan_start)
+ elapsed = now - scan_start;
+ else
+ elapsed = ULONG_MAX - scan_start + now;
+
+ timeout = duration - elapsed;
+ } else {
+ timeout = 0;
+ }
+ queue_delayed_work(hdev->workqueue,
+ &hdev->le_scan_disable, timeout);
+}
+
+static void le_scan_restart_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ le_scan_restart.work);
+ struct hci_request req;
+ struct hci_cp_le_set_scan_enable cp;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ /* If controller is not scanning we are done. */
+ if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ return;
+
+ hci_req_init(&req, hdev);
+
+ hci_req_add_le_scan_disable(&req);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = LE_SCAN_ENABLE;
+ cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+ hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+
+ err = hci_req_run(&req, le_scan_restart_work_complete);
+ if (err)
+ BT_ERR("Restart LE scan request failed: err %d", err);
+}
+
/* Copy the Identity Address of the controller.
*
* If the controller has a public BD_ADDR, then by default use that one.
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
INIT_WORK(&hdev->tx_work, hci_tx_work);
INIT_WORK(&hdev->power_on, hci_power_on);
+ INIT_WORK(&hdev->error_reset, hci_error_reset);
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
+ INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
skb_queue_head_init(&hdev->rx_q);
skb_queue_head_init(&hdev->cmd_q);
rfkill_destroy(hdev->rfkill);
}
- smp_unregister(hdev);
-
device_del(&hdev->dev);
debugfs_remove_recursive(hdev->debugfs);
.release = single_release,
};
+static int remote_oob_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ struct oob_data *data;
+
+ hci_dev_lock(hdev);
+ list_for_each_entry(data, &hdev->remote_oob_data, list) {
+ seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n",
+ &data->bdaddr, data->bdaddr_type, data->present,
+ 16, data->hash192, 16, data->rand192,
+ 16, data->hash256, 19, data->rand256);
+ }
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int remote_oob_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, remote_oob_show, inode->i_private);
+}
+
+static const struct file_operations remote_oob_fops = {
+ .open = remote_oob_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int conn_info_min_age_set(void *data, u64 val)
{
struct hci_dev *hdev = data;
DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
conn_info_max_age_set, "%llu\n");
+static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+ .open = simple_open,
+ .read = use_debug_keys_read,
+ .llseek = default_llseek,
+};
+
static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
&hdev->manufacturer);
debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+ debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
+ &hdev->hw_error_code);
+
debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
&device_list_fops);
debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
&blacklist_fops);
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+ debugfs_create_file("remote_oob", 0400, hdev->debugfs, hdev,
+ &remote_oob_fops);
debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
&conn_info_min_age_fops);
debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
&conn_info_max_age_fops);
+ if (lmp_ssp_capable(hdev) || lmp_le_capable(hdev))
+ debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+ hdev, &use_debug_keys_fops);
+
if (lmp_sc_capable(hdev) || lmp_le_capable(hdev))
debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
hdev, &sc_only_mode_fops);
DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
NULL, "0x%4.4llx\n");
+static ssize_t ssp_debug_mode_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = hdev->ssp_debug_mode ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations ssp_debug_mode_fops = {
+ .open = simple_open,
+ .read = ssp_debug_mode_read,
+ .llseek = default_llseek,
+};
+
static int auto_accept_delay_set(void *data, u64 val)
{
struct hci_dev *hdev = data;
debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
&voice_setting_fops);
- if (lmp_ssp_capable(hdev))
+ if (lmp_ssp_capable(hdev)) {
+ debugfs_create_file("ssp_debug_mode", 0444, hdev->debugfs,
+ hdev, &ssp_debug_mode_fops);
debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
hdev, &auto_accept_delay_fops);
+ }
if (lmp_sniff_capable(hdev)) {
debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
#include "amp.h"
#include "smp.h"
+#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+
/* Handle HCI Event packets */
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
/* Reset all non-persistent flags */
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
- hdev->discovery.state = DISCOVERY_STOPPED;
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
hdev->inq_tx_power = HCI_TX_POWER_INVALID;
hdev->adv_tx_power = HCI_TX_POWER_INVALID;
hdev->features[1][0] &= ~LMP_HOST_SC;
}
- if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_sc_enable_complete(hdev, sent->support, status);
- else if (!status) {
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags) && !status) {
if (sent->support)
set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
else
hci_dev_unlock(hdev);
}
+static void hci_cc_write_ssp_debug_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ u8 status = *((u8 *) skb->data);
+ u8 *mode;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (status)
+ return;
+
+ mode = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE);
+ if (mode)
+ hdev->ssp_debug_mode = *mode;
+}
+
static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%2.2x", hdev->name, status);
if (conn->state != BT_CONFIG)
goto unlock;
- if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) {
+ if (!ev->status && lmp_ext_feat_capable(hdev) &&
+ lmp_ext_feat_capable(conn)) {
struct hci_cp_read_remote_ext_features cp;
cp.handle = ev->handle;
cp.page = 0x01;
hci_cc_read_tx_power(hdev, skb);
break;
+ case HCI_OP_WRITE_SSP_DEBUG_MODE:
+ hci_cc_write_ssp_debug_mode(hdev, skb);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
{
struct hci_ev_hardware_error *ev = (void *) skb->data;
- BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+ hdev->hw_error_code = ev->code;
+
+ queue_work(hdev->req_workqueue, &hdev->error_reset);
}
static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
return (conn->remote_auth & ~0x01) | (conn->auth_type & 0x01);
}
+static u8 bredr_oob_data_present(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct oob_data *data;
+
+ data = hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR);
+ if (!data)
+ return 0x00;
+
+ if (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) {
+ if (bredr_sc_enabled(hdev)) {
+ /* When Secure Connections is enabled, then just
+ * return the present value stored with the OOB
+ * data. The stored value contains the right present
+ * information. However it can only be trusted when
+ * not in Secure Connection Only mode.
+ */
+ if (!test_bit(HCI_SC_ONLY, &hdev->dev_flags))
+ return data->present;
+
+ /* When Secure Connections Only mode is enabled, then
+ * the P-256 values are required. If they are not
+ * available, then do not declare that OOB data is
+ * present.
+ */
+ if (!memcmp(data->rand256, ZERO_KEY, 16) ||
+ !memcmp(data->hash256, ZERO_KEY, 16))
+ return 0x00;
+
+ return 0x02;
+ }
+
+ /* When Secure Connections is not enabled or actually
+ * not supported by the hardware, then check that if
+ * P-192 data values are present.
+ */
+ if (!memcmp(data->rand192, ZERO_KEY, 16) ||
+ !memcmp(data->hash192, ZERO_KEY, 16))
+ return 0x00;
+
+ return 0x01;
+ }
+
+ return 0x00;
+}
+
static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_io_capa_request *ev = (void *) skb->data;
conn->auth_type &= HCI_AT_NO_BONDING_MITM;
cp.authentication = conn->auth_type;
-
- if (hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR) &&
- (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
- cp.oob_data = 0x01;
- else
- cp.oob_data = 0x00;
+ cp.oob_data = bredr_oob_data_present(conn);
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
sizeof(cp), &cp);
goto unlock;
data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR);
- if (data) {
- if (bredr_sc_enabled(hdev)) {
- struct hci_cp_remote_oob_ext_data_reply cp;
-
- bacpy(&cp.bdaddr, &ev->bdaddr);
- memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
- memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
- memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
- memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
+ if (!data) {
+ struct hci_cp_remote_oob_data_neg_reply cp;
- hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
- sizeof(cp), &cp);
- } else {
- struct hci_cp_remote_oob_data_reply cp;
+ bacpy(&cp.bdaddr, &ev->bdaddr);
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY,
+ sizeof(cp), &cp);
+ goto unlock;
+ }
- bacpy(&cp.bdaddr, &ev->bdaddr);
- memcpy(cp.hash, data->hash192, sizeof(cp.hash));
- memcpy(cp.rand, data->rand192, sizeof(cp.rand));
+ if (bredr_sc_enabled(hdev)) {
+ struct hci_cp_remote_oob_ext_data_reply cp;
- hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
- sizeof(cp), &cp);
+ bacpy(&cp.bdaddr, &ev->bdaddr);
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags)) {
+ memset(cp.hash192, 0, sizeof(cp.hash192));
+ memset(cp.rand192, 0, sizeof(cp.rand192));
+ } else {
+ memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
+ memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
}
+ memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
+ memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
+
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
+ sizeof(cp), &cp);
} else {
- struct hci_cp_remote_oob_data_neg_reply cp;
+ struct hci_cp_remote_oob_data_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY,
+ memcpy(cp.hash, data->hash192, sizeof(cp.hash));
+ memcpy(cp.rand, data->rand192, sizeof(cp.rand));
+
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
sizeof(cp), &cp);
}
static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
int flags)
{
- DECLARE_WAITQUEUE(wait, current);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
struct sock *sk = sock->sk, *nsk;
long timeo;
int err = 0;
/* Wait for an incoming connection. (wake-one). */
add_wait_queue_exclusive(sk_sleep(sk), &wait);
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
if (sk->sk_state != BT_LISTEN) {
err = -EBADFD;
break;
}
release_sock(sk);
- timeo = schedule_timeout(timeo);
+
+ timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
+
lock_sock_nested(sk, L2CAP_NESTING_PARENT);
}
- __set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
if (err)
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
+#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+
struct pending_cmd {
struct list_head list;
u16 opcode;
static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
+ struct mgmt_addr_info *addr = data;
int err;
BT_DBG("%s ", hdev->name);
+ if (!bdaddr_type_is_valid(addr->type))
+ return cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS, addr,
+ sizeof(*addr));
+
hci_dev_lock(hdev);
if (len == MGMT_ADD_REMOTE_OOB_DATA_SIZE) {
status, &cp->addr, sizeof(cp->addr));
} else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
struct mgmt_cp_add_remote_oob_ext_data *cp = data;
- u8 *rand192, *hash192;
+ u8 *rand192, *hash192, *rand256, *hash256;
u8 status;
- if (cp->addr.type != BDADDR_BREDR) {
- err = cmd_complete(sk, hdev->id,
- MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS,
- &cp->addr, sizeof(cp->addr));
- goto unlock;
- }
-
if (bdaddr_type_is_le(cp->addr.type)) {
+ /* Enforce zero-valued 192-bit parameters as
+ * long as legacy SMP OOB isn't implemented.
+ */
+ if (memcmp(cp->rand192, ZERO_KEY, 16) ||
+ memcmp(cp->hash192, ZERO_KEY, 16)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ addr, sizeof(*addr));
+ goto unlock;
+ }
+
rand192 = NULL;
hash192 = NULL;
} else {
- rand192 = cp->rand192;
- hash192 = cp->hash192;
+ /* In case one of the P-192 values is set to zero,
+ * then just disable OOB data for P-192.
+ */
+ if (!memcmp(cp->rand192, ZERO_KEY, 16) ||
+ !memcmp(cp->hash192, ZERO_KEY, 16)) {
+ rand192 = NULL;
+ hash192 = NULL;
+ } else {
+ rand192 = cp->rand192;
+ hash192 = cp->hash192;
+ }
+ }
+
+ /* In case one of the P-256 values is set to zero, then just
+ * disable OOB data for P-256.
+ */
+ if (!memcmp(cp->rand256, ZERO_KEY, 16) ||
+ !memcmp(cp->hash256, ZERO_KEY, 16)) {
+ rand256 = NULL;
+ hash256 = NULL;
+ } else {
+ rand256 = cp->rand256;
+ hash256 = cp->hash256;
}
err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
cp->addr.type, hash192, rand192,
- cp->hash256, cp->rand256);
+ hash256, rand256);
if (err < 0)
status = MGMT_STATUS_FAILED;
else
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ /* If the scan involves LE scan, pick proper timeout to schedule
+ * hdev->le_scan_disable that will stop it.
+ */
switch (hdev->discovery.type) {
case DISCOV_TYPE_LE:
timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
break;
}
- if (timeout)
+ if (timeout) {
+ /* When service discovery is used and the controller has
+ * a strict duplicate filter, it is important to remember
+ * the start and duration of the scan. This is required
+ * for restarting scanning during the discovery phase.
+ */
+ if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+ &hdev->quirks) &&
+ (hdev->discovery.uuid_count > 0 ||
+ hdev->discovery.rssi != HCI_RSSI_INVALID)) {
+ hdev->discovery.scan_start = jiffies;
+ hdev->discovery.scan_duration = timeout;
+ }
+
queue_delayed_work(hdev->workqueue,
&hdev->le_scan_disable, timeout);
+ }
unlock:
hci_dev_unlock(hdev);
* Dual-mode controllers shall operate with the public
* address as its identity address for BR/EDR and LE. So
* reject the attempt to create an invalid configuration.
+ *
+ * The same restrictions applies when secure connections
+ * has been enabled. For BR/EDR this is a controller feature
+ * while for LE it is a host stack feature. This means that
+ * switching BR/EDR back on when secure connections has been
+ * enabled is not a supported transaction.
*/
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
- bacmp(&hdev->static_addr, BDADDR_ANY)) {
+ (bacmp(&hdev->static_addr, BDADDR_ANY) ||
+ test_bit(HCI_SC_ENABLED, &hdev->dev_flags))) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
MGMT_STATUS_REJECTED);
goto unlock;
return err;
}
+static void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+ struct pending_cmd *cmd;
+ struct mgmt_mode *cp;
+
+ BT_DBG("%s status %u", hdev->name, status);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev);
+ if (!cmd)
+ goto unlock;
+
+ if (status) {
+ cmd_status(cmd->sk, cmd->index, cmd->opcode,
+ mgmt_status(status));
+ goto remove;
+ }
+
+ cp = cmd->param;
+
+ switch (cp->val) {
+ case 0x00:
+ clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ break;
+ case 0x01:
+ set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ break;
+ case 0x02:
+ set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ set_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ break;
+ }
+
+ send_settings_rsp(cmd->sk, MGMT_OP_SET_SECURE_CONN, hdev);
+ new_settings(hdev, cmd->sk);
+
+remove:
+ mgmt_pending_remove(cmd);
+unlock:
+ hci_dev_unlock(hdev);
+}
+
static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
+ struct hci_request req;
u8 val;
int err;
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
MGMT_STATUS_NOT_SUPPORTED);
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
+ lmp_sc_capable(hdev) &&
+ !test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+ MGMT_STATUS_REJECTED);
+
if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
- err = hci_send_cmd(hdev, HCI_OP_WRITE_SC_SUPPORT, 1, &val);
+ hci_req_init(&req, hdev);
+ hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, 1, &val);
+ err = hci_req_run(&req, sc_enable_complete);
if (err < 0) {
mgmt_pending_remove(cmd);
goto failed;
}
- if (cp->val == 0x02)
- set_bit(HCI_SC_ONLY, &hdev->dev_flags);
- else
- clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
-
failed:
hci_dev_unlock(hdev);
return err;
if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
!lmp_host_ssp_capable(hdev)) {
- u8 ssp = 1;
+ u8 mode = 0x01;
- hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
- }
+ hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
- if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
- u8 sc = 0x01;
- hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, sizeof(sc), &sc);
+ if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+ u8 support = 0x01;
+
+ hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT,
+ sizeof(support), &support);
+ }
}
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
hci_req_run(&req, NULL);
}
-void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
-{
- struct cmd_lookup match = { NULL, hdev };
- bool changed = false;
-
- if (status) {
- u8 mgmt_err = mgmt_status(status);
-
- if (enable) {
- if (test_and_clear_bit(HCI_SC_ENABLED,
- &hdev->dev_flags))
- new_settings(hdev, NULL);
- clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
- }
-
- mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
- cmd_status_rsp, &mgmt_err);
- return;
- }
-
- if (enable) {
- changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
- } else {
- changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
- clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
- }
-
- mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
- settings_rsp, &match);
-
- if (changed)
- new_settings(hdev, match.sk);
-
- if (match.sk)
- sock_put(match.sk);
-}
-
static void sk_lookup(struct pending_cmd *cmd, void *data)
{
struct cmd_lookup *match = data;
cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_status(status));
} else {
- if (bredr_sc_enabled(hdev) && hash256 && rand256) {
- struct mgmt_rp_read_local_oob_ext_data rp;
+ struct mgmt_rp_read_local_oob_data rp;
+ size_t rp_size = sizeof(rp);
- memcpy(rp.hash192, hash192, sizeof(rp.hash192));
- memcpy(rp.rand192, rand192, sizeof(rp.rand192));
+ memcpy(rp.hash192, hash192, sizeof(rp.hash192));
+ memcpy(rp.rand192, rand192, sizeof(rp.rand192));
+ if (bredr_sc_enabled(hdev) && hash256 && rand256) {
memcpy(rp.hash256, hash256, sizeof(rp.hash256));
memcpy(rp.rand256, rand256, sizeof(rp.rand256));
-
- cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA, 0,
- &rp, sizeof(rp));
} else {
- struct mgmt_rp_read_local_oob_data rp;
-
- memcpy(rp.hash, hash192, sizeof(rp.hash));
- memcpy(rp.rand, rand192, sizeof(rp.rand));
-
- cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA, 0,
- &rp, sizeof(rp));
+ rp_size -= sizeof(rp.hash256) + sizeof(rp.rand256);
}
+
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 0,
+ &rp, rp_size);
}
mgmt_pending_remove(cmd);
return false;
}
+static void restart_le_scan(struct hci_dev *hdev)
+{
+ /* If controller is not scanning we are done. */
+ if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ return;
+
+ if (time_after(jiffies + DISCOV_LE_RESTART_DELAY,
+ hdev->discovery.scan_start +
+ hdev->discovery.scan_duration))
+ return;
+
+ queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+ DISCOV_LE_RESTART_DELAY);
+}
+
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
/* When using service discovery with a RSSI threshold, then check
* if such a RSSI threshold is specified. If a RSSI threshold has
- * been specified, then all results with a RSSI smaller than the
- * RSSI threshold will be dropped.
+ * been specified, and HCI_QUIRK_STRICT_DUPLICATE_FILTER is not set,
+ * then all results with a RSSI smaller than the RSSI threshold will be
+ * dropped. If the quirk is set, let it through for further processing,
+ * as we might need to restart the scan.
*
* For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
* the results are also dropped.
*/
if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
- (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
+ (rssi == HCI_RSSI_INVALID ||
+ (rssi < hdev->discovery.rssi &&
+ !test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks))))
return;
/* Make sure that the buffer is big enough. The 5 extra bytes
* However when using service discovery, the value 127 will be
* returned when the RSSI is not available.
*/
- if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi)
+ if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi &&
+ link_type == ACL_LINK)
rssi = 0;
bacpy(&ev->addr.bdaddr, bdaddr);
* kept and checking possible scan response data
* will be skipped.
*/
- if (hdev->discovery.uuid_count > 0)
+ if (hdev->discovery.uuid_count > 0) {
match = eir_has_uuids(eir, eir_len,
hdev->discovery.uuid_count,
hdev->discovery.uuids);
- else
+ /* If duplicate filtering does not report RSSI changes,
+ * then restart scanning to ensure updated result with
+ * updated RSSI values.
+ */
+ if (match && test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+ &hdev->quirks))
+ restart_le_scan(hdev);
+ } else {
match = true;
+ }
if (!match && !scan_rsp_len)
return;
hdev->discovery.uuid_count,
hdev->discovery.uuids))
return;
+
+ /* If duplicate filtering does not report RSSI changes,
+ * then restart scanning to ensure updated result with
+ * updated RSSI values.
+ */
+ if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
+ &hdev->quirks))
+ restart_le_scan(hdev);
}
/* Append scan response data to event */
return;
}
+ /* Validate the reported RSSI value against the RSSI threshold once more
+ * incase HCI_QUIRK_STRICT_DUPLICATE_FILTER forced a restart of LE
+ * scanning.
+ */
+ if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+ rssi < hdev->discovery.rssi)
+ return;
+
ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
{
- DECLARE_WAITQUEUE(wait, current);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
struct sock *sk = sock->sk, *nsk;
long timeo;
int err = 0;
/* Wait for an incoming connection. (wake-one). */
add_wait_queue_exclusive(sk_sleep(sk), &wait);
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
if (sk->sk_state != BT_LISTEN) {
err = -EBADFD;
break;
}
release_sock(sk);
- timeo = schedule_timeout(timeo);
+
+ timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
+
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
}
- __set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
if (err)
static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
{
- DECLARE_WAITQUEUE(wait, current);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
struct sock *sk = sock->sk, *ch;
long timeo;
int err = 0;
/* Wait for an incoming connection. (wake-one). */
add_wait_queue_exclusive(sk_sleep(sk), &wait);
while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
if (sk->sk_state != BT_LISTEN) {
err = -EBADFD;
break;
}
release_sock(sk);
- timeo = schedule_timeout(timeo);
+
+ timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
lock_sock(sk);
}
- __set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
if (err)
oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
bdaddr_type);
- if (oob_data) {
+ if (oob_data && oob_data->present) {
set_bit(SMP_FLAG_OOB, &smp->flags);
oob_flag = SMP_OOB_PRESENT;
memcpy(smp->rr, oob_data->rand256, 16);
# Makefile for the Linux networking core.
#
-obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \
+obj-y := sock.o request_sock.o skbuff.o datagram.o stream.o scm.o \
gen_stats.o gen_estimator.o net_namespace.o secure_seq.o flow_dissector.o
obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
}
EXPORT_SYMBOL(netdev_upper_dev_unlink);
+/**
+ * netdev_bonding_info_change - Dispatch event about slave change
+ * @dev: device
+ * @netdev_bonding_info: info to dispatch
+ *
+ * Send NETDEV_BONDING_INFO to netdev notifiers with info.
+ * The caller must hold the RTNL lock.
+ */
+void netdev_bonding_info_change(struct net_device *dev,
+ struct netdev_bonding_info *bonding_info)
+{
+ struct netdev_notifier_bonding_info info;
+
+ memcpy(&info.bonding_info, bonding_info,
+ sizeof(struct netdev_bonding_info));
+ call_netdevice_notifiers_info(NETDEV_BONDING_INFO, dev,
+ &info.info);
+}
+EXPORT_SYMBOL(netdev_bonding_info_change);
+
void netdev_adjacent_add_links(struct net_device *dev)
{
struct netdev_adjacent *iter;
dev_maps = rcu_dereference(dev->xps_maps);
if (dev_maps) {
map = rcu_dereference(
- dev_maps->cpu_map[raw_smp_processor_id()]);
+ dev_maps->cpu_map[skb->sender_cpu - 1]);
if (map) {
if (map->len == 1)
queue_index = map->queues[0];
{
int queue_index = 0;
+#ifdef CONFIG_XPS
+ if (skb->sender_cpu == 0)
+ skb->sender_cpu = raw_smp_processor_id() + 1;
+#endif
+
if (dev->real_num_tx_queues != 1) {
const struct net_device_ops *ops = dev->netdev_ops;
if (ops->ndo_select_queue)
+++ /dev/null
-/*
- * iovec manipulation routines.
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Fixes:
- * Andrew Lunn : Errors in iovec copying.
- * Pedro Roque : Added memcpy_fromiovecend and
- * csum_..._fromiovecend.
- * Andi Kleen : fixed error handling for 2.1
- * Alexey Kuznetsov: 2.1 optimisations
- * Andi Kleen : Fix csum*fromiovecend for IPv6.
- */
-
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/net.h>
-#include <linux/in6.h>
-#include <asm/uaccess.h>
-#include <asm/byteorder.h>
-#include <net/checksum.h>
-#include <net/sock.h>
-
-/*
- * And now for the all-in-one: copy and checksum from a user iovec
- * directly to a datagram
- * Calls to csum_partial but the last must be in 32 bit chunks
- *
- * ip_build_xmit must ensure that when fragmenting only the last
- * call to this function will be unaligned also.
- */
-int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
- int offset, unsigned int len, __wsum *csump)
-{
- __wsum csum = *csump;
- int partial_cnt = 0, err = 0;
-
- /* Skip over the finished iovecs */
- while (offset >= iov->iov_len) {
- offset -= iov->iov_len;
- iov++;
- }
-
- while (len > 0) {
- u8 __user *base = iov->iov_base + offset;
- int copy = min_t(unsigned int, len, iov->iov_len - offset);
-
- offset = 0;
-
- /* There is a remnant from previous iov. */
- if (partial_cnt) {
- int par_len = 4 - partial_cnt;
-
- /* iov component is too short ... */
- if (par_len > copy) {
- if (copy_from_user(kdata, base, copy))
- goto out_fault;
- kdata += copy;
- base += copy;
- partial_cnt += copy;
- len -= copy;
- iov++;
- if (len)
- continue;
- *csump = csum_partial(kdata - partial_cnt,
- partial_cnt, csum);
- goto out;
- }
- if (copy_from_user(kdata, base, par_len))
- goto out_fault;
- csum = csum_partial(kdata - partial_cnt, 4, csum);
- kdata += par_len;
- base += par_len;
- copy -= par_len;
- len -= par_len;
- partial_cnt = 0;
- }
-
- if (len > copy) {
- partial_cnt = copy % 4;
- if (partial_cnt) {
- copy -= partial_cnt;
- if (copy_from_user(kdata + copy, base + copy,
- partial_cnt))
- goto out_fault;
- }
- }
-
- if (copy) {
- csum = csum_and_copy_from_user(base, kdata, copy,
- csum, &err);
- if (err)
- goto out;
- }
- len -= copy + partial_cnt;
- kdata += copy + partial_cnt;
- iov++;
- }
- *csump = csum;
-out:
- return err;
-
-out_fault:
- err = -EFAULT;
- goto out;
-}
-EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
-
-unsigned long iov_pages(const struct iovec *iov, int offset,
- unsigned long nr_segs)
-{
- unsigned long seg, base;
- int pages = 0, len, size;
-
- while (nr_segs && (offset >= iov->iov_len)) {
- offset -= iov->iov_len;
- ++iov;
- --nr_segs;
- }
-
- for (seg = 0; seg < nr_segs; seg++) {
- base = (unsigned long)iov[seg].iov_base + offset;
- len = iov[seg].iov_len - offset;
- size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
- pages += size;
- offset = 0;
- }
-
- return pages;
-}
-EXPORT_SYMBOL(iov_pages);
return ERR_PTR(-EINVAL);
}
#endif
+EXPORT_SYMBOL_GPL(get_net_ns_by_fd);
struct net *get_net_ns_by_pid(pid_t pid)
{
case NETDEV_UNREGISTER_FINAL:
case NETDEV_RELEASE:
case NETDEV_JOIN:
+ case NETDEV_BONDING_INFO:
break;
default:
rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL);
#ifdef CONFIG_NET_RX_BUSY_POLL
CHECK_SKB_FIELD(napi_id);
#endif
+#ifdef CONFIG_XPS
+ CHECK_SKB_FIELD(sender_cpu);
+#endif
#ifdef CONFIG_NET_SCHED
CHECK_SKB_FIELD(tc_index);
#ifdef CONFIG_NET_CLS_ACT
skb->ignore_df = 0;
skb_dst_drop(skb);
skb->mark = 0;
+ skb->sender_cpu = 0;
skb_init_secmark(skb);
secpath_reset(skb);
nf_reset(skb);
size_t start = ntohs(pd[0]);
size_t offset = ntohs(pd[1]);
size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
- __wsum delta;
if (skb->remcsum_offload) {
/* Already processed in GRO path */
return NULL;
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
- if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
- __skb_checksum_complete(skb);
-
- delta = remcsum_adjust((void *)guehdr + hdrlen,
- skb->csum, start, offset);
-
- /* Adjust skb->csum since we changed the packet */
- skb->csum = csum_add(skb->csum, delta);
+ skb_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset);
return guehdr;
}
size_t start = ntohs(pd[0]);
size_t offset = ntohs(pd[1]);
size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
- __wsum delta;
if (skb->remcsum_offload)
return guehdr;
return NULL;
}
- delta = remcsum_adjust((void *)guehdr + hdrlen,
- NAPI_GRO_CB(skb)->csum, start, offset);
-
- /* Adjust skb->csum since we changed the packet */
- skb->csum = csum_add(skb->csum, delta);
- NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
+ skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset);
skb->remcsum_offload = 1;
struct msghdr *msg = from;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- /* XXX: stripping const */
- if (memcpy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len) < 0)
+ if (copy_from_iter(to, len, &msg->msg_iter) != len)
return -EFAULT;
} else {
__wsum csum = 0;
- /* XXX: stripping const */
- if (csum_partial_copy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len, &csum) < 0)
+ if (csum_and_copy_from_iter(to, len, &csum, &msg->msg_iter) != len)
return -EFAULT;
skb->csum = csum_block_add(skb->csum, csum, odd);
}
struct pingfakehdr *pfh = (struct pingfakehdr *)from;
if (offset == 0) {
- if (fraglen < sizeof(struct icmphdr))
+ fraglen -= sizeof(struct icmphdr);
+ if (fraglen < 0)
BUG();
- if (csum_partial_copy_fromiovecend(to + sizeof(struct icmphdr),
- pfh->iov, 0, fraglen - sizeof(struct icmphdr),
- &pfh->wcheck))
+ if (csum_and_copy_from_iter(to + sizeof(struct icmphdr),
+ fraglen, &pfh->wcheck,
+ &pfh->msg->msg_iter) != fraglen)
return -EFAULT;
} else if (offset < sizeof(struct icmphdr)) {
BUG();
} else {
- if (csum_partial_copy_fromiovecend
- (to, pfh->iov, offset - sizeof(struct icmphdr),
- fraglen, &pfh->wcheck))
+ if (csum_and_copy_from_iter(to, fraglen, &pfh->wcheck,
+ &pfh->msg->msg_iter) != fraglen)
return -EFAULT;
}
pfh.icmph.checksum = 0;
pfh.icmph.un.echo.id = inet->inet_sport;
pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence;
- /* XXX: stripping const */
- pfh.iov = (struct iovec *)msg->msg_iter.iov;
+ pfh.msg = msg;
pfh.wcheck = 0;
pfh.family = AF_INET;
}
static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
- void *from, size_t length,
+ struct msghdr *msg, size_t length,
struct rtable **rtp,
unsigned int flags)
{
skb->transport_header = skb->network_header;
err = -EFAULT;
- if (memcpy_fromiovecend((void *)iph, from, 0, length))
+ if (memcpy_from_msg(iph, msg, length))
goto error_free;
iphlen = iph->ihl * 4;
back_from_confirm:
if (inet->hdrincl)
- /* XXX: stripping const */
- err = raw_send_hdrinc(sk, &fl4, (struct iovec *)msg->msg_iter.iov, len,
+ err = raw_send_hdrinc(sk, &fl4, msg, len,
&rt, msg->msg_flags);
else {
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{
- const struct iovec *iov;
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
- int iovlen, flags, err, copied = 0;
- int mss_now = 0, size_goal, copied_syn = 0, offset = 0;
+ int flags, err, copied = 0;
+ int mss_now = 0, size_goal, copied_syn = 0;
bool sg;
long timeo;
goto out;
else if (err)
goto out_err;
- offset = copied_syn;
}
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
mss_now = tcp_send_mss(sk, &size_goal, flags);
/* Ok commence sending. */
- iovlen = msg->msg_iter.nr_segs;
- iov = msg->msg_iter.iov;
copied = 0;
err = -EPIPE;
sg = !!(sk->sk_route_caps & NETIF_F_SG);
- while (--iovlen >= 0) {
- size_t seglen = iov->iov_len;
- unsigned char __user *from = iov->iov_base;
+ while (iov_iter_count(&msg->msg_iter)) {
+ int copy = 0;
+ int max = size_goal;
- iov++;
- if (unlikely(offset > 0)) { /* Skip bytes copied in SYN */
- if (offset >= seglen) {
- offset -= seglen;
- continue;
- }
- seglen -= offset;
- from += offset;
- offset = 0;
+ skb = tcp_write_queue_tail(sk);
+ if (tcp_send_head(sk)) {
+ if (skb->ip_summed == CHECKSUM_NONE)
+ max = mss_now;
+ copy = max - skb->len;
}
- while (seglen > 0) {
- int copy = 0;
- int max = size_goal;
-
- skb = tcp_write_queue_tail(sk);
- if (tcp_send_head(sk)) {
- if (skb->ip_summed == CHECKSUM_NONE)
- max = mss_now;
- copy = max - skb->len;
- }
-
- if (copy <= 0) {
+ if (copy <= 0) {
new_segment:
- /* Allocate new segment. If the interface is SG,
- * allocate skb fitting to single page.
- */
- if (!sk_stream_memory_free(sk))
- goto wait_for_sndbuf;
+ /* Allocate new segment. If the interface is SG,
+ * allocate skb fitting to single page.
+ */
+ if (!sk_stream_memory_free(sk))
+ goto wait_for_sndbuf;
- skb = sk_stream_alloc_skb(sk,
- select_size(sk, sg),
- sk->sk_allocation);
- if (!skb)
- goto wait_for_memory;
+ skb = sk_stream_alloc_skb(sk,
+ select_size(sk, sg),
+ sk->sk_allocation);
+ if (!skb)
+ goto wait_for_memory;
- /*
- * Check whether we can use HW checksum.
- */
- if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
- skb->ip_summed = CHECKSUM_PARTIAL;
+ /*
+ * Check whether we can use HW checksum.
+ */
+ if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
+ skb->ip_summed = CHECKSUM_PARTIAL;
- skb_entail(sk, skb);
- copy = size_goal;
- max = size_goal;
+ skb_entail(sk, skb);
+ copy = size_goal;
+ max = size_goal;
- /* All packets are restored as if they have
- * already been sent. skb_mstamp isn't set to
- * avoid wrong rtt estimation.
- */
- if (tp->repair)
- TCP_SKB_CB(skb)->sacked |= TCPCB_REPAIRED;
- }
+ /* All packets are restored as if they have
+ * already been sent. skb_mstamp isn't set to
+ * avoid wrong rtt estimation.
+ */
+ if (tp->repair)
+ TCP_SKB_CB(skb)->sacked |= TCPCB_REPAIRED;
+ }
- /* Try to append data to the end of skb. */
- if (copy > seglen)
- copy = seglen;
-
- /* Where to copy to? */
- if (skb_availroom(skb) > 0) {
- /* We have some space in skb head. Superb! */
- copy = min_t(int, copy, skb_availroom(skb));
- err = skb_add_data_nocache(sk, skb, from, copy);
- if (err)
- goto do_fault;
- } else {
- bool merge = true;
- int i = skb_shinfo(skb)->nr_frags;
- struct page_frag *pfrag = sk_page_frag(sk);
-
- if (!sk_page_frag_refill(sk, pfrag))
- goto wait_for_memory;
-
- if (!skb_can_coalesce(skb, i, pfrag->page,
- pfrag->offset)) {
- if (i == MAX_SKB_FRAGS || !sg) {
- tcp_mark_push(tp, skb);
- goto new_segment;
- }
- merge = false;
- }
+ /* Try to append data to the end of skb. */
+ if (copy > iov_iter_count(&msg->msg_iter))
+ copy = iov_iter_count(&msg->msg_iter);
+
+ /* Where to copy to? */
+ if (skb_availroom(skb) > 0) {
+ /* We have some space in skb head. Superb! */
+ copy = min_t(int, copy, skb_availroom(skb));
+ err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy);
+ if (err)
+ goto do_fault;
+ } else {
+ bool merge = true;
+ int i = skb_shinfo(skb)->nr_frags;
+ struct page_frag *pfrag = sk_page_frag(sk);
+
+ if (!sk_page_frag_refill(sk, pfrag))
+ goto wait_for_memory;
- copy = min_t(int, copy, pfrag->size - pfrag->offset);
-
- if (!sk_wmem_schedule(sk, copy))
- goto wait_for_memory;
-
- err = skb_copy_to_page_nocache(sk, from, skb,
- pfrag->page,
- pfrag->offset,
- copy);
- if (err)
- goto do_error;
-
- /* Update the skb. */
- if (merge) {
- skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
- } else {
- skb_fill_page_desc(skb, i, pfrag->page,
- pfrag->offset, copy);
- get_page(pfrag->page);
+ if (!skb_can_coalesce(skb, i, pfrag->page,
+ pfrag->offset)) {
+ if (i == MAX_SKB_FRAGS || !sg) {
+ tcp_mark_push(tp, skb);
+ goto new_segment;
}
- pfrag->offset += copy;
+ merge = false;
}
- if (!copied)
- TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;
+ copy = min_t(int, copy, pfrag->size - pfrag->offset);
- tp->write_seq += copy;
- TCP_SKB_CB(skb)->end_seq += copy;
- tcp_skb_pcount_set(skb, 0);
+ if (!sk_wmem_schedule(sk, copy))
+ goto wait_for_memory;
- from += copy;
- copied += copy;
- if ((seglen -= copy) == 0 && iovlen == 0) {
- tcp_tx_timestamp(sk, skb);
- goto out;
+ err = skb_copy_to_page_nocache(sk, &msg->msg_iter, skb,
+ pfrag->page,
+ pfrag->offset,
+ copy);
+ if (err)
+ goto do_error;
+
+ /* Update the skb. */
+ if (merge) {
+ skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
+ } else {
+ skb_fill_page_desc(skb, i, pfrag->page,
+ pfrag->offset, copy);
+ get_page(pfrag->page);
}
+ pfrag->offset += copy;
+ }
- if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
- continue;
+ if (!copied)
+ TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;
+
+ tp->write_seq += copy;
+ TCP_SKB_CB(skb)->end_seq += copy;
+ tcp_skb_pcount_set(skb, 0);
+
+ copied += copy;
+ if (!iov_iter_count(&msg->msg_iter)) {
+ tcp_tx_timestamp(sk, skb);
+ goto out;
+ }
- if (forced_push(tp)) {
- tcp_mark_push(tp, skb);
- __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
- } else if (skb == tcp_send_head(sk))
- tcp_push_one(sk, mss_now);
+ if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
continue;
+ if (forced_push(tp)) {
+ tcp_mark_push(tp, skb);
+ __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
+ } else if (skb == tcp_send_head(sk))
+ tcp_push_one(sk, mss_now);
+ continue;
+
wait_for_sndbuf:
- set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
- if (copied)
- tcp_push(sk, flags & ~MSG_MORE, mss_now,
- TCP_NAGLE_PUSH, size_goal);
+ if (copied)
+ tcp_push(sk, flags & ~MSG_MORE, mss_now,
+ TCP_NAGLE_PUSH, size_goal);
- if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
- goto do_error;
+ if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
+ goto do_error;
- mss_now = tcp_send_mss(sk, &size_goal, flags);
- }
+ mss_now = tcp_send_mss(sk, &size_goal, flags);
}
out:
skb_orphan(skb);
skb->sk = sk;
- skb->destructor = tcp_wfree;
+ skb->destructor = skb_is_tcp_pure_ack(skb) ? sock_wfree : tcp_wfree;
skb_set_hash_from_sk(skb, sk);
atomic_add(skb->truesize, &sk->sk_wmem_alloc);
{
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_fastopen_request *fo = tp->fastopen_req;
- int syn_loss = 0, space, err = 0;
+ int syn_loss = 0, space, err = 0, copied;
unsigned long last_syn_loss = 0;
struct sk_buff *syn_data;
goto fallback;
syn_data->ip_summed = CHECKSUM_PARTIAL;
memcpy(syn_data->cb, syn->cb, sizeof(syn->cb));
- if (unlikely(memcpy_fromiovecend(skb_put(syn_data, space),
- fo->data->msg_iter.iov, 0, space))) {
+ copied = copy_from_iter(skb_put(syn_data, space), space,
+ &fo->data->msg_iter);
+ if (unlikely(!copied)) {
kfree_skb(syn_data);
goto fallback;
}
+ if (copied != space) {
+ skb_trim(syn_data, copied);
+ space = copied;
+ }
/* No more data pending in inet_wait_for_connect() */
if (space == fo->size)
skb_reserve(buff, MAX_TCP_HEADER);
tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
+ /* We do not want pure acks influencing TCP Small Queues or fq/pacing
+ * too much.
+ * SKB_TRUESIZE(max(1 .. 66, MAX_TCP_HEADER)) is unfortunately ~784
+ * We also avoid tcp_wfree() overhead (cache line miss accessing
+ * tp->tsq_flags) by using regular sock_wfree()
+ */
+ skb_set_tcp_pure_ack(buff);
+
/* Send it off, this clears delayed acks for us. */
skb_mstamp_get(&buff->skb_mstamp);
tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
pfh.icmph.checksum = 0;
pfh.icmph.un.echo.id = inet->inet_sport;
pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence;
- /* XXX: stripping const */
- pfh.iov = (struct iovec *)msg->msg_iter.iov;
+ pfh.msg = msg;
pfh.wcheck = 0;
pfh.family = AF_INET6;
return err;
}
-static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
+static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
struct flowi6 *fl6, struct dst_entry **dstp,
unsigned int flags)
{
skb->ip_summed = CHECKSUM_NONE;
skb->transport_header = skb->network_header;
- err = memcpy_fromiovecend((void *)iph, from, 0, length);
+ err = memcpy_from_msg(iph, msg, length);
if (err)
goto error_fault;
back_from_confirm:
if (inet->hdrincl)
- /* XXX: stripping const */
- err = rawv6_send_hdrinc(sk, (struct iovec *)msg->msg_iter.iov, len, &fl6, &dst, msg->msg_flags);
+ err = rawv6_send_hdrinc(sk, msg, len, &fl6, &dst, msg->msg_flags);
else {
lock_sock(sk);
err = ip6_append_data(sk, raw6_getfrag, &rfv,
select CRYPTO_ARC4
select CRYPTO_AES
select CRYPTO_CCM
+ select CRYPTO_GCM
select CRC32
select AVERAGE
---help---
michael.o \
tkip.o \
aes_ccm.o \
+ aes_gcm.o \
aes_cmac.o \
+ aes_gmac.o \
cfg.o \
ethtool.o \
rx.o \
#include "aes_ccm.h"
void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic)
+ u8 *data, size_t data_len, u8 *mic,
+ size_t mic_len)
{
struct scatterlist assoc, pt, ct[2];
sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
sg_init_table(ct, 2);
sg_set_buf(&ct[0], data, data_len);
- sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+ sg_set_buf(&ct[1], mic, mic_len);
aead_request_set_tfm(aead_req, tfm);
aead_request_set_assoc(aead_req, &assoc, assoc.length);
}
int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic)
+ u8 *data, size_t data_len, u8 *mic,
+ size_t mic_len)
{
struct scatterlist assoc, pt, ct[2];
char aead_req_data[sizeof(struct aead_request) +
sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
sg_init_table(ct, 2);
sg_set_buf(&ct[0], data, data_len);
- sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+ sg_set_buf(&ct[1], mic, mic_len);
aead_request_set_tfm(aead_req, tfm);
aead_request_set_assoc(aead_req, &assoc, assoc.length);
- aead_request_set_crypt(aead_req, ct, &pt,
- data_len + IEEE80211_CCMP_MIC_LEN, b_0);
+ aead_request_set_crypt(aead_req, ct, &pt, data_len + mic_len, b_0);
return crypto_aead_decrypt(aead_req);
}
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
+ size_t key_len,
+ size_t mic_len)
{
struct crypto_aead *tfm;
int err;
if (IS_ERR(tfm))
return tfm;
- err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+ err = crypto_aead_setkey(tfm, key, key_len);
if (!err)
- err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
+ err = crypto_aead_setauthsize(tfm, mic_len);
if (!err)
return tfm;
#include <linux/crypto.h>
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
+ size_t key_len,
+ size_t mic_len);
void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic);
+ u8 *data, size_t data_len, u8 *mic,
+ size_t mic_len);
int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
- u8 *data, size_t data_len, u8 *mic);
+ u8 *data, size_t data_len, u8 *mic,
+ size_t mic_len);
void ieee80211_aes_key_free(struct crypto_aead *tfm);
#endif /* AES_CCM_H */
#include "key.h"
#include "aes_cmac.h"
-#define AES_CMAC_KEY_LEN 16
#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */
+#define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */
#define AAD_LEN 20
pad[AES_BLOCK_SIZE - 1] ^= 0x87;
}
-
-static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
- const u8 *addr[], const size_t *len, u8 *mac)
+static void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac,
+ size_t mac_len)
{
u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
const u8 *pos, *end;
for (i = 0; i < AES_BLOCK_SIZE; i++)
pad[i] ^= cbc[i];
crypto_cipher_encrypt_one(tfm, pad, pad);
- memcpy(mac, pad, CMAC_TLEN);
+ memcpy(mac, pad, mac_len);
}
addr[2] = zero;
len[2] = CMAC_TLEN;
- aes_128_cmac_vector(tfm, 3, addr, len, mic);
+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN);
}
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
+ const u8 *data, size_t data_len, u8 *mic)
+{
+ const u8 *addr[3];
+ size_t len[3];
+ u8 zero[CMAC_TLEN_256];
+
+ memset(zero, 0, CMAC_TLEN_256);
+ addr[0] = aad;
+ len[0] = AAD_LEN;
+ addr[1] = data;
+ len[1] = data_len - CMAC_TLEN_256;
+ addr[2] = zero;
+ len[2] = CMAC_TLEN_256;
+
+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256);
+}
-struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[])
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
+ size_t key_len)
{
struct crypto_cipher *tfm;
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
if (!IS_ERR(tfm))
- crypto_cipher_setkey(tfm, key, AES_CMAC_KEY_LEN);
+ crypto_cipher_setkey(tfm, key, key_len);
return tfm;
}
#include <linux/crypto.h>
-struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[]);
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
+ size_t key_len);
void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
const u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
+ const u8 *data, size_t data_len, u8 *mic);
void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
#endif /* AES_CMAC_H */
--- /dev/null
+/*
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <crypto/aes.h>
+
+#include <net/mac80211.h>
+#include "key.h"
+#include "aes_gcm.h"
+
+void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ struct scatterlist assoc, pt, ct[2];
+
+ char aead_req_data[sizeof(struct aead_request) +
+ crypto_aead_reqsize(tfm)]
+ __aligned(__alignof__(struct aead_request));
+ struct aead_request *aead_req = (void *)aead_req_data;
+
+ memset(aead_req, 0, sizeof(aead_req_data));
+
+ sg_init_one(&pt, data, data_len);
+ sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+ sg_init_table(ct, 2);
+ sg_set_buf(&ct[0], data, data_len);
+ sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+
+ aead_request_set_tfm(aead_req, tfm);
+ aead_request_set_assoc(aead_req, &assoc, assoc.length);
+ aead_request_set_crypt(aead_req, &pt, ct, data_len, j_0);
+
+ crypto_aead_encrypt(aead_req);
+}
+
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ struct scatterlist assoc, pt, ct[2];
+ char aead_req_data[sizeof(struct aead_request) +
+ crypto_aead_reqsize(tfm)]
+ __aligned(__alignof__(struct aead_request));
+ struct aead_request *aead_req = (void *)aead_req_data;
+
+ if (data_len == 0)
+ return -EINVAL;
+
+ memset(aead_req, 0, sizeof(aead_req_data));
+
+ sg_init_one(&pt, data, data_len);
+ sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+ sg_init_table(ct, 2);
+ sg_set_buf(&ct[0], data, data_len);
+ sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+
+ aead_request_set_tfm(aead_req, tfm);
+ aead_request_set_assoc(aead_req, &assoc, assoc.length);
+ aead_request_set_crypt(aead_req, ct, &pt,
+ data_len + IEEE80211_GCMP_MIC_LEN, j_0);
+
+ return crypto_aead_decrypt(aead_req);
+}
+
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
+ size_t key_len)
+{
+ struct crypto_aead *tfm;
+ int err;
+
+ tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm))
+ return tfm;
+
+ err = crypto_aead_setkey(tfm, key, key_len);
+ if (!err)
+ err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
+ if (!err)
+ return tfm;
+
+ crypto_free_aead(tfm);
+ return ERR_PTR(err);
+}
+
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
+{
+ crypto_free_aead(tfm);
+}
--- /dev/null
+/*
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AES_GCM_H
+#define AES_GCM_H
+
+#include <linux/crypto.h>
+
+void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+ u8 *data, size_t data_len, u8 *mic);
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
+ size_t key_len);
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm);
+
+#endif /* AES_GCM_H */
--- /dev/null
+/*
+ * AES-GMAC for IEEE 802.11 BIP-GMAC-128 and BIP-GMAC-256
+ * Copyright 2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <crypto/aes.h>
+
+#include <net/mac80211.h>
+#include "key.h"
+#include "aes_gmac.h"
+
+#define GMAC_MIC_LEN 16
+#define GMAC_NONCE_LEN 12
+#define AAD_LEN 20
+
+int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+ const u8 *data, size_t data_len, u8 *mic)
+{
+ struct scatterlist sg[3], ct[1];
+ char aead_req_data[sizeof(struct aead_request) +
+ crypto_aead_reqsize(tfm)]
+ __aligned(__alignof__(struct aead_request));
+ struct aead_request *aead_req = (void *)aead_req_data;
+ u8 zero[GMAC_MIC_LEN], iv[AES_BLOCK_SIZE];
+
+ if (data_len < GMAC_MIC_LEN)
+ return -EINVAL;
+
+ memset(aead_req, 0, sizeof(aead_req_data));
+
+ memset(zero, 0, GMAC_MIC_LEN);
+ sg_init_table(sg, 3);
+ sg_set_buf(&sg[0], aad, AAD_LEN);
+ sg_set_buf(&sg[1], data, data_len - GMAC_MIC_LEN);
+ sg_set_buf(&sg[2], zero, GMAC_MIC_LEN);
+
+ memcpy(iv, nonce, GMAC_NONCE_LEN);
+ memset(iv + GMAC_NONCE_LEN, 0, sizeof(iv) - GMAC_NONCE_LEN);
+ iv[AES_BLOCK_SIZE - 1] = 0x01;
+
+ sg_init_table(ct, 1);
+ sg_set_buf(&ct[0], mic, GMAC_MIC_LEN);
+
+ aead_request_set_tfm(aead_req, tfm);
+ aead_request_set_assoc(aead_req, sg, AAD_LEN + data_len);
+ aead_request_set_crypt(aead_req, NULL, ct, 0, iv);
+
+ crypto_aead_encrypt(aead_req);
+
+ return 0;
+}
+
+struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
+ size_t key_len)
+{
+ struct crypto_aead *tfm;
+ int err;
+
+ tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm))
+ return tfm;
+
+ err = crypto_aead_setkey(tfm, key, key_len);
+ if (!err)
+ return tfm;
+ if (!err)
+ err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN);
+
+ crypto_free_aead(tfm);
+ return ERR_PTR(err);
+}
+
+void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
+{
+ crypto_free_aead(tfm);
+}
--- /dev/null
+/*
+ * Copyright 2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AES_GMAC_H
+#define AES_GMAC_H
+
+#include <linux/crypto.h>
+
+struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
+ size_t key_len);
+int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+ const u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
+
+#endif /* AES_GMAC_H */
return -EINVAL;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
break;
default:
cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
params.seq_len = 6;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
pn64 = atomic64_read(&key->u.ccmp.tx_pn);
seq[0] = pn64;
seq[1] = pn64 >> 8;
params.seq_len = 6;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
seq[0] = pn64;
seq[1] = pn64 >> 8;
params.seq = seq;
params.seq_len = 6;
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
+ seq[0] = pn64;
+ seq[1] = pn64 >> 8;
+ seq[2] = pn64 >> 16;
+ seq[3] = pn64 >> 24;
+ seq[4] = pn64 >> 32;
+ seq[5] = pn64 >> 40;
+ params.seq = seq;
+ params.seq_len = 6;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+ seq[0] = pn64;
+ seq[1] = pn64 >> 8;
+ seq[2] = pn64 >> 16;
+ seq[3] = pn64 >> 24;
+ seq[4] = pn64 >> 32;
+ seq[5] = pn64 >> 40;
+ params.seq = seq;
+ params.seq_len = 6;
+ break;
}
params.key = key->conf.key;
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata;
+ enum nl80211_tx_power_setting txp_type = type;
+ bool update_txp_type = false;
if (wdev) {
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ txp_type = NL80211_TX_POWER_LIMITED;
break;
case NL80211_TX_POWER_LIMITED:
case NL80211_TX_POWER_FIXED:
break;
}
- ieee80211_recalc_txpower(sdata);
+ if (txp_type != sdata->vif.bss_conf.txpower_type) {
+ update_txp_type = true;
+ sdata->vif.bss_conf.txpower_type = txp_type;
+ }
+
+ ieee80211_recalc_txpower(sdata, update_txp_type);
return 0;
}
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ txp_type = NL80211_TX_POWER_LIMITED;
break;
case NL80211_TX_POWER_LIMITED:
case NL80211_TX_POWER_FIXED:
}
mutex_lock(&local->iflist_mtx);
- list_for_each_entry(sdata, &local->interfaces, list)
+ list_for_each_entry(sdata, &local->interfaces, list) {
sdata->user_power_level = local->user_power_level;
+ if (txp_type != sdata->vif.bss_conf.txpower_type)
+ update_txp_type = true;
+ sdata->vif.bss_conf.txpower_type = txp_type;
+ }
list_for_each_entry(sdata, &local->interfaces, list)
- ieee80211_recalc_txpower(sdata);
+ ieee80211_recalc_txpower(sdata, update_txp_type);
mutex_unlock(&local->iflist_mtx);
return 0;
}
if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
- ieee80211_recalc_txpower(sdata);
+ ieee80211_recalc_txpower(sdata, false);
ieee80211_recalc_chanctx_min_def(local, new_ctx);
}
ieee80211_bss_info_change_notify(sdata,
changed);
- ieee80211_recalc_txpower(sdata);
+ ieee80211_recalc_txpower(sdata, false);
}
ieee80211_recalc_chanctx_chantype(local, ctx);
key->u.tkip.tx.iv16);
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
pn = atomic64_read(&key->u.ccmp.tx_pn);
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
pn = atomic64_read(&key->u.aes_cmac.tx_pn);
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ pn = atomic64_read(&key->u.aes_gmac.tx_pn);
+ len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
+ (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+ (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ pn = atomic64_read(&key->u.gcmp.tx_pn);
+ len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
+ (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+ (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
+ break;
default:
return 0;
}
len = p - buf;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
rpn = key->u.ccmp.rx_pn[i];
p += scnprintf(p, sizeof(buf)+buf-p,
len = p - buf;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
rpn = key->u.aes_cmac.rx_pn;
p += scnprintf(p, sizeof(buf)+buf-p,
"%02x%02x%02x%02x%02x%02x\n",
rpn[3], rpn[4], rpn[5]);
len = p - buf;
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ rpn = key->u.aes_gmac.rx_pn;
+ p += scnprintf(p, sizeof(buf)+buf-p,
+ "%02x%02x%02x%02x%02x%02x\n",
+ rpn[0], rpn[1], rpn[2],
+ rpn[3], rpn[4], rpn[5]);
+ len = p - buf;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
+ rpn = key->u.gcmp.rx_pn[i];
+ p += scnprintf(p, sizeof(buf)+buf-p,
+ "%02x%02x%02x%02x%02x%02x\n",
+ rpn[0], rpn[1], rpn[2],
+ rpn[3], rpn[4], rpn[5]);
+ }
+ len = p - buf;
+ break;
default:
return 0;
}
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
len = scnprintf(buf, sizeof(buf), "%u\n",
key->u.aes_cmac.replays);
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ len = scnprintf(buf, sizeof(buf), "%u\n",
+ key->u.aes_gmac.replays);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ len = scnprintf(buf, sizeof(buf), "%u\n", key->u.gcmp.replays);
+ break;
default:
return 0;
}
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
len = scnprintf(buf, sizeof(buf), "%u\n",
key->u.aes_cmac.icverrors);
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ len = scnprintf(buf, sizeof(buf), "%u\n",
+ key->u.aes_gmac.icverrors);
+ break;
default:
return 0;
}
void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+ bool update_bss);
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
{
struct ieee80211_local *local = hw_to_local(hw);
- WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+ WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+ !test_bit(SCAN_COMPLETED, &local->scanning),
"%s: resume with hardware scan still in progress\n",
wiphy_name(hw->wiphy));
struct ieee80211_sub_if_data *sdata,
unsigned int queues, bool drop);
+static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
+{
+ /*
+ * If quiescing is set, we are racing with __ieee80211_suspend.
+ * __ieee80211_suspend flushes the workers after setting quiescing,
+ * and we check quiescing / suspended before enqueing new workers.
+ * We should abort the worker to avoid the races below.
+ */
+ if (local->quiescing)
+ return false;
+
+ /*
+ * We might already be suspended if the following scenario occurs:
+ * __ieee80211_suspend Control path
+ *
+ * if (local->quiescing)
+ * return;
+ * local->quiescing = true;
+ * flush_workqueue();
+ * queue_work(...);
+ * local->suspended = true;
+ * local->quiescing = false;
+ * worker starts running...
+ */
+ if (local->suspended)
+ return false;
+
+ return true;
+}
+
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *bssid,
return false;
}
-void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+ bool update_bss)
{
- if (__ieee80211_recalc_txpower(sdata))
+ if (__ieee80211_recalc_txpower(sdata) || update_bss)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
}
if (local->scanning)
return;
- /*
- * ieee80211_queue_work() should have picked up most cases,
- * here we'll pick the rest.
- */
- if (WARN(local->suspended,
- "interface work scheduled while going to suspend\n"))
+ if (!ieee80211_can_run_worker(local))
return;
/* first process frames */
#include "debugfs_key.h"
#include "aes_ccm.h"
#include "aes_cmac.h"
+#include "aes_gmac.h"
+#include "aes_gcm.h"
/**
{
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- int ret;
+ int ret = -EOPNOTSUPP;
might_sleep();
return 0;
}
- if (ret != -ENOSPC && ret != -EOPNOTSUPP)
+ if (ret != -ENOSPC && ret != -EOPNOTSUPP && ret != 1)
sdata_err(sdata,
"failed to set key (%d, %pM) to hardware (%d)\n",
key->conf.keyidx,
case WLAN_CIPHER_SUITE_WEP104:
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_AES_CMAC:
- /* all of these we can do in software */
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ /* all of these we can do in software - if driver can */
+ if (ret == 1)
+ return 0;
+ if (key->local->hw.flags & IEEE80211_HW_SW_CRYPTO_CONTROL)
+ return -EINVAL;
return 0;
default:
return -EINVAL;
* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
*/
- key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
+ key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+ key_data, key_len, IEEE80211_CCMP_MIC_LEN);
+ if (IS_ERR(key->u.ccmp.tfm)) {
+ err = PTR_ERR(key->u.ccmp.tfm);
+ kfree(key);
+ return ERR_PTR(err);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ key->conf.iv_len = IEEE80211_CCMP_256_HDR_LEN;
+ key->conf.icv_len = IEEE80211_CCMP_256_MIC_LEN;
+ for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++)
+ for (j = 0; j < IEEE80211_CCMP_256_PN_LEN; j++)
+ key->u.ccmp.rx_pn[i][j] =
+ seq[IEEE80211_CCMP_256_PN_LEN - j - 1];
+ /* Initialize AES key state here as an optimization so that
+ * it does not need to be initialized for every packet.
+ */
+ key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+ key_data, key_len, IEEE80211_CCMP_256_MIC_LEN);
if (IS_ERR(key->u.ccmp.tfm)) {
err = PTR_ERR(key->u.ccmp.tfm);
kfree(key);
}
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
key->conf.iv_len = 0;
- key->conf.icv_len = sizeof(struct ieee80211_mmie);
+ if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ key->conf.icv_len = sizeof(struct ieee80211_mmie);
+ else
+ key->conf.icv_len = sizeof(struct ieee80211_mmie_16);
if (seq)
for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++)
key->u.aes_cmac.rx_pn[j] =
* it does not need to be initialized for every packet.
*/
key->u.aes_cmac.tfm =
- ieee80211_aes_cmac_key_setup(key_data);
+ ieee80211_aes_cmac_key_setup(key_data, key_len);
if (IS_ERR(key->u.aes_cmac.tfm)) {
err = PTR_ERR(key->u.aes_cmac.tfm);
kfree(key);
return ERR_PTR(err);
}
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ key->conf.iv_len = 0;
+ key->conf.icv_len = sizeof(struct ieee80211_mmie_16);
+ if (seq)
+ for (j = 0; j < IEEE80211_GMAC_PN_LEN; j++)
+ key->u.aes_gmac.rx_pn[j] =
+ seq[IEEE80211_GMAC_PN_LEN - j - 1];
+ /* Initialize AES key state here as an optimization so that
+ * it does not need to be initialized for every packet.
+ */
+ key->u.aes_gmac.tfm =
+ ieee80211_aes_gmac_key_setup(key_data, key_len);
+ if (IS_ERR(key->u.aes_gmac.tfm)) {
+ err = PTR_ERR(key->u.aes_gmac.tfm);
+ kfree(key);
+ return ERR_PTR(err);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ key->conf.iv_len = IEEE80211_GCMP_HDR_LEN;
+ key->conf.icv_len = IEEE80211_GCMP_MIC_LEN;
+ for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++)
+ for (j = 0; j < IEEE80211_GCMP_PN_LEN; j++)
+ key->u.gcmp.rx_pn[i][j] =
+ seq[IEEE80211_GCMP_PN_LEN - j - 1];
+ /* Initialize AES key state here as an optimization so that
+ * it does not need to be initialized for every packet.
+ */
+ key->u.gcmp.tfm = ieee80211_aes_gcm_key_setup_encrypt(key_data,
+ key_len);
+ if (IS_ERR(key->u.gcmp.tfm)) {
+ err = PTR_ERR(key->u.gcmp.tfm);
+ kfree(key);
+ return ERR_PTR(err);
+ }
+ break;
default:
if (cs) {
size_t len = (seq_len > MAX_PN_LEN) ?
static void ieee80211_key_free_common(struct ieee80211_key *key)
{
- if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
ieee80211_aes_key_free(key->u.ccmp.tfm);
- if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ ieee80211_aes_gcm_key_free(key->u.gcmp.tfm);
+ break;
+ }
kzfree(key);
}
seq->tkip.iv16 = key->u.tkip.tx.iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
pn64 = atomic64_read(&key->u.ccmp.tx_pn);
seq->ccmp.pn[5] = pn64;
seq->ccmp.pn[4] = pn64 >> 8;
seq->ccmp.pn[0] = pn64 >> 40;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
seq->ccmp.pn[5] = pn64;
seq->ccmp.pn[4] = pn64 >> 8;
seq->ccmp.pn[1] = pn64 >> 32;
seq->ccmp.pn[0] = pn64 >> 40;
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
+ seq->ccmp.pn[5] = pn64;
+ seq->ccmp.pn[4] = pn64 >> 8;
+ seq->ccmp.pn[3] = pn64 >> 16;
+ seq->ccmp.pn[2] = pn64 >> 24;
+ seq->ccmp.pn[1] = pn64 >> 32;
+ seq->ccmp.pn[0] = pn64 >> 40;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+ seq->gcmp.pn[5] = pn64;
+ seq->gcmp.pn[4] = pn64 >> 8;
+ seq->gcmp.pn[3] = pn64 >> 16;
+ seq->gcmp.pn[2] = pn64 >> 24;
+ seq->gcmp.pn[1] = pn64 >> 32;
+ seq->gcmp.pn[0] = pn64 >> 40;
+ break;
default:
WARN_ON(1);
}
seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
return;
if (tid < 0)
memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
if (WARN_ON(tid != 0))
return;
pn = key->u.aes_cmac.rx_pn;
memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN);
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (WARN_ON(tid != 0))
+ return;
+ pn = key->u.aes_gmac.rx_pn;
+ memcpy(seq->aes_gmac.pn, pn, IEEE80211_GMAC_PN_LEN);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+ return;
+ if (tid < 0)
+ pn = key->u.gcmp.rx_pn[IEEE80211_NUM_TIDS];
+ else
+ pn = key->u.gcmp.rx_pn[tid];
+ memcpy(seq->gcmp.pn, pn, IEEE80211_GCMP_PN_LEN);
+ break;
}
}
EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
key->u.tkip.tx.iv16 = seq->tkip.iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
pn64 = (u64)seq->ccmp.pn[5] |
((u64)seq->ccmp.pn[4] << 8) |
((u64)seq->ccmp.pn[3] << 16) |
atomic64_set(&key->u.ccmp.tx_pn, pn64);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
pn64 = (u64)seq->aes_cmac.pn[5] |
((u64)seq->aes_cmac.pn[4] << 8) |
((u64)seq->aes_cmac.pn[3] << 16) |
((u64)seq->aes_cmac.pn[0] << 40);
atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ pn64 = (u64)seq->aes_gmac.pn[5] |
+ ((u64)seq->aes_gmac.pn[4] << 8) |
+ ((u64)seq->aes_gmac.pn[3] << 16) |
+ ((u64)seq->aes_gmac.pn[2] << 24) |
+ ((u64)seq->aes_gmac.pn[1] << 32) |
+ ((u64)seq->aes_gmac.pn[0] << 40);
+ atomic64_set(&key->u.aes_gmac.tx_pn, pn64);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ pn64 = (u64)seq->gcmp.pn[5] |
+ ((u64)seq->gcmp.pn[4] << 8) |
+ ((u64)seq->gcmp.pn[3] << 16) |
+ ((u64)seq->gcmp.pn[2] << 24) |
+ ((u64)seq->gcmp.pn[1] << 32) |
+ ((u64)seq->gcmp.pn[0] << 40);
+ atomic64_set(&key->u.gcmp.tx_pn, pn64);
+ break;
default:
WARN_ON(1);
break;
key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
return;
if (tid < 0)
memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
if (WARN_ON(tid != 0))
return;
pn = key->u.aes_cmac.rx_pn;
memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (WARN_ON(tid != 0))
+ return;
+ pn = key->u.aes_gmac.rx_pn;
+ memcpy(pn, seq->aes_gmac.pn, IEEE80211_GMAC_PN_LEN);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+ return;
+ if (tid < 0)
+ pn = key->u.gcmp.rx_pn[IEEE80211_NUM_TIDS];
+ else
+ pn = key->u.gcmp.rx_pn[tid];
+ memcpy(pn, seq->gcmp.pn, IEEE80211_GCMP_PN_LEN);
+ break;
default:
WARN_ON(1);
break;
u32 replays; /* dot11RSNAStatsCMACReplays */
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
} aes_cmac;
+ struct {
+ atomic64_t tx_pn;
+ u8 rx_pn[IEEE80211_GMAC_PN_LEN];
+ struct crypto_aead *tfm;
+ u32 replays; /* dot11RSNAStatsCMACReplays */
+ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
+ } aes_gmac;
+ struct {
+ atomic64_t tx_pn;
+ /* Last received packet number. The first
+ * IEEE80211_NUM_TIDS counters are used with Data
+ * frames and the last counter is used with Robust
+ * Management frames.
+ */
+ u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_GCMP_PN_LEN];
+ struct crypto_aead *tfm;
+ u32 replays; /* dot11RSNAStatsGCMPReplays */
+ } gcmp;
struct {
/* generic cipher scheme */
u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN];
bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
IS_ERR(local->wep_rx_tfm));
bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
- const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
int n_suites = 0, r = 0, w = 0;
u32 *suites;
static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_CCMP_256,
+ WLAN_CIPHER_SUITE_GCMP,
+ WLAN_CIPHER_SUITE_GCMP_256,
/* keep last -- depends on hw flags! */
- WLAN_CIPHER_SUITE_AES_CMAC
+ WLAN_CIPHER_SUITE_AES_CMAC,
+ WLAN_CIPHER_SUITE_BIP_CMAC_256,
+ WLAN_CIPHER_SUITE_BIP_GMAC_128,
+ WLAN_CIPHER_SUITE_BIP_GMAC_256,
};
- /* Driver specifies the ciphers, we have nothing to do... */
- if (local->hw.wiphy->cipher_suites && have_wep)
- return 0;
+ if (local->hw.flags & IEEE80211_HW_SW_CRYPTO_CONTROL ||
+ local->hw.wiphy->cipher_suites) {
+ /* If the driver advertises, or doesn't support SW crypto,
+ * we only need to remove WEP if necessary.
+ */
+ if (have_wep)
+ return 0;
+
+ /* well if it has _no_ ciphers ... fine */
+ if (!local->hw.wiphy->n_cipher_suites)
+ return 0;
+
+ /* Driver provides cipher suites, but we need to exclude WEP */
+ suites = kmemdup(local->hw.wiphy->cipher_suites,
+ sizeof(u32) * local->hw.wiphy->n_cipher_suites,
+ GFP_KERNEL);
+ if (!suites)
+ return -ENOMEM;
+
+ for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
+ u32 suite = local->hw.wiphy->cipher_suites[r];
- /* Set up cipher suites if driver relies on mac80211 cipher defs */
- if (!local->hw.wiphy->cipher_suites && !cs) {
+ if (suite == WLAN_CIPHER_SUITE_WEP40 ||
+ suite == WLAN_CIPHER_SUITE_WEP104)
+ continue;
+ suites[w++] = suite;
+ }
+ } else if (!local->hw.cipher_schemes) {
+ /* If the driver doesn't have cipher schemes, there's nothing
+ * else to do other than assign the (software supported and
+ * perhaps offloaded) cipher suites.
+ */
local->hw.wiphy->cipher_suites = cipher_suites;
local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
if (!have_mfp)
- local->hw.wiphy->n_cipher_suites--;
+ local->hw.wiphy->n_cipher_suites -= 4;
if (!have_wep) {
local->hw.wiphy->cipher_suites += 2;
local->hw.wiphy->n_cipher_suites -= 2;
}
+ /* not dynamically allocated, so just return */
return 0;
- }
+ } else {
+ const struct ieee80211_cipher_scheme *cs;
- if (!local->hw.wiphy->cipher_suites) {
- /*
- * Driver specifies cipher schemes only
- * We start counting ciphers defined by schemes, TKIP and CCMP
+ cs = local->hw.cipher_schemes;
+
+ /* Driver specifies cipher schemes only (but not cipher suites
+ * including the schemes)
+ *
+ * We start counting ciphers defined by schemes, TKIP, CCMP,
+ * CCMP-256, GCMP, and GCMP-256
*/
- n_suites = local->hw.n_cipher_schemes + 2;
+ n_suites = local->hw.n_cipher_schemes + 5;
/* check if we have WEP40 and WEP104 */
if (have_wep)
n_suites += 2;
- /* check if we have AES_CMAC */
+ /* check if we have AES_CMAC, BIP-CMAC-256, BIP-GMAC-128,
+ * BIP-GMAC-256
+ */
if (have_mfp)
- n_suites++;
+ n_suites += 4;
suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
if (!suites)
return -ENOMEM;
suites[w++] = WLAN_CIPHER_SUITE_CCMP;
+ suites[w++] = WLAN_CIPHER_SUITE_CCMP_256;
suites[w++] = WLAN_CIPHER_SUITE_TKIP;
+ suites[w++] = WLAN_CIPHER_SUITE_GCMP;
+ suites[w++] = WLAN_CIPHER_SUITE_GCMP_256;
if (have_wep) {
suites[w++] = WLAN_CIPHER_SUITE_WEP40;
suites[w++] = WLAN_CIPHER_SUITE_WEP104;
}
- if (have_mfp)
+ if (have_mfp) {
suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
+ suites[w++] = WLAN_CIPHER_SUITE_BIP_CMAC_256;
+ suites[w++] = WLAN_CIPHER_SUITE_BIP_GMAC_128;
+ suites[w++] = WLAN_CIPHER_SUITE_BIP_GMAC_256;
+ }
for (r = 0; r < local->hw.n_cipher_schemes; r++)
suites[w++] = cs[r].cipher;
- } else {
- /* Driver provides cipher suites, but we need to exclude WEP */
- suites = kmemdup(local->hw.wiphy->cipher_suites,
- sizeof(u32) * local->hw.wiphy->n_cipher_suites,
- GFP_KERNEL);
- if (!suites)
- return -ENOMEM;
-
- for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
- u32 suite = local->hw.wiphy->cipher_suites[r];
-
- if (suite == WLAN_CIPHER_SUITE_WEP40 ||
- suite == WLAN_CIPHER_SUITE_WEP104)
- continue;
- suites[w++] = suite;
- }
}
local->hw.wiphy->cipher_suites = suites;
ieee80211_max_network_latency;
result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
- if (result) {
- rtnl_lock();
+ if (result)
goto fail_pm_qos;
- }
#ifdef CONFIG_INET
local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
fail_ifa:
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
- rtnl_lock();
#endif
fail_pm_qos:
- ieee80211_led_exit(local);
+ rtnl_lock();
+ rate_control_deinitialize(local);
ieee80211_remove_interfaces(local);
fail_rate:
rtnl_unlock();
+ ieee80211_led_exit(local);
ieee80211_wep_free(local);
- sta_info_stop(local);
destroy_workqueue(local->workqueue);
fail_workqueue:
wiphy_unregister(local->hw.wiphy);
kfree(rcu_access_pointer(local->tx_latency));
+ sta_info_stop(local);
+
wiphy_free(local->hw.wiphy);
}
EXPORT_SYMBOL(ieee80211_free_hw);
sdata->u.mesh.mshcfg.auto_open_plinks &&
rssi_threshold_check(sdata, sta))
changed = mesh_plink_open(sta);
- else if (sta->plink_state == NL80211_PLINK_LISTEN &&
- (sdata->u.mesh.user_mpm ||
- sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED))
- cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
- elems->ie_start,
- elems->total_len,
- GFP_ATOMIC);
ieee80211_mps_frame_release(sta, elems);
out:
/* disable per-vif ps */
ieee80211_recalc_ps_vif(sdata);
+ /* make sure ongoing transmission finishes */
+ synchronize_net();
+
/*
* drop any frame before deauth/disassoc, this can be data or
* management frame. Since we are disconnecting, we should not
{
struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
struct ieee80211_mmie *mmie;
+ struct ieee80211_mmie_16 *mmie16;
if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
return -1;
mmie = (struct ieee80211_mmie *)
(skb->data + skb->len - sizeof(*mmie));
- if (mmie->element_id != WLAN_EID_MMIE ||
- mmie->length != sizeof(*mmie) - 2)
- return -1;
-
- return le16_to_cpu(mmie->key_id);
+ if (mmie->element_id == WLAN_EID_MMIE &&
+ mmie->length == sizeof(*mmie) - 2)
+ return le16_to_cpu(mmie->key_id);
+
+ mmie16 = (struct ieee80211_mmie_16 *)
+ (skb->data + skb->len - sizeof(*mmie16));
+ if (skb->len >= 24 + sizeof(*mmie16) &&
+ mmie16->element_id == WLAN_EID_MMIE &&
+ mmie16->length == sizeof(*mmie16) - 2)
+ return le16_to_cpu(mmie16->key_id);
+
+ return -1;
}
static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
result = ieee80211_crypto_tkip_decrypt(rx);
break;
case WLAN_CIPHER_SUITE_CCMP:
- result = ieee80211_crypto_ccmp_decrypt(rx);
+ result = ieee80211_crypto_ccmp_decrypt(
+ rx, IEEE80211_CCMP_MIC_LEN);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ result = ieee80211_crypto_ccmp_decrypt(
+ rx, IEEE80211_CCMP_256_MIC_LEN);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
result = ieee80211_crypto_aes_cmac_decrypt(rx);
break;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ result = ieee80211_crypto_aes_cmac_256_decrypt(rx);
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ result = ieee80211_crypto_aes_gmac_decrypt(rx);
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ result = ieee80211_crypto_gcmp_decrypt(rx);
+ break;
default:
result = ieee80211_crypto_hw_decrypt(rx);
}
/* This is the first fragment of a new frame. */
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
rx->seqno_idx, &(rx->skb));
- if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP &&
+ if (rx->key &&
+ (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP ||
+ rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256) &&
ieee80211_has_protected(fc)) {
int queue = rx->security_idx;
/* Store CCMP PN so that we can verify that the next
int i;
u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
int queue;
- if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)
+ if (!rx->key ||
+ (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256))
return RX_DROP_UNUSABLE;
memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
return RX_DROP_MONITOR;
if (rx->sta) {
- /* The security index has the same property as needed
+ /* The seqno index has the same property as needed
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
* for non-QoS-data frames. Here we know it's a data
* frame, so count MSDUs.
*/
- rx->sta->rx_msdu[rx->security_idx]++;
+ rx->sta->rx_msdu[rx->seqno_idx]++;
}
/*
mutex_lock(&local->mtx);
+ if (!ieee80211_can_run_worker(local)) {
+ aborted = true;
+ goto out_complete;
+ }
+
sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));
scan_req = rcu_dereference_protected(local->scan_req,
sinfo->generation = sdata->local->sta_generation;
+ /* do before driver, so beacon filtering drivers have a
+ * chance to e.g. just add the number of filtered beacons
+ * (or just modify the value entirely, of course)
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sinfo->rx_beacon = sdata->u.mgd.count_beacon_signal;
+
drv_sta_statistics(local, sdata, &sta->sta, sinfo);
sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) |
sinfo->rx_dropped_misc = sta->rx_dropped;
sinfo->beacon_loss_count = sta->beacon_loss_count;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX) |
+ BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
+ sinfo->rx_beacon_signal_avg = ieee80211_ave_rssi(&sdata->vif);
+ }
+
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) {
*/
sband = local->hw.wiphy->bands[band];
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
- if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
- action_code == WLAN_TDLS_SETUP_RESPONSE) &&
- ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
- if (action_code == WLAN_TDLS_SETUP_REQUEST) {
- ieee80211_apply_htcap_overrides(sdata, &ht_cap);
-
- /* disable SMPS in TDLS initiator */
- ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
- << IEEE80211_HT_CAP_SM_PS_SHIFT);
- } else {
- /* disable SMPS in TDLS responder */
- sta->sta.ht_cap.cap |=
- (WLAN_HT_CAP_SM_PS_DISABLED
- << IEEE80211_HT_CAP_SM_PS_SHIFT);
-
- /* the peer caps are already intersected with our own */
- memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
- }
+
+ if (action_code == WLAN_TDLS_SETUP_REQUEST && ht_cap.ht_supported) {
+ ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+ /* disable SMPS in TDLS initiator */
+ ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
+ << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+ } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
+ ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+ /* disable SMPS in TDLS responder */
+ sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
+ << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+ /* the peer caps are already intersected with our own */
+ memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
*/
if ((action_code == WLAN_TDLS_TEARDOWN) &&
(sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
- struct sta_info *sta = NULL;
bool try_resend; /* Should we keep skb for possible resend */
/* If not sending directly to peer - no point in keeping skb */
tx->key = NULL;
break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
if (!ieee80211_is_data_present(hdr->frame_control) &&
!ieee80211_use_mfp(hdr->frame_control, tx->sta,
tx->skb))
ieee80211_is_mgmt(hdr->frame_control);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
if (!ieee80211_is_mgmt(hdr->frame_control))
tx->key = NULL;
break;
case WLAN_CIPHER_SUITE_TKIP:
return ieee80211_crypto_tkip_encrypt(tx);
case WLAN_CIPHER_SUITE_CCMP:
- return ieee80211_crypto_ccmp_encrypt(tx);
+ return ieee80211_crypto_ccmp_encrypt(
+ tx, IEEE80211_CCMP_MIC_LEN);
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ return ieee80211_crypto_ccmp_encrypt(
+ tx, IEEE80211_CCMP_256_MIC_LEN);
case WLAN_CIPHER_SUITE_AES_CMAC:
return ieee80211_crypto_aes_cmac_encrypt(tx);
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ return ieee80211_crypto_aes_cmac_256_encrypt(tx);
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ return ieee80211_crypto_aes_gmac_encrypt(tx);
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ return ieee80211_crypto_gcmp_encrypt(tx);
default:
return ieee80211_crypto_hw_encrypt(tx);
}
/*
* Nothing should have been stuffed into the workqueue during
- * the suspend->resume cycle. If this WARN is seen then there
- * is a bug with either the driver suspend or something in
- * mac80211 stuffing into the workqueue which we haven't yet
- * cleared during mac80211's suspend cycle.
+ * the suspend->resume cycle. Since we can't check each caller
+ * of this function if we are already quiescing / suspended,
+ * check here and don't WARN since this can actually happen when
+ * the rx path (for example) is racing against __ieee80211_suspend
+ * and suspending / quiescing was set after the rx path checked
+ * them.
*/
static bool ieee80211_can_queue_work(struct ieee80211_local *local)
{
- if (WARN(local->suspended && !local->resuming,
- "queueing ieee80211 work while going to suspend\n"))
+ if (local->quiescing || (local->suspended && !local->resuming)) {
+ pr_warn("queueing ieee80211 work while going to suspend\n");
return false;
+ }
return true;
}
mb();
local->resuming = false;
+ /* It's possible that we don't handle the scan completion in
+ * time during suspend, so if it's still marked as completed
+ * here, queue the work and flush it to clean things up.
+ * Instead of calling the worker function directly here, we
+ * really queue it to avoid potential races with other flows
+ * scheduling the same work.
+ */
+ if (test_bit(SCAN_COMPLETED, &local->scanning)) {
+ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
+ flush_delayed_work(&local->scan_work);
+ }
+
if (local->open_count && !reconfig_due_to_wowlan)
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
#include "tkip.h"
#include "aes_ccm.h"
#include "aes_cmac.h"
+#include "aes_gmac.h"
+#include "aes_gcm.h"
#include "wpa.h"
ieee80211_tx_result
}
-static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
+static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb,
+ unsigned int mic_len)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_key *key = tx->key;
if (info->control.hw_key)
tail = 0;
else
- tail = IEEE80211_CCMP_MIC_LEN;
+ tail = mic_len;
if (WARN_ON(skb_tailroom(skb) < tail ||
skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
pos += IEEE80211_CCMP_HDR_LEN;
ccmp_special_blocks(skb, pn, b_0, aad);
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
- skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+ skb_put(skb, mic_len), mic_len);
return 0;
}
ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
+ unsigned int mic_len)
{
struct sk_buff *skb;
ieee80211_tx_set_protected(tx);
skb_queue_walk(&tx->skbs, skb) {
- if (ccmp_encrypt_skb(tx, skb) < 0)
+ if (ccmp_encrypt_skb(tx, skb, mic_len) < 0)
return TX_DROP;
}
ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
+ unsigned int mic_len)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int hdrlen;
!ieee80211_is_robust_mgmt_frame(skb))
return RX_CONTINUE;
- data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
- IEEE80211_CCMP_MIC_LEN;
+ data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
key->u.ccmp.tfm, b_0, aad,
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
data_len,
- skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
+ skb->data + skb->len - mic_len, mic_len))
return RX_DROP_UNUSABLE;
}
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
/* Remove CCMP header and MIC */
- if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN))
+ if (pskb_trim(skb, skb->len - mic_len))
return RX_DROP_UNUSABLE;
memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
return RX_CONTINUE;
}
+static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad)
+{
+ __le16 mask_fc;
+ u8 qos_tid;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ memcpy(j_0, hdr->addr2, ETH_ALEN);
+ memcpy(&j_0[ETH_ALEN], pn, IEEE80211_GCMP_PN_LEN);
+ j_0[13] = 0;
+ j_0[14] = 0;
+ j_0[AES_BLOCK_SIZE - 1] = 0x01;
+
+ /* AAD (extra authenticate-only data) / masked 802.11 header
+ * FC | A1 | A2 | A3 | SC | [A4] | [QC]
+ */
+ put_unaligned_be16(ieee80211_hdrlen(hdr->frame_control) - 2, &aad[0]);
+ /* Mask FC: zero subtype b4 b5 b6 (if not mgmt)
+ * Retry, PwrMgt, MoreData; set Protected
+ */
+ mask_fc = hdr->frame_control;
+ mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |
+ IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
+ if (!ieee80211_is_mgmt(hdr->frame_control))
+ mask_fc &= ~cpu_to_le16(0x0070);
+ mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+ put_unaligned(mask_fc, (__le16 *)&aad[2]);
+ memcpy(&aad[4], &hdr->addr1, 3 * ETH_ALEN);
+
+ /* Mask Seq#, leave Frag# */
+ aad[22] = *((u8 *)&hdr->seq_ctrl) & 0x0f;
+ aad[23] = 0;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ qos_tid = *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_TID_MASK;
+ else
+ qos_tid = 0;
+
+ if (ieee80211_has_a4(hdr->frame_control)) {
+ memcpy(&aad[24], hdr->addr4, ETH_ALEN);
+ aad[30] = qos_tid;
+ aad[31] = 0;
+ } else {
+ memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN);
+ aad[24] = qos_tid;
+ }
+}
+
+static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
+{
+ hdr[0] = pn[5];
+ hdr[1] = pn[4];
+ hdr[2] = 0;
+ hdr[3] = 0x20 | (key_id << 6);
+ hdr[4] = pn[3];
+ hdr[5] = pn[2];
+ hdr[6] = pn[1];
+ hdr[7] = pn[0];
+}
+
+static inline void gcmp_hdr2pn(u8 *pn, const u8 *hdr)
+{
+ pn[0] = hdr[7];
+ pn[1] = hdr[6];
+ pn[2] = hdr[5];
+ pn[3] = hdr[4];
+ pn[4] = hdr[1];
+ pn[5] = hdr[0];
+}
+
+static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_key *key = tx->key;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int hdrlen, len, tail;
+ u8 *pos;
+ u8 pn[6];
+ u64 pn64;
+ u8 aad[2 * AES_BLOCK_SIZE];
+ u8 j_0[AES_BLOCK_SIZE];
+
+ if (info->control.hw_key &&
+ !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
+ !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+ !((info->control.hw_key->flags &
+ IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
+ ieee80211_is_mgmt(hdr->frame_control))) {
+ /* hwaccel has no need for preallocated room for GCMP
+ * header or MIC fields
+ */
+ return 0;
+ }
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ len = skb->len - hdrlen;
+
+ if (info->control.hw_key)
+ tail = 0;
+ else
+ tail = IEEE80211_GCMP_MIC_LEN;
+
+ if (WARN_ON(skb_tailroom(skb) < tail ||
+ skb_headroom(skb) < IEEE80211_GCMP_HDR_LEN))
+ return -1;
+
+ pos = skb_push(skb, IEEE80211_GCMP_HDR_LEN);
+ memmove(pos, pos + IEEE80211_GCMP_HDR_LEN, hdrlen);
+ skb_set_network_header(skb, skb_network_offset(skb) +
+ IEEE80211_GCMP_HDR_LEN);
+
+ /* the HW only needs room for the IV, but not the actual IV */
+ if (info->control.hw_key &&
+ (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
+ return 0;
+
+ hdr = (struct ieee80211_hdr *)pos;
+ pos += hdrlen;
+
+ pn64 = atomic64_inc_return(&key->u.gcmp.tx_pn);
+
+ pn[5] = pn64;
+ pn[4] = pn64 >> 8;
+ pn[3] = pn64 >> 16;
+ pn[2] = pn64 >> 24;
+ pn[1] = pn64 >> 32;
+ pn[0] = pn64 >> 40;
+
+ gcmp_pn2hdr(pos, pn, key->conf.keyidx);
+
+ /* hwaccel - with software GCMP header */
+ if (info->control.hw_key)
+ return 0;
+
+ pos += IEEE80211_GCMP_HDR_LEN;
+ gcmp_special_blocks(skb, pn, j_0, aad);
+ ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
+ skb_put(skb, IEEE80211_GCMP_MIC_LEN));
+
+ return 0;
+}
+
+ieee80211_tx_result
+ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx)
+{
+ struct sk_buff *skb;
+
+ ieee80211_tx_set_protected(tx);
+
+ skb_queue_walk(&tx->skbs, skb) {
+ if (gcmp_encrypt_skb(tx, skb) < 0)
+ return TX_DROP;
+ }
+
+ return TX_CONTINUE;
+}
+
+ieee80211_rx_result
+ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ int hdrlen;
+ struct ieee80211_key *key = rx->key;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ u8 pn[IEEE80211_GCMP_PN_LEN];
+ int data_len;
+ int queue;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_is_robust_mgmt_frame(skb))
+ return RX_CONTINUE;
+
+ data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN -
+ IEEE80211_GCMP_MIC_LEN;
+ if (!rx->sta || data_len < 0)
+ return RX_DROP_UNUSABLE;
+
+ if (status->flag & RX_FLAG_DECRYPTED) {
+ if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
+ return RX_DROP_UNUSABLE;
+ } else {
+ if (skb_linearize(rx->skb))
+ return RX_DROP_UNUSABLE;
+ }
+
+ gcmp_hdr2pn(pn, skb->data + hdrlen);
+
+ queue = rx->security_idx;
+
+ if (memcmp(pn, key->u.gcmp.rx_pn[queue], IEEE80211_GCMP_PN_LEN) <= 0) {
+ key->u.gcmp.replays++;
+ return RX_DROP_UNUSABLE;
+ }
+
+ if (!(status->flag & RX_FLAG_DECRYPTED)) {
+ u8 aad[2 * AES_BLOCK_SIZE];
+ u8 j_0[AES_BLOCK_SIZE];
+ /* hardware didn't decrypt/verify MIC */
+ gcmp_special_blocks(skb, pn, j_0, aad);
+
+ if (ieee80211_aes_gcm_decrypt(
+ key->u.gcmp.tfm, j_0, aad,
+ skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
+ data_len,
+ skb->data + skb->len - IEEE80211_GCMP_MIC_LEN))
+ return RX_DROP_UNUSABLE;
+ }
+
+ memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
+
+ /* Remove GCMP header and MIC */
+ if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
+ return RX_DROP_UNUSABLE;
+ memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
+ skb_pull(skb, IEEE80211_GCMP_HDR_LEN);
+
+ return RX_CONTINUE;
+}
+
static ieee80211_tx_result
ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
struct sk_buff *skb)
return TX_CONTINUE;
}
+ieee80211_tx_result
+ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
+{
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_key *key = tx->key;
+ struct ieee80211_mmie_16 *mmie;
+ u8 aad[20];
+ u64 pn64;
+
+ if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+ return TX_DROP;
+
+ skb = skb_peek(&tx->skbs);
+
+ info = IEEE80211_SKB_CB(skb);
+
+ if (info->control.hw_key)
+ return TX_CONTINUE;
+
+ if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+ return TX_DROP;
+
+ mmie = (struct ieee80211_mmie_16 *)skb_put(skb, sizeof(*mmie));
+ mmie->element_id = WLAN_EID_MMIE;
+ mmie->length = sizeof(*mmie) - 2;
+ mmie->key_id = cpu_to_le16(key->conf.keyidx);
+
+ /* PN = PN + 1 */
+ pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn);
+
+ bip_ipn_set64(mmie->sequence_number, pn64);
+
+ bip_aad(skb, aad);
+
+ /* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
+ */
+ ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
+ skb->data + 24, skb->len - 24, mmie->mic);
+
+ return TX_CONTINUE;
+}
ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
+ieee80211_rx_result
+ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx)
+{
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_key *key = rx->key;
+ struct ieee80211_mmie_16 *mmie;
+ u8 aad[20], mic[16], ipn[6];
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_mgmt(hdr->frame_control))
+ return RX_CONTINUE;
+
+ /* management frames are already linear */
+
+ if (skb->len < 24 + sizeof(*mmie))
+ return RX_DROP_UNUSABLE;
+
+ mmie = (struct ieee80211_mmie_16 *)
+ (skb->data + skb->len - sizeof(*mmie));
+ if (mmie->element_id != WLAN_EID_MMIE ||
+ mmie->length != sizeof(*mmie) - 2)
+ return RX_DROP_UNUSABLE; /* Invalid MMIE */
+
+ bip_ipn_swap(ipn, mmie->sequence_number);
+
+ if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
+ key->u.aes_cmac.replays++;
+ return RX_DROP_UNUSABLE;
+ }
+
+ if (!(status->flag & RX_FLAG_DECRYPTED)) {
+ /* hardware didn't decrypt/verify MIC */
+ bip_aad(skb, aad);
+ ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
+ skb->data + 24, skb->len - 24, mic);
+ if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
+ key->u.aes_cmac.icverrors++;
+ return RX_DROP_UNUSABLE;
+ }
+ }
+
+ memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
+
+ /* Remove MMIE */
+ skb_trim(skb, skb->len - sizeof(*mmie));
+
+ return RX_CONTINUE;
+}
+
+ieee80211_tx_result
+ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
+{
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_key *key = tx->key;
+ struct ieee80211_mmie_16 *mmie;
+ struct ieee80211_hdr *hdr;
+ u8 aad[20];
+ u64 pn64;
+ u8 nonce[12];
+
+ if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+ return TX_DROP;
+
+ skb = skb_peek(&tx->skbs);
+
+ info = IEEE80211_SKB_CB(skb);
+
+ if (info->control.hw_key)
+ return TX_CONTINUE;
+
+ if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+ return TX_DROP;
+
+ mmie = (struct ieee80211_mmie_16 *)skb_put(skb, sizeof(*mmie));
+ mmie->element_id = WLAN_EID_MMIE;
+ mmie->length = sizeof(*mmie) - 2;
+ mmie->key_id = cpu_to_le16(key->conf.keyidx);
+
+ /* PN = PN + 1 */
+ pn64 = atomic64_inc_return(&key->u.aes_gmac.tx_pn);
+
+ bip_ipn_set64(mmie->sequence_number, pn64);
+
+ bip_aad(skb, aad);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ memcpy(nonce, hdr->addr2, ETH_ALEN);
+ bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
+
+ /* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
+ if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+ skb->data + 24, skb->len - 24, mmie->mic) < 0)
+ return TX_DROP;
+
+ return TX_CONTINUE;
+}
+
+ieee80211_rx_result
+ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
+{
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_key *key = rx->key;
+ struct ieee80211_mmie_16 *mmie;
+ u8 aad[20], mic[16], ipn[6], nonce[12];
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_mgmt(hdr->frame_control))
+ return RX_CONTINUE;
+
+ /* management frames are already linear */
+
+ if (skb->len < 24 + sizeof(*mmie))
+ return RX_DROP_UNUSABLE;
+
+ mmie = (struct ieee80211_mmie_16 *)
+ (skb->data + skb->len - sizeof(*mmie));
+ if (mmie->element_id != WLAN_EID_MMIE ||
+ mmie->length != sizeof(*mmie) - 2)
+ return RX_DROP_UNUSABLE; /* Invalid MMIE */
+
+ bip_ipn_swap(ipn, mmie->sequence_number);
+
+ if (memcmp(ipn, key->u.aes_gmac.rx_pn, 6) <= 0) {
+ key->u.aes_gmac.replays++;
+ return RX_DROP_UNUSABLE;
+ }
+
+ if (!(status->flag & RX_FLAG_DECRYPTED)) {
+ /* hardware didn't decrypt/verify MIC */
+ bip_aad(skb, aad);
+
+ memcpy(nonce, hdr->addr2, ETH_ALEN);
+ memcpy(nonce + ETH_ALEN, ipn, 6);
+
+ if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+ skb->data + 24, skb->len - 24,
+ mic) < 0 ||
+ memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
+ key->u.aes_gmac.icverrors++;
+ return RX_DROP_UNUSABLE;
+ }
+ }
+
+ memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
+
+ /* Remove MMIE */
+ skb_trim(skb, skb->len - sizeof(*mmie));
+
+ return RX_CONTINUE;
+}
+
ieee80211_tx_result
ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
{
ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx);
ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
+ unsigned int mic_len);
ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
+ unsigned int mic_len);
ieee80211_tx_result
ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_tx_result
+ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx);
ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_rx_result
+ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_tx_result
+ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx);
ieee80211_tx_result
ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx);
ieee80211_rx_result
ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_tx_result
+ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx);
+
#endif /* WPA_H */
struct nft_set_iter *iter)
{
struct rhashtable *priv = nft_set_priv(set);
- const struct bucket_table *tbl;
const struct nft_hash_elem *he;
+ struct rhashtable_iter hti;
struct nft_set_elem elem;
- unsigned int i;
+ int err;
- tbl = rht_dereference_rcu(priv->tbl, priv);
- for (i = 0; i < tbl->size; i++) {
- struct rhash_head *pos;
+ err = rhashtable_walk_init(priv, &hti);
+ iter->err = err;
+ if (err)
+ return;
+
+ err = rhashtable_walk_start(&hti);
+ if (err && err != -EAGAIN) {
+ iter->err = err;
+ goto out;
+ }
- rht_for_each_entry_rcu(he, pos, tbl, i, node) {
- if (iter->count < iter->skip)
- goto cont;
+ while ((he = rhashtable_walk_next(&hti))) {
+ if (IS_ERR(he)) {
+ err = PTR_ERR(he);
+ if (err != -EAGAIN) {
+ iter->err = err;
+ goto out;
+ }
+ }
+
+ if (iter->count < iter->skip)
+ goto cont;
+
+ memcpy(&elem.key, &he->key, sizeof(elem.key));
+ if (set->flags & NFT_SET_MAP)
+ memcpy(&elem.data, he->data, sizeof(elem.data));
+ elem.flags = 0;
- memcpy(&elem.key, &he->key, sizeof(elem.key));
- if (set->flags & NFT_SET_MAP)
- memcpy(&elem.data, he->data, sizeof(elem.data));
- elem.flags = 0;
+ iter->err = iter->fn(ctx, set, iter, &elem);
+ if (iter->err < 0)
+ goto out;
- iter->err = iter->fn(ctx, set, iter, &elem);
- if (iter->err < 0)
- return;
cont:
- iter->count++;
- }
+ iter->count++;
}
+
+out:
+ rhashtable_walk_stop(&hti);
+ rhashtable_walk_exit(&hti);
}
static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
goto out;
}
+ /* It's a really convoluted way for userland to ask for mmaped
+ * sendmsg(), but that's what we've got...
+ */
if (netlink_tx_is_mmaped(sk) &&
+ msg->msg_iter.type == ITER_IOVEC &&
+ msg->msg_iter.nr_segs == 1 &&
msg->msg_iter.iov->iov_base == NULL) {
err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group,
&scm);
#ifdef CONFIG_PROC_FS
struct nl_seq_iter {
struct seq_net_private p;
+ struct rhashtable_iter hti;
int link;
- int hash_idx;
};
-static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos)
+static int netlink_walk_start(struct nl_seq_iter *iter)
{
- struct nl_seq_iter *iter = seq->private;
- int i, j;
- struct netlink_sock *nlk;
- struct sock *s;
- loff_t off = 0;
-
- for (i = 0; i < MAX_LINKS; i++) {
- struct rhashtable *ht = &nl_table[i].hash;
- const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht);
-
- for (j = 0; j < tbl->size; j++) {
- struct rhash_head *node;
-
- rht_for_each_entry_rcu(nlk, node, tbl, j, node) {
- s = (struct sock *)nlk;
+ int err;
- if (sock_net(s) != seq_file_net(seq))
- continue;
- if (off == pos) {
- iter->link = i;
- iter->hash_idx = j;
- return s;
- }
- ++off;
- }
- }
+ err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti);
+ if (err) {
+ iter->link = MAX_LINKS;
+ return err;
}
- return NULL;
+
+ err = rhashtable_walk_start(&iter->hti);
+ return err == -EAGAIN ? 0 : err;
}
-static void *netlink_seq_start(struct seq_file *seq, loff_t *pos)
- __acquires(RCU)
+static void netlink_walk_stop(struct nl_seq_iter *iter)
{
- rcu_read_lock();
- return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+ rhashtable_walk_stop(&iter->hti);
+ rhashtable_walk_exit(&iter->hti);
}
-static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+static void *__netlink_seq_next(struct seq_file *seq)
{
- struct rhashtable *ht;
- const struct bucket_table *tbl;
- struct rhash_head *node;
+ struct nl_seq_iter *iter = seq->private;
struct netlink_sock *nlk;
- struct nl_seq_iter *iter;
- struct net *net;
- int i, j;
- ++*pos;
+ do {
+ for (;;) {
+ int err;
- if (v == SEQ_START_TOKEN)
- return netlink_seq_socket_idx(seq, 0);
+ nlk = rhashtable_walk_next(&iter->hti);
- net = seq_file_net(seq);
- iter = seq->private;
- nlk = v;
+ if (IS_ERR(nlk)) {
+ if (PTR_ERR(nlk) == -EAGAIN)
+ continue;
- i = iter->link;
- ht = &nl_table[i].hash;
- tbl = rht_dereference_rcu(ht->tbl, ht);
- rht_for_each_entry_rcu_continue(nlk, node, nlk->node.next, tbl, iter->hash_idx, node)
- if (net_eq(sock_net((struct sock *)nlk), net))
- return nlk;
+ return nlk;
+ }
- j = iter->hash_idx + 1;
+ if (nlk)
+ break;
- do {
+ netlink_walk_stop(iter);
+ if (++iter->link >= MAX_LINKS)
+ return NULL;
- for (; j < tbl->size; j++) {
- rht_for_each_entry_rcu(nlk, node, tbl, j, node) {
- if (net_eq(sock_net((struct sock *)nlk), net)) {
- iter->link = i;
- iter->hash_idx = j;
- return nlk;
- }
- }
+ err = netlink_walk_start(iter);
+ if (err)
+ return ERR_PTR(err);
}
+ } while (sock_net(&nlk->sk) != seq_file_net(seq));
- j = 0;
- } while (++i < MAX_LINKS);
+ return nlk;
+}
- return NULL;
+static void *netlink_seq_start(struct seq_file *seq, loff_t *posp)
+{
+ struct nl_seq_iter *iter = seq->private;
+ void *obj = SEQ_START_TOKEN;
+ loff_t pos;
+ int err;
+
+ iter->link = 0;
+
+ err = netlink_walk_start(iter);
+ if (err)
+ return ERR_PTR(err);
+
+ for (pos = *posp; pos && obj && !IS_ERR(obj); pos--)
+ obj = __netlink_seq_next(seq);
+
+ return obj;
+}
+
+static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ ++*pos;
+ return __netlink_seq_next(seq);
}
static void netlink_seq_stop(struct seq_file *seq, void *v)
- __releases(RCU)
{
- rcu_read_unlock();
+ struct nl_seq_iter *iter = seq->private;
+
+ if (iter->link >= MAX_LINKS)
+ return;
+
+ netlink_walk_stop(iter);
}
{ "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
{ "BCM2E39", RFKILL_TYPE_BLUETOOTH },
{ "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
+ { "BCM2E40", RFKILL_TYPE_BLUETOOTH },
{ "BCM2E64", RFKILL_TYPE_BLUETOOTH },
{ "BCM4752", RFKILL_TYPE_GPS },
{ "LNV4752", RFKILL_TYPE_GPS },
call->state != RXRPC_CALL_SERVER_SEND_REPLY) {
ret = -EPROTO; /* request phase complete for this client call */
} else {
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
ret = rxrpc_send_data(NULL, call->socket, call, msg, len);
- set_fs(oldfs);
}
release_sock(&call->socket->sk);
struct msghdr *msg, size_t len)
{
struct rxrpc_skb_priv *sp;
- unsigned char __user *from;
struct sk_buff *skb;
- const struct iovec *iov;
struct sock *sk = &rx->sk;
long timeo;
bool more;
- int ret, ioc, segment, copied;
+ int ret, copied;
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
return -EPIPE;
- iov = msg->msg_iter.iov;
- ioc = msg->msg_iter.nr_segs - 1;
- from = iov->iov_base;
- segment = iov->iov_len;
- iov++;
more = msg->msg_flags & MSG_MORE;
skb = call->tx_pending;
call->tx_pending = NULL;
copied = 0;
- do {
+ if (len > iov_iter_count(&msg->msg_iter))
+ len = iov_iter_count(&msg->msg_iter);
+ while (len) {
int copy;
- if (segment > len)
- segment = len;
-
- _debug("SEGMENT %d @%p", segment, from);
-
if (!skb) {
size_t size, chunk, max, space;
/* append next segment of data to the current buffer */
copy = skb_tailroom(skb);
ASSERTCMP(copy, >, 0);
- if (copy > segment)
- copy = segment;
+ if (copy > len)
+ copy = len;
if (copy > sp->remain)
copy = sp->remain;
_debug("add");
- ret = skb_add_data(skb, from, copy);
+ ret = skb_add_data(skb, &msg->msg_iter, copy);
_debug("added");
if (ret < 0)
goto efault;
copied += copy;
len -= copy;
- segment -= copy;
- from += copy;
- while (segment == 0 && ioc > 0) {
- from = iov->iov_base;
- segment = iov->iov_len;
- iov++;
- ioc--;
- }
- if (len == 0) {
- segment = 0;
- ioc = 0;
- }
/* check for the far side aborting the call or a network error
* occurring */
goto call_aborted;
/* add the packet to the send queue if it's now full */
- if (sp->remain <= 0 || (segment == 0 && !more)) {
+ if (sp->remain <= 0 || (!len && !more)) {
struct rxrpc_connection *conn = call->conn;
uint32_t seq;
size_t pad;
memcpy(skb->head, &sp->hdr,
sizeof(struct rxrpc_header));
- rxrpc_queue_packet(call, skb, segment == 0 && !more);
+ rxrpc_queue_packet(call, skb, !iov_iter_count(&msg->msg_iter) && !more);
skb = NULL;
}
-
- } while (segment > 0);
+ }
success:
ret = copied;
#include <net/pkt_sched.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <net/tcp.h>
/*
* Per flow structure, dynamically allocated
u32 flow_refill_delay;
u32 flow_max_rate; /* optional max rate per flow */
u32 flow_plimit; /* max packets per flow */
+ u32 orphan_mask; /* mask for orphaned skb */
struct rb_root *fq_root;
u8 rate_enable;
u8 fq_trees_log;
if (unlikely((skb->priority & TC_PRIO_MAX) == TC_PRIO_CONTROL))
return &q->internal;
- if (unlikely(!sk)) {
+ /* SYNACK messages are attached to a listener socket.
+ * 1) They are not part of a 'flow' yet
+ * 2) We do not want to rate limit them (eg SYNFLOOD attack),
+ * especially if the listener set SO_MAX_PACING_RATE
+ * 3) We pretend they are orphaned
+ */
+ if (!sk || sk->sk_state == TCP_LISTEN) {
+ unsigned long hash = skb_get_hash(skb) & q->orphan_mask;
+
/* By forcing low order bit to 1, we make sure to not
* collide with a local flow (socket pointers are word aligned)
*/
- sk = (struct sock *)(skb_get_hash(skb) | 1L);
+ sk = (struct sock *)((hash << 1) | 1UL);
+ skb_orphan(skb);
}
root = &q->fq_root[hash_32((u32)(long)sk, q->fq_trees_log)];
goto begin;
}
- if (unlikely(f->head && now < f->time_next_packet)) {
+ skb = f->head;
+ if (unlikely(skb && now < f->time_next_packet &&
+ !skb_is_tcp_pure_ack(skb))) {
head->first = f->next;
fq_flow_set_throttled(q, f);
goto begin;
goto begin;
}
prefetch(&skb->end);
- f->time_next_packet = now;
f->credit -= qdisc_pkt_len(skb);
if (f->credit > 0 || !q->rate_enable)
goto out;
+ /* Do not pace locally generated ack packets */
+ if (skb_is_tcp_pure_ack(skb))
+ goto out;
+
rate = q->flow_max_rate;
if (skb->sk)
rate = min(skb->sk->sk_pacing_rate, rate);
q->flow_refill_delay = usecs_to_jiffies(usecs_delay);
}
+ if (tb[TCA_FQ_ORPHAN_MASK])
+ q->orphan_mask = nla_get_u32(tb[TCA_FQ_ORPHAN_MASK]);
+
if (!err) {
sch_tree_unlock(sch);
err = fq_resize(sch, fq_log);
q->delayed = RB_ROOT;
q->fq_root = NULL;
q->fq_trees_log = ilog2(1024);
+ q->orphan_mask = 1024 - 1;
qdisc_watchdog_init(&q->watchdog, sch);
if (opt)
nla_put_u32(skb, TCA_FQ_FLOW_MAX_RATE, q->flow_max_rate) ||
nla_put_u32(skb, TCA_FQ_FLOW_REFILL_DELAY,
jiffies_to_usecs(q->flow_refill_delay)) ||
+ nla_put_u32(skb, TCA_FQ_ORPHAN_MASK, q->orphan_mask) ||
nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log))
goto nla_put_failure;
unsigned int sysctl_net_busy_poll __read_mostly;
#endif
-static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos);
-static ssize_t sock_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos);
+static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to);
+static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from);
static int sock_mmap(struct file *file, struct vm_area_struct *vma);
static int sock_close(struct inode *inode, struct file *file);
static const struct file_operations socket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .aio_read = sock_aio_read,
- .aio_write = sock_aio_write,
+ .read = new_sync_read,
+ .write = new_sync_write,
+ .read_iter = sock_read_iter,
+ .write_iter = sock_write_iter,
.poll = sock_poll,
.unlocked_ioctl = sock_ioctl,
#ifdef CONFIG_COMPAT
return sock->ops->splice_read(sock, ppos, pipe, len, flags);
}
-static ssize_t do_sock_read(struct msghdr *msg, struct kiocb *iocb,
- struct file *file, const struct iovec *iov,
- unsigned long nr_segs)
+static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
+ struct file *file = iocb->ki_filp;
struct socket *sock = file->private_data;
+ struct msghdr msg = {.msg_iter = *to};
+ ssize_t res;
- msg->msg_name = NULL;
- msg->msg_namelen = 0;
- msg->msg_control = NULL;
- msg->msg_controllen = 0;
- iov_iter_init(&msg->msg_iter, READ, iov, nr_segs, iocb->ki_nbytes);
- msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+ if (file->f_flags & O_NONBLOCK)
+ msg.msg_flags = MSG_DONTWAIT;
- return __sock_recvmsg(iocb, sock, msg, iocb->ki_nbytes, msg->msg_flags);
-}
-
-static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
-{
- struct msghdr msg;
-
- if (pos != 0)
+ if (iocb->ki_pos != 0)
return -ESPIPE;
if (iocb->ki_nbytes == 0) /* Match SYS5 behaviour */
return 0;
- return do_sock_read(&msg, iocb, iocb->ki_filp, iov, nr_segs);
+ res = __sock_recvmsg(iocb, sock, &msg,
+ iocb->ki_nbytes, msg.msg_flags);
+ *to = msg.msg_iter;
+ return res;
}
-static ssize_t do_sock_write(struct msghdr *msg, struct kiocb *iocb,
- struct file *file, const struct iovec *iov,
- unsigned long nr_segs)
+static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
+ struct file *file = iocb->ki_filp;
struct socket *sock = file->private_data;
+ struct msghdr msg = {.msg_iter = *from};
+ ssize_t res;
- msg->msg_name = NULL;
- msg->msg_namelen = 0;
- msg->msg_control = NULL;
- msg->msg_controllen = 0;
- iov_iter_init(&msg->msg_iter, WRITE, iov, nr_segs, iocb->ki_nbytes);
- msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
- if (sock->type == SOCK_SEQPACKET)
- msg->msg_flags |= MSG_EOR;
-
- return __sock_sendmsg(iocb, sock, msg, iocb->ki_nbytes);
-}
+ if (iocb->ki_pos != 0)
+ return -ESPIPE;
-static ssize_t sock_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
-{
- struct msghdr msg;
+ if (file->f_flags & O_NONBLOCK)
+ msg.msg_flags = MSG_DONTWAIT;
- if (pos != 0)
- return -ESPIPE;
+ if (sock->type == SOCK_SEQPACKET)
+ msg.msg_flags |= MSG_EOR;
- return do_sock_write(&msg, iocb, iocb->ki_filp, iov, nr_segs);
+ res = __sock_sendmsg(iocb, sock, &msg, iocb->ki_nbytes);
+ *from = msg.msg_iter;
+ return res;
}
/*
/*
* net/tipc/discover.c
*
- * Copyright (c) 2003-2006, 2014, Ericsson AB
+ * Copyright (c) 2003-2006, 2014-2015, Ericsson AB
* Copyright (c) 2005-2006, 2010-2011, Wind River Systems
* All rights reserved.
*
/* indicates no timer in use */
#define TIPC_LINK_REQ_INACTIVE 0xffffffff
-
/**
* struct tipc_link_req - information about an ongoing link setup request
* @bearer_id: identity of bearer issuing requests
if (!tipc_in_scope(bearer->domain, onode))
return;
- /* Locate, or if necessary, create, node: */
- node = tipc_node_find(net, onode);
- if (!node)
- node = tipc_node_create(net, onode);
+ node = tipc_node_create(net, onode);
if (!node)
return;
-
tipc_node_lock(node);
link = node->links[bearer->identity];
return (i + 3) & ~3u;
}
+static void tipc_link_release(struct kref *kref)
+{
+ kfree(container_of(kref, struct tipc_link, ref));
+}
+
+static void tipc_link_get(struct tipc_link *l_ptr)
+{
+ kref_get(&l_ptr->ref);
+}
+
+static void tipc_link_put(struct tipc_link *l_ptr)
+{
+ kref_put(&l_ptr->ref, tipc_link_release);
+}
+
static void link_init_max_pkt(struct tipc_link *l_ptr)
{
struct tipc_node *node = l_ptr->owner;
tipc_link_push_packets(l_ptr);
tipc_node_unlock(l_ptr->owner);
+ tipc_link_put(l_ptr);
}
static void link_set_timer(struct tipc_link *link, unsigned long time)
{
- mod_timer(&link->timer, jiffies + time);
+ if (!mod_timer(&link->timer, jiffies + time))
+ tipc_link_get(link);
}
/**
pr_warn("Link creation failed, no memory\n");
return NULL;
}
-
+ kref_init(&l_ptr->ref);
l_ptr->addr = peer;
if_name = strchr(b_ptr->name, ':') + 1;
sprintf(l_ptr->name, "%u.%u.%u:%s-%u.%u.%u:unknown",
skb_queue_head_init(&l_ptr->waiting_sks);
link_reset_statistics(l_ptr);
-
tipc_node_attach_link(n_ptr, l_ptr);
-
setup_timer(&l_ptr->timer, link_timeout, (unsigned long)l_ptr);
-
link_state_event(l_ptr, STARTING_EVT);
return l_ptr;
}
+/**
+ * link_delete - Conditional deletion of link.
+ * If timer still running, real delete is done when it expires
+ * @link: link to be deleted
+ */
+void tipc_link_delete(struct tipc_link *link)
+{
+ tipc_link_reset_fragments(link);
+ tipc_node_detach_link(link->owner, link);
+ tipc_link_put(link);
+}
+
void tipc_link_delete_list(struct net *net, unsigned int bearer_id,
bool shutting_down)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *l_ptr;
- struct tipc_node *n_ptr;
+ struct tipc_link *link;
+ struct tipc_node *node;
rcu_read_lock();
- list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
- tipc_node_lock(n_ptr);
- l_ptr = n_ptr->links[bearer_id];
- if (l_ptr) {
- tipc_link_reset(l_ptr);
- if (shutting_down || !tipc_node_is_up(n_ptr)) {
- tipc_node_detach_link(l_ptr->owner, l_ptr);
- tipc_link_reset_fragments(l_ptr);
- tipc_node_unlock(n_ptr);
-
- /* Nobody else can access this link now: */
- del_timer_sync(&l_ptr->timer);
- kfree(l_ptr);
- } else {
- /* Detach/delete when failover is finished: */
- l_ptr->flags |= LINK_STOPPED;
- tipc_node_unlock(n_ptr);
- del_timer_sync(&l_ptr->timer);
- }
+ list_for_each_entry_rcu(node, &tn->node_list, list) {
+ tipc_node_lock(node);
+ link = node->links[bearer_id];
+ if (!link) {
+ tipc_node_unlock(node);
continue;
}
- tipc_node_unlock(n_ptr);
+ tipc_link_reset(link);
+ if (del_timer(&link->timer))
+ tipc_link_put(link);
+ link->flags |= LINK_STOPPED;
+ /* Delete link now, or when failover is finished: */
+ if (shutting_down || !tipc_node_is_up(node))
+ tipc_link_delete(link);
+ tipc_node_unlock(node);
}
rcu_read_unlock();
}
break;
case STARTING_EVT:
l_ptr->flags |= LINK_STARTED;
- /* fall through */
+ l_ptr->fsm_msg_cnt++;
+ link_set_timer(l_ptr, cont_intv);
+ break;
case TIMEOUT_EVT:
tipc_link_proto_xmit(l_ptr, RESET_MSG, 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
}
}
exit:
- if ((l_ptr->exp_msg_count == 0) && (l_ptr->flags & LINK_STOPPED)) {
- tipc_node_detach_link(l_ptr->owner, l_ptr);
- kfree(l_ptr);
- }
+ if ((!l_ptr->exp_msg_count) && (l_ptr->flags & LINK_STOPPED))
+ tipc_link_delete(l_ptr);
return buf;
}
* @media_addr: media address to use when sending messages over link
* @timer: link timer
* @owner: pointer to peer node
+ * @refcnt: reference counter for permanent references (owner node & timer)
* @flags: execution state flags for link endpoint instance
* @checkpoint: reference point for triggering link continuity checking
* @peer_session: link session # being used by peer end of link
struct tipc_media_addr media_addr;
struct timer_list timer;
struct tipc_node *owner;
+ struct kref ref;
/* Management and link supervision data */
unsigned int flags;
struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
struct tipc_bearer *b_ptr,
const struct tipc_media_addr *media_addr);
+void tipc_link_delete(struct tipc_link *link);
void tipc_link_delete_list(struct net *net, unsigned int bearer_id,
bool shutting_down);
void tipc_link_failover_send_queue(struct tipc_link *l_ptr);
* tipc_msg_build - create buffer chain containing specified header and data
* @mhdr: Message header, to be prepended to data
* @m: User message
- * @offset: Posision in iov to start copying from
* @dsz: Total length of user data
* @pktmax: Max packet size that can be used
* @list: Buffer or chain of buffers to be returned to caller
__skb_queue_tail(list, skb);
skb_copy_to_linear_data(skb, mhdr, mhsz);
pktpos = skb->data + mhsz;
- if (!dsz || !memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset,
- dsz))
+ if (copy_from_iter(pktpos, dsz, &m->msg_iter) == dsz)
return dsz;
rc = -EFAULT;
goto error;
if (drem < pktrem)
pktrem = drem;
- if (memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset, pktrem)) {
+ if (copy_from_iter(pktpos, pktrem, &m->msg_iter) != pktrem) {
rc = -EFAULT;
goto error;
}
drem -= pktrem;
- offset += pktrem;
if (!drem)
break;
struct tipc_node *n_ptr, *temp_node;
spin_lock_bh(&tn->node_list_lock);
-
+ n_ptr = tipc_node_find(net, addr);
+ if (n_ptr)
+ goto exit;
n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
if (!n_ptr) {
- spin_unlock_bh(&tn->node_list_lock);
pr_warn("Node creation failed, no memory\n");
- return NULL;
+ goto exit;
}
-
n_ptr->addr = addr;
n_ptr->net = net;
spin_lock_init(&n_ptr->lock);
list_add_tail_rcu(&n_ptr->list, &temp_node->list);
n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
n_ptr->signature = INVALID_NODE_SIG;
-
tn->num_nodes++;
-
+exit:
spin_unlock_bh(&tn->node_list_lock);
return n_ptr;
}
l_ptr->reset_checkpoint = l_ptr->next_in_no;
l_ptr->exp_msg_count = 0;
tipc_link_reset_fragments(l_ptr);
+
+ /* Link marked for deletion after failover? => do it now */
+ if (l_ptr->flags & LINK_STOPPED)
+ tipc_link_delete(l_ptr);
}
n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;
struct net *net = sock_net(sk);
struct tipc_msg *mhdr = &tipc_sk(sk)->phdr;
struct sk_buff_head head;
+ struct iov_iter save = msg->msg_iter;
uint mtu;
int rc;
rc = dsz;
break;
}
- if (rc == -EMSGSIZE)
+ if (rc == -EMSGSIZE) {
+ msg->msg_iter = save;
goto new_mtu;
+ }
if (rc != -ELINKCONG)
break;
tipc_sk(sk)->link_cong = 1;
struct sk_buff_head head;
struct sk_buff *skb;
struct tipc_name_seq *seq = &dest->addr.nameseq;
+ struct iov_iter save;
u32 mtu;
long timeo;
int rc;
msg_set_hdr_sz(mhdr, BASIC_H_SIZE);
}
+ save = m->msg_iter;
new_mtu:
mtu = tipc_node_get_mtu(net, dnode, tsk->portid);
__skb_queue_head_init(&head);
rc = dsz;
break;
}
- if (rc == -EMSGSIZE)
+ if (rc == -EMSGSIZE) {
+ m->msg_iter = save;
goto new_mtu;
+ }
if (rc != -ELINKCONG)
break;
tsk->link_cong = 1;
long timeo;
u32 dnode;
uint mtu, send, sent = 0;
+ struct iov_iter save;
/* Handle implied connection establishment */
if (unlikely(dest)) {
dnode = tsk_peer_node(tsk);
next:
+ save = m->msg_iter;
mtu = tsk->max_pkt;
send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE);
__skb_queue_head_init(&head);
if (rc == -EMSGSIZE) {
tsk->max_pkt = tipc_node_get_mtu(net, dnode,
portid);
+ m->msg_iter = save;
goto next;
}
if (rc != -ELINKCONG)
struct msghdr *msg,
size_t len)
{
- /* XXX: stripping const */
- return vmci_qpair_enquev(vmci_trans(vsk)->qpair, (struct iovec *)msg->msg_iter.iov, len, 0);
+ return vmci_qpair_enquev(vmci_trans(vsk)->qpair, msg, len, 0);
}
static s64 vmci_transport_stream_has_data(struct vsock_sock *vsk)
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
+ [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
+ [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
};
/* policy for the key attributes */
request->ssids = (void *)&request->channels[n_channels];
request->n_ssids = n_ssids;
if (ie_len) {
- if (request->ssids)
+ if (n_ssids)
request->ie = (void *)(request->ssids + n_ssids);
else
request->ie = (void *)(request->channels + n_channels);
request->n_channels = i;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
+ if (n_ssids) {
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
request->ssids = (void *)&request->channels[n_channels];
request->n_ssids = n_ssids;
if (ie_len) {
- if (request->ssids)
+ if (n_ssids)
request->ie = (void *)(request->ssids + n_ssids);
else
request->ie = (void *)(request->channels + n_channels);
if (n_match_sets) {
if (request->ie)
request->match_sets = (void *)(request->ie + ie_len);
- else if (request->ssids)
+ else if (n_ssids)
request->match_sets =
(void *)(request->ssids + n_ssids);
else
request->n_channels = i;
i = 0;
- if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+ if (n_ssids) {
nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp) {
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
}
}
+ if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY])
+ request->delay =
+ nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
+
request->interval = interval;
request->scan_start = jiffies;
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net *net;
int err;
- u32 pid;
- if (!info->attrs[NL80211_ATTR_PID])
- return -EINVAL;
+ if (info->attrs[NL80211_ATTR_PID]) {
+ u32 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+
+ net = get_net_ns_by_pid(pid);
+ } else if (info->attrs[NL80211_ATTR_NETNS_FD]) {
+ u32 fd = nla_get_u32(info->attrs[NL80211_ATTR_NETNS_FD]);
- pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+ net = get_net_ns_by_fd(fd);
+ } else {
+ return -EINVAL;
+ }
- net = get_net_ns_by_pid(pid);
if (IS_ERR(net))
return PTR_ERR(net);
if (pairwise && !mac_addr)
return -EINVAL;
- /*
- * Disallow pairwise keys with non-zero index unless it's WEP
- * or a vendor specific cipher (because current deployments use
- * pairwise WEP keys with non-zero indices and for vendor specific
- * ciphers this should be validated in the driver or hardware level
- * - but 802.11i clearly specifies to use zero)
- */
- if (pairwise && key_idx &&
- ((params->cipher == WLAN_CIPHER_SUITE_TKIP) ||
- (params->cipher == WLAN_CIPHER_SUITE_CCMP) ||
- (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC)))
- return -EINVAL;
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ /* Disallow pairwise keys with non-zero index unless it's WEP
+ * or a vendor specific cipher (because current deployments use
+ * pairwise WEP keys with non-zero indices and for vendor
+ * specific ciphers this should be validated in the driver or
+ * hardware level - but 802.11i clearly specifies to use zero)
+ */
+ if (pairwise && key_idx)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ /* Disallow BIP (group-only) cipher as pairwise cipher */
+ if (pairwise)
+ return -EINVAL;
+ break;
+ default:
+ break;
+ }
switch (params->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
if (params->key_len != WLAN_KEY_LEN_CCMP)
return -EINVAL;
break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ if (params->key_len != WLAN_KEY_LEN_CCMP_256)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ if (params->key_len != WLAN_KEY_LEN_GCMP)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (params->key_len != WLAN_KEY_LEN_GCMP_256)
+ return -EINVAL;
+ break;
case WLAN_CIPHER_SUITE_WEP104:
if (params->key_len != WLAN_KEY_LEN_WEP104)
return -EINVAL;
if (params->key_len != WLAN_KEY_LEN_AES_CMAC)
return -EINVAL;
break;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ if (params->key_len != WLAN_KEY_LEN_BIP_CMAC_256)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_128)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_256)
+ return -EINVAL;
+ break;
default:
/*
* We don't know anything about this algorithm,
return -EINVAL;
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
if (params->seq_len != 6)
return -EINVAL;
break;