Commit c747583d authored by Inaky Perez-Gonzalez's avatar Inaky Perez-Gonzalez Committed by David S. Miller

wimax/i2400m: implement RX reorder support

Allow the device to give the driver RX data with reorder information.

When that is done, the device will indicate the driver if a packet has
to be held in a (sorted) queue. It will also tell the driver when held
packets have to be released to the OS.

This is done to improve the WiMAX-protocol level retransmission
support when missing frames are detected.

The code docs provide details about the implementation.

In general, this just hooks into the RX path in rx.c; if a packet with
the reorder bit in the RX header is detected, the reorder information
in the header is extracted and one of the four main reorder operations
are executed. In one case (queue) no packet will be delivered to the
networking stack, just queued, whereas in the others (reset, update_ws
and queue_update_ws), queued packet might be delivered depending on
the window start for the specific queue.

The modifications to files other than rx.c are:

- control.c: during device initialization, enable reordering support
  if the rx_reorder_disabled module parameter is not enabled

- driver.c: expose a rx_reorder_disable module parameter and call
  i2400m_rx_setup/release() to initialize/shutdown RX reorder
  support.

- i2400m.h: introduce members in 'struct i2400m' needed for
  implementing reorder support.

- linux/i2400m.h: introduce TLVs, commands and constant definitions
  related to RX reorder

Last but not least, the rx reorder code includes an small circular log
where the last N reorder operations are recorded to be displayed in
case of inconsistency. Otherwise diagnosing issues would be almost
impossible.
Signed-off-by: default avatarInaky Perez-Gonzalez <inaky@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 61b8d268
...@@ -1312,10 +1312,12 @@ int i2400m_dev_initialize(struct i2400m *i2400m) ...@@ -1312,10 +1312,12 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
struct i2400m_tlv_config_idle_parameters idle_params; struct i2400m_tlv_config_idle_parameters idle_params;
struct i2400m_tlv_config_idle_timeout idle_timeout; struct i2400m_tlv_config_idle_timeout idle_timeout;
struct i2400m_tlv_config_d2h_data_format df; struct i2400m_tlv_config_d2h_data_format df;
struct i2400m_tlv_config_dl_host_reorder dlhr;
const struct i2400m_tlv_hdr *args[9]; const struct i2400m_tlv_hdr *args[9];
unsigned argc = 0; unsigned argc = 0;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m); d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
/* Disable idle mode? (enabled by default) */
if (i2400m_idle_mode_disabled) { if (i2400m_idle_mode_disabled) {
if (i2400m_le_v1_3(i2400m)) { if (i2400m_le_v1_3(i2400m)) {
idle_params.hdr.type = idle_params.hdr.type =
...@@ -1335,12 +1337,24 @@ int i2400m_dev_initialize(struct i2400m *i2400m) ...@@ -1335,12 +1337,24 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
} }
} }
if (i2400m_ge_v1_4(i2400m)) { if (i2400m_ge_v1_4(i2400m)) {
/* Enable extended RX data format? */
df.hdr.type = df.hdr.type =
cpu_to_le16(I2400M_TLV_CONFIG_D2H_DATA_FORMAT); cpu_to_le16(I2400M_TLV_CONFIG_D2H_DATA_FORMAT);
df.hdr.length = cpu_to_le16( df.hdr.length = cpu_to_le16(
sizeof(df) - sizeof(df.hdr)); sizeof(df) - sizeof(df.hdr));
df.format = 1; df.format = 1;
args[argc++] = &df.hdr; args[argc++] = &df.hdr;
/* Enable RX data reordering?
* (switch flipped in rx.c:i2400m_rx_setup() after fw upload) */
if (i2400m->rx_reorder) {
dlhr.hdr.type =
cpu_to_le16(I2400M_TLV_CONFIG_DL_HOST_REORDER);
dlhr.hdr.length = cpu_to_le16(
sizeof(dlhr) - sizeof(dlhr.hdr));
dlhr.reorder = 1;
args[argc++] = &dlhr.hdr;
}
} }
result = i2400m_set_init_config(i2400m, args, argc); result = i2400m_set_init_config(i2400m, args, argc);
if (result < 0) if (result < 0)
......
...@@ -76,6 +76,11 @@ MODULE_PARM_DESC(idle_mode_disabled, ...@@ -76,6 +76,11 @@ MODULE_PARM_DESC(idle_mode_disabled,
"If true, the device will not enable idle mode negotiation " "If true, the device will not enable idle mode negotiation "
"with the base station (when connected) to save power."); "with the base station (when connected) to save power.");
int i2400m_rx_reorder_disabled; /* 0 (rx reorder enabled) by default */
module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644);
MODULE_PARM_DESC(rx_reorder_disabled,
"If true, RX reordering will be disabled.");
/** /**
* i2400m_queue_work - schedule work on a i2400m's queue * i2400m_queue_work - schedule work on a i2400m's queue
* *
...@@ -396,6 +401,9 @@ retry: ...@@ -396,6 +401,9 @@ retry:
result = i2400m_tx_setup(i2400m); result = i2400m_tx_setup(i2400m);
if (result < 0) if (result < 0)
goto error_tx_setup; goto error_tx_setup;
result = i2400m_rx_setup(i2400m);
if (result < 0)
goto error_rx_setup;
result = i2400m->bus_dev_start(i2400m); result = i2400m->bus_dev_start(i2400m);
if (result < 0) if (result < 0)
goto error_bus_dev_start; goto error_bus_dev_start;
...@@ -430,6 +438,8 @@ error_fw_check: ...@@ -430,6 +438,8 @@ error_fw_check:
error_create_workqueue: error_create_workqueue:
i2400m->bus_dev_stop(i2400m); i2400m->bus_dev_stop(i2400m);
error_bus_dev_start: error_bus_dev_start:
i2400m_rx_release(i2400m);
error_rx_setup:
i2400m_tx_release(i2400m); i2400m_tx_release(i2400m);
error_tx_setup: error_tx_setup:
error_bootstrap: error_bootstrap:
...@@ -477,6 +487,7 @@ void __i2400m_dev_stop(struct i2400m *i2400m) ...@@ -477,6 +487,7 @@ void __i2400m_dev_stop(struct i2400m *i2400m)
i2400m->ready = 0; i2400m->ready = 0;
destroy_workqueue(i2400m->work_queue); destroy_workqueue(i2400m->work_queue);
i2400m->bus_dev_stop(i2400m); i2400m->bus_dev_stop(i2400m);
i2400m_rx_release(i2400m);
i2400m_tx_release(i2400m); i2400m_tx_release(i2400m);
wimax_state_change(wimax_dev, WIMAX_ST_DOWN); wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m); d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m);
......
...@@ -174,6 +174,7 @@ enum i2400m_reset_type { ...@@ -174,6 +174,7 @@ enum i2400m_reset_type {
}; };
struct i2400m_reset_ctx; struct i2400m_reset_ctx;
struct i2400m_roq;
/** /**
* struct i2400m - descriptor for an Intel 2400m * struct i2400m - descriptor for an Intel 2400m
...@@ -257,6 +258,9 @@ struct i2400m_reset_ctx; ...@@ -257,6 +258,9 @@ struct i2400m_reset_ctx;
* force this to be the first field so that we can get from * force this to be the first field so that we can get from
* netdev_priv() the right pointer. * netdev_priv() the right pointer.
* *
* @rx_reorder: 1 if RX reordering is enabled; this can only be
* set at probe time.
*
* @state: device's state (as reported by it) * @state: device's state (as reported by it)
* *
* @state_wq: waitqueue that is woken up whenever the state changes * @state_wq: waitqueue that is woken up whenever the state changes
...@@ -313,6 +317,12 @@ struct i2400m_reset_ctx; ...@@ -313,6 +317,12 @@ struct i2400m_reset_ctx;
* *
* @rx_size_max: buggest RX message received. * @rx_size_max: buggest RX message received.
* *
* @rx_roq: RX ReOrder queues. (fw >= v1.4) When packets are received
* out of order, the device will ask the driver to hold certain
* packets until the ones that are received out of order can be
* delivered. Then the driver can release them to the host. See
* drivers/net/i2400m/rx.c for details.
*
* @init_mutex: Mutex used for serializing the device bringup * @init_mutex: Mutex used for serializing the device bringup
* sequence; this way if the device reboots in the middle, we * sequence; this way if the device reboots in the middle, we
* don't try to do a bringup again while we are tearing down the * don't try to do a bringup again while we are tearing down the
...@@ -377,6 +387,7 @@ struct i2400m { ...@@ -377,6 +387,7 @@ struct i2400m {
unsigned boot_mode:1; /* is the device in boot mode? */ unsigned boot_mode:1; /* is the device in boot mode? */
unsigned sboot:1; /* signed or unsigned fw boot */ unsigned sboot:1; /* signed or unsigned fw boot */
unsigned ready:1; /* all probing steps done */ unsigned ready:1; /* all probing steps done */
unsigned rx_reorder:1; /* RX reorder is enabled */
u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */
/* typed u8 so debugfs/u8 can tweak */ /* typed u8 so debugfs/u8 can tweak */
enum i2400m_system_state state; enum i2400m_system_state state;
...@@ -405,10 +416,11 @@ struct i2400m { ...@@ -405,10 +416,11 @@ struct i2400m {
unsigned tx_pl_num, tx_pl_max, tx_pl_min, unsigned tx_pl_num, tx_pl_max, tx_pl_min,
tx_num, tx_size_acc, tx_size_min, tx_size_max; tx_num, tx_size_acc, tx_size_min, tx_size_max;
/* RX stats */ /* RX stuff */
spinlock_t rx_lock; /* protect RX state */ spinlock_t rx_lock; /* protect RX state */
unsigned rx_pl_num, rx_pl_max, rx_pl_min, unsigned rx_pl_num, rx_pl_max, rx_pl_min,
rx_num, rx_size_acc, rx_size_min, rx_size_max; rx_num, rx_size_acc, rx_size_min, rx_size_max;
struct i2400m_roq *rx_roq; /* not under rx_lock! */
struct mutex msg_mutex; /* serialize command execution */ struct mutex msg_mutex; /* serialize command execution */
struct completion msg_completion; struct completion msg_completion;
...@@ -442,6 +454,7 @@ void i2400m_init(struct i2400m *i2400m) ...@@ -442,6 +454,7 @@ void i2400m_init(struct i2400m *i2400m)
wimax_dev_init(&i2400m->wimax_dev); wimax_dev_init(&i2400m->wimax_dev);
i2400m->boot_mode = 1; i2400m->boot_mode = 1;
i2400m->rx_reorder = 1;
init_waitqueue_head(&i2400m->state_wq); init_waitqueue_head(&i2400m->state_wq);
spin_lock_init(&i2400m->tx_lock); spin_lock_init(&i2400m->tx_lock);
...@@ -591,6 +604,9 @@ extern int i2400m_tx_setup(struct i2400m *); ...@@ -591,6 +604,9 @@ extern int i2400m_tx_setup(struct i2400m *);
extern void i2400m_wake_tx_work(struct work_struct *); extern void i2400m_wake_tx_work(struct work_struct *);
extern void i2400m_tx_release(struct i2400m *); extern void i2400m_tx_release(struct i2400m *);
extern int i2400m_rx_setup(struct i2400m *);
extern void i2400m_rx_release(struct i2400m *);
extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned,
const void *, int); const void *, int);
extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, extern void i2400m_net_erx(struct i2400m *, struct sk_buff *,
...@@ -788,6 +804,7 @@ void __i2400m_msleep(unsigned ms) ...@@ -788,6 +804,7 @@ void __i2400m_msleep(unsigned ms)
/* Module parameters */ /* Module parameters */
extern int i2400m_idle_mode_disabled; extern int i2400m_idle_mode_disabled;
extern int i2400m_rx_reorder_disabled;
#endif /* #ifndef __I2400M_H__ */ #endif /* #ifndef __I2400M_H__ */
This diff is collapsed.
...@@ -225,15 +225,16 @@ struct i2400m_pl_data_hdr { ...@@ -225,15 +225,16 @@ struct i2400m_pl_data_hdr {
/* /*
* Payload for an extended data packet * Payload for an extended data packet
* *
* New in v1.4 * New in fw v1.4
* *
* @reorder: if this payload has to be reorder or not (and how)
* @cs: the type of data in the packet, as defined per (802.16e * @cs: the type of data in the packet, as defined per (802.16e
* T11.13.19.1). Currently only 2 (IPv4 packet) supported. * T11.13.19.1). Currently only 2 (IPv4 packet) supported.
* *
* This is prefixed to each and every INCOMING DATA packet. * This is prefixed to each and every INCOMING DATA packet.
*/ */
struct i2400m_pl_edata_hdr { struct i2400m_pl_edata_hdr {
__le32 reorder; __le32 reorder; /* bits defined in i2400m_ro */
__u8 cs; __u8 cs;
__u8 reserved[11]; __u8 reserved[11];
} __attribute__((packed)); } __attribute__((packed));
...@@ -243,8 +244,23 @@ enum i2400m_cs { ...@@ -243,8 +244,23 @@ enum i2400m_cs {
I2400M_CS_IPV4 = 2, I2400M_CS_IPV4 = 2,
}; };
enum i2400m_reorder { enum i2400m_ro {
I2400M_REORDER_NEEDED = 0x01, I2400M_RO_NEEDED = 0x01,
I2400M_RO_TYPE = 0x03,
I2400M_RO_TYPE_SHIFT = 1,
I2400M_RO_CIN = 0x0f,
I2400M_RO_CIN_SHIFT = 4,
I2400M_RO_FBN = 0x07ff,
I2400M_RO_FBN_SHIFT = 8,
I2400M_RO_SN = 0x07ff,
I2400M_RO_SN_SHIFT = 21,
};
enum i2400m_ro_type {
I2400M_RO_TYPE_RESET = 0,
I2400M_RO_TYPE_PACKET,
I2400M_RO_TYPE_WS,
I2400M_RO_TYPE_PACKET_WS,
}; };
...@@ -410,6 +426,7 @@ enum i2400m_tlv { ...@@ -410,6 +426,7 @@ enum i2400m_tlv {
I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601, I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601,
I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611, I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611,
I2400M_TLV_CONFIG_D2H_DATA_FORMAT = 614, I2400M_TLV_CONFIG_D2H_DATA_FORMAT = 614,
I2400M_TLV_CONFIG_DL_HOST_REORDER = 615,
}; };
...@@ -553,5 +570,12 @@ struct i2400m_tlv_config_d2h_data_format { ...@@ -553,5 +570,12 @@ struct i2400m_tlv_config_d2h_data_format {
__u8 reserved[3]; __u8 reserved[3];
} __attribute__((packed)); } __attribute__((packed));
/* New in v1.4 */
struct i2400m_tlv_config_dl_host_reorder {
struct i2400m_tlv_hdr hdr;
__u8 reorder; /* 0 disabled, 1 enabled */
__u8 reserved[3];
} __attribute__((packed));
#endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */ #endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment