From e3453f6342110d60edb37be92c4a4f668ca8b0c4 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Thu, 18 Jun 2009 07:03:47 +0000 Subject: pegasus usb-net: Fix endianness bugs This fixes various endianness bugs. Some harmless and some real ones. This is tested on a PowerPC-64 machine. Signed-off-by: Michael Buesch Cc: Stable Signed-off-by: David S. Miller --- drivers/net/usb/pegasus.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 2138535f233..73acbd244aa 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -297,7 +297,7 @@ static int update_eth_regs_async(pegasus_t * pegasus) pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; - pegasus->dr.wValue = 0; + pegasus->dr.wValue = cpu_to_le16(0); pegasus->dr.wIndex = cpu_to_le16(EthCtrl0); pegasus->dr.wLength = cpu_to_le16(3); pegasus->ctrl_urb->transfer_buffer_length = 3; @@ -446,11 +446,12 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) int i; __u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE }; int ret; + __le16 le_data = cpu_to_le16(data); set_registers(pegasus, EpromOffset, 4, d); enable_eprom_write(pegasus); set_register(pegasus, EpromOffset, index); - set_registers(pegasus, EpromData, 2, &data); + set_registers(pegasus, EpromData, 2, &le_data); set_register(pegasus, EpromCtrl, EPROM_WRITE); for (i = 0; i < REG_TIMEOUT; i++) { @@ -923,29 +924,32 @@ static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) static inline void disable_net_traffic(pegasus_t * pegasus) { - int tmp = 0; + __le16 tmp = cpu_to_le16(0); - set_registers(pegasus, EthCtrl0, 2, &tmp); + set_registers(pegasus, EthCtrl0, sizeof(tmp), &tmp); } static inline void get_interrupt_interval(pegasus_t * pegasus) { - __u8 data[2]; + u16 data; + u8 interval; - read_eprom_word(pegasus, 4, (__u16 *) data); + read_eprom_word(pegasus, 4, &data); + interval = data >> 8; if (pegasus->usb->speed != USB_SPEED_HIGH) { - if (data[1] < 0x80) { + if (interval < 0x80) { if (netif_msg_timer(pegasus)) dev_info(&pegasus->intf->dev, "intr interval " "changed from %ums to %ums\n", - data[1], 0x80); - data[1] = 0x80; + interval, 0x80); + interval = 0x80; + data = (data & 0x00FF) | ((u16)interval << 8); #ifdef PEGASUS_WRITE_EEPROM - write_eprom_word(pegasus, 4, *(__u16 *) data); + write_eprom_word(pegasus, 4, data); #endif } } - pegasus->intr_interval = data[1]; + pegasus->intr_interval = interval; } static void set_carrier(struct net_device *net) @@ -1299,7 +1303,8 @@ static int pegasus_blacklisted(struct usb_device *udev) /* Special quirk to keep the driver from handling the Belkin Bluetooth * dongle which happens to have the same ID. */ - if ((udd->idVendor == VENDOR_BELKIN && udd->idProduct == 0x0121) && + if ((udd->idVendor == cpu_to_le16(VENDOR_BELKIN)) && + (udd->idProduct == cpu_to_le16(0x0121)) && (udd->bDeviceClass == USB_CLASS_WIRELESS_CONTROLLER) && (udd->bDeviceProtocol == 1)) return 1; -- cgit v1.2.3 From 5fb379ee67a7ec55ff65b467b472f3d69b60ba16 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Thu, 18 Jun 2009 00:02:59 +0000 Subject: be2net: Add MCC queue mechanism for BE cmds Currenlty all cmds use the blocking MCC mbox to post cmds. An mbox cmd is protected via a spin_lock(cmd_lock) and not spin_lock_bh() as it is undesirable to disable BHs while a blocking mbox cmd is in progress (and take long to finish.) This can lockup a cmd in progress in process context. Instead cmds that may be called in BH context must use the MCC queue to post cmds. The cmd completions are rcvd in a separate completion queue and the events are placed in the tx-event queue. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/benet/be.h | 95 ++++++++++------ drivers/net/benet/be_cmds.c | 258 ++++++++++++++++++++++++++++++++++---------- drivers/net/benet/be_cmds.h | 40 ++++++- drivers/net/benet/be_hw.h | 8 +- drivers/net/benet/be_main.c | 215 ++++++++++++++++++++++++------------ 5 files changed, 455 insertions(+), 161 deletions(-) (limited to 'drivers') diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index b4bb06fdf30..ef5be133ce6 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -65,7 +65,7 @@ static inline char *nic_name(struct pci_dev *pdev) #define TX_CQ_LEN 1024 #define RX_Q_LEN 1024 /* Does not support any other value */ #define RX_CQ_LEN 1024 -#define MCC_Q_LEN 64 /* total size not to exceed 8 pages */ +#define MCC_Q_LEN 128 /* total size not to exceed 8 pages */ #define MCC_CQ_LEN 256 #define BE_NAPI_WEIGHT 64 @@ -91,6 +91,61 @@ struct be_queue_info { atomic_t used; /* Number of valid elements in the queue */ }; +static inline u32 MODULO(u16 val, u16 limit) +{ + BUG_ON(limit & (limit - 1)); + return val & (limit - 1); +} + +static inline void index_adv(u16 *index, u16 val, u16 limit) +{ + *index = MODULO((*index + val), limit); +} + +static inline void index_inc(u16 *index, u16 limit) +{ + *index = MODULO((*index + 1), limit); +} + +static inline void *queue_head_node(struct be_queue_info *q) +{ + return q->dma_mem.va + q->head * q->entry_size; +} + +static inline void *queue_tail_node(struct be_queue_info *q) +{ + return q->dma_mem.va + q->tail * q->entry_size; +} + +static inline void queue_head_inc(struct be_queue_info *q) +{ + index_inc(&q->head, q->len); +} + +static inline void queue_tail_inc(struct be_queue_info *q) +{ + index_inc(&q->tail, q->len); +} + + +struct be_eq_obj { + struct be_queue_info q; + char desc[32]; + + /* Adaptive interrupt coalescing (AIC) info */ + bool enable_aic; + u16 min_eqd; /* in usecs */ + u16 max_eqd; /* in usecs */ + u16 cur_eqd; /* in usecs */ + + struct napi_struct napi; +}; + +struct be_mcc_obj { + struct be_queue_info q; + struct be_queue_info cq; +}; + struct be_ctrl_info { u8 __iomem *csr; u8 __iomem *db; /* Door Bell */ @@ -98,11 +153,16 @@ struct be_ctrl_info { int pci_func; /* Mbox used for cmd request/response */ - spinlock_t cmd_lock; /* For serializing cmds to BE card */ + spinlock_t mbox_lock; /* For serializing mbox cmds to BE card */ struct be_dma_mem mbox_mem; /* Mbox mem is adjusted to align to 16 bytes. The allocated addr * is stored for freeing purpose */ struct be_dma_mem mbox_mem_alloced; + + /* MCC Rings */ + struct be_mcc_obj mcc_obj; + spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */ + spinlock_t mcc_cq_lock; }; #include "be_cmds.h" @@ -150,19 +210,6 @@ struct be_stats_obj { struct be_dma_mem cmd; }; -struct be_eq_obj { - struct be_queue_info q; - char desc[32]; - - /* Adaptive interrupt coalescing (AIC) info */ - bool enable_aic; - u16 min_eqd; /* in usecs */ - u16 max_eqd; /* in usecs */ - u16 cur_eqd; /* in usecs */ - - struct napi_struct napi; -}; - struct be_tx_obj { struct be_queue_info q; struct be_queue_info cq; @@ -235,22 +282,6 @@ extern struct ethtool_ops be_ethtool_ops; #define BE_SET_NETDEV_OPS(netdev, ops) (netdev->netdev_ops = ops) -static inline u32 MODULO(u16 val, u16 limit) -{ - BUG_ON(limit & (limit - 1)); - return val & (limit - 1); -} - -static inline void index_adv(u16 *index, u16 val, u16 limit) -{ - *index = MODULO((*index + val), limit); -} - -static inline void index_inc(u16 *index, u16 limit) -{ - *index = MODULO((*index + 1), limit); -} - #define PAGE_SHIFT_4K 12 #define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K) @@ -339,4 +370,6 @@ static inline u8 is_udp_pkt(struct sk_buff *skb) return val; } +extern void be_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, + u16 num_popped); #endif /* BE_H */ diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index d444aed962b..f1ec191f0c0 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -17,6 +17,90 @@ #include "be.h" +void be_mcc_notify(struct be_ctrl_info *ctrl) +{ + struct be_queue_info *mccq = &ctrl->mcc_obj.q; + u32 val = 0; + + val |= mccq->id & DB_MCCQ_RING_ID_MASK; + val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT; + iowrite32(val, ctrl->db + DB_MCCQ_OFFSET); +} + +/* To check if valid bit is set, check the entire word as we don't know + * the endianness of the data (old entry is host endian while a new entry is + * little endian) */ +static inline bool be_mcc_compl_is_new(struct be_mcc_cq_entry *compl) +{ + if (compl->flags != 0) { + compl->flags = le32_to_cpu(compl->flags); + BUG_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0); + return true; + } else { + return false; + } +} + +/* Need to reset the entire word that houses the valid bit */ +static inline void be_mcc_compl_use(struct be_mcc_cq_entry *compl) +{ + compl->flags = 0; +} + +static int be_mcc_compl_process(struct be_ctrl_info *ctrl, + struct be_mcc_cq_entry *compl) +{ + u16 compl_status, extd_status; + + /* Just swap the status to host endian; mcc tag is opaquely copied + * from mcc_wrb */ + be_dws_le_to_cpu(compl, 4); + + compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) & + CQE_STATUS_COMPL_MASK; + if (compl_status != MCC_STATUS_SUCCESS) { + extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) & + CQE_STATUS_EXTD_MASK; + printk(KERN_WARNING DRV_NAME + " error in cmd completion: status(compl/extd)=%d/%d\n", + compl_status, extd_status); + return -1; + } + return 0; +} + + +static struct be_mcc_cq_entry *be_mcc_compl_get(struct be_ctrl_info *ctrl) +{ + struct be_queue_info *mcc_cq = &ctrl->mcc_obj.cq; + struct be_mcc_cq_entry *compl = queue_tail_node(mcc_cq); + + if (be_mcc_compl_is_new(compl)) { + queue_tail_inc(mcc_cq); + return compl; + } + return NULL; +} + +void be_process_mcc(struct be_ctrl_info *ctrl) +{ + struct be_mcc_cq_entry *compl; + int num = 0; + + spin_lock_bh(&ctrl->mcc_cq_lock); + while ((compl = be_mcc_compl_get(ctrl))) { + if (!(compl->flags & CQE_FLAGS_ASYNC_MASK)) { + be_mcc_compl_process(ctrl, compl); + atomic_dec(&ctrl->mcc_obj.q.used); + } + be_mcc_compl_use(compl); + num++; + } + if (num) + be_cq_notify(ctrl, ctrl->mcc_obj.cq.id, true, num); + spin_unlock_bh(&ctrl->mcc_cq_lock); +} + static int be_mbox_db_ready_wait(void __iomem *db) { int cnt = 0, wait = 5; @@ -44,11 +128,11 @@ static int be_mbox_db_ready_wait(void __iomem *db) /* * Insert the mailbox address into the doorbell in two steps + * Polls on the mbox doorbell till a command completion (or a timeout) occurs */ static int be_mbox_db_ring(struct be_ctrl_info *ctrl) { int status; - u16 compl_status, extd_status; u32 val = 0; void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET; struct be_dma_mem *mbox_mem = &ctrl->mbox_mem; @@ -79,24 +163,17 @@ static int be_mbox_db_ring(struct be_ctrl_info *ctrl) if (status != 0) return status; - /* compl entry has been made now */ - be_dws_le_to_cpu(cqe, sizeof(*cqe)); - if (!(cqe->flags & CQE_FLAGS_VALID_MASK)) { - printk(KERN_WARNING DRV_NAME ": ERROR invalid mbox compl\n"); + /* A cq entry has been made now */ + if (be_mcc_compl_is_new(cqe)) { + status = be_mcc_compl_process(ctrl, &mbox->cqe); + be_mcc_compl_use(cqe); + if (status) + return status; + } else { + printk(KERN_WARNING DRV_NAME "invalid mailbox completion\n"); return -1; } - - compl_status = (cqe->status >> CQE_STATUS_COMPL_SHIFT) & - CQE_STATUS_COMPL_MASK; - if (compl_status != MCC_STATUS_SUCCESS) { - extd_status = (cqe->status >> CQE_STATUS_EXTD_SHIFT) & - CQE_STATUS_EXTD_MASK; - printk(KERN_WARNING DRV_NAME - ": ERROR in cmd compl. status(compl/extd)=%d/%d\n", - compl_status, extd_status); - } - - return compl_status; + return 0; } static int be_POST_stage_get(struct be_ctrl_info *ctrl, u16 *stage) @@ -235,6 +312,18 @@ static inline struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem) return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; } +static inline struct be_mcc_wrb *wrb_from_mcc(struct be_queue_info *mccq) +{ + struct be_mcc_wrb *wrb = NULL; + if (atomic_read(&mccq->used) < mccq->len) { + wrb = queue_head_node(mccq); + queue_head_inc(mccq); + atomic_inc(&mccq->used); + memset(wrb, 0, sizeof(*wrb)); + } + return wrb; +} + int be_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay) { @@ -244,7 +333,7 @@ int be_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem = &eq->dma_mem; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -272,7 +361,7 @@ int be_cmd_eq_create(struct be_ctrl_info *ctrl, eq->id = le16_to_cpu(resp->eq_id); eq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -284,7 +373,7 @@ int be_cmd_mac_addr_query(struct be_ctrl_info *ctrl, u8 *mac_addr, struct be_cmd_resp_mac_query *resp = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -304,7 +393,7 @@ int be_cmd_mac_addr_query(struct be_ctrl_info *ctrl, u8 *mac_addr, if (!status) memcpy(mac_addr, resp->mac.addr, ETH_ALEN); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -315,7 +404,7 @@ int be_cmd_pmac_add(struct be_ctrl_info *ctrl, u8 *mac_addr, struct be_cmd_req_pmac_add *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -332,7 +421,7 @@ int be_cmd_pmac_add(struct be_ctrl_info *ctrl, u8 *mac_addr, *pmac_id = le32_to_cpu(resp->pmac_id); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -342,7 +431,7 @@ int be_cmd_pmac_del(struct be_ctrl_info *ctrl, u32 if_id, u32 pmac_id) struct be_cmd_req_pmac_del *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -354,7 +443,7 @@ int be_cmd_pmac_del(struct be_ctrl_info *ctrl, u32 if_id, u32 pmac_id) req->pmac_id = cpu_to_le32(pmac_id); status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -370,7 +459,7 @@ int be_cmd_cq_create(struct be_ctrl_info *ctrl, void *ctxt = &req->context; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -388,7 +477,7 @@ int be_cmd_cq_create(struct be_ctrl_info *ctrl, AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts); AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1); AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id); - AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 0); + AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1); AMAP_SET_BITS(struct amap_cq_context, func, ctxt, ctrl->pci_func); be_dws_cpu_to_le(ctxt, sizeof(req->context)); @@ -399,7 +488,56 @@ int be_cmd_cq_create(struct be_ctrl_info *ctrl, cq->id = le16_to_cpu(resp->cq_id); cq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); + + return status; +} + +static u32 be_encoded_q_len(int q_len) +{ + u32 len_encoded = fls(q_len); /* log2(len) + 1 */ + if (len_encoded == 16) + len_encoded = 0; + return len_encoded; +} + +int be_cmd_mccq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *mccq, + struct be_queue_info *cq) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_mcc_create *req = embedded_payload(wrb); + struct be_dma_mem *q_mem = &mccq->dma_mem; + void *ctxt = &req->context; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_MCC_CREATE, sizeof(*req)); + + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + + AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt, ctrl->pci_func); + AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1); + AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt, + be_encoded_q_len(mccq->len)); + AMAP_SET_BITS(struct amap_mcc_context, cq_id, ctxt, cq->id); + + be_dws_cpu_to_le(ctxt, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_db_ring(ctrl); + if (!status) { + struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb); + mccq->id = le16_to_cpu(resp->id); + mccq->created = true; + } + spin_unlock(&ctrl->mbox_lock); return status; } @@ -415,7 +553,7 @@ int be_cmd_txq_create(struct be_ctrl_info *ctrl, int status; u32 len_encoded; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -446,7 +584,7 @@ int be_cmd_txq_create(struct be_ctrl_info *ctrl, txq->id = le16_to_cpu(resp->cid); txq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -460,7 +598,7 @@ int be_cmd_rxq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem = &rxq->dma_mem; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -482,7 +620,7 @@ int be_cmd_rxq_create(struct be_ctrl_info *ctrl, rxq->id = le16_to_cpu(resp->id); rxq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -496,7 +634,7 @@ int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, u8 subsys = 0, opcode = 0; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -518,6 +656,10 @@ int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, subsys = CMD_SUBSYSTEM_ETH; opcode = OPCODE_ETH_RX_DESTROY; break; + case QTYPE_MCCQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_MCC_DESTROY; + break; default: printk(KERN_WARNING DRV_NAME ":bad Q type in Q destroy cmd\n"); status = -1; @@ -528,7 +670,7 @@ int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, status = be_mbox_db_ring(ctrl); err: - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -541,7 +683,7 @@ int be_cmd_if_create(struct be_ctrl_info *ctrl, u32 flags, u8 *mac, struct be_cmd_req_if_create *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -562,7 +704,7 @@ int be_cmd_if_create(struct be_ctrl_info *ctrl, u32 flags, u8 *mac, *pmac_id = le32_to_cpu(resp->pmac_id); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -572,7 +714,7 @@ int be_cmd_if_destroy(struct be_ctrl_info *ctrl, u32 interface_id) struct be_cmd_req_if_destroy *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -583,7 +725,7 @@ int be_cmd_if_destroy(struct be_ctrl_info *ctrl, u32 interface_id) req->interface_id = cpu_to_le32(interface_id); status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -598,7 +740,7 @@ int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd) struct be_sge *sge = nonembedded_sgl(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); memset(req, 0, sizeof(*req)); @@ -617,7 +759,7 @@ int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd) be_dws_le_to_cpu(&resp->hw_stats, sizeof(resp->hw_stats)); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -628,7 +770,7 @@ int be_cmd_link_status_query(struct be_ctrl_info *ctrl, struct be_cmd_req_link_status *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -646,7 +788,7 @@ int be_cmd_link_status_query(struct be_ctrl_info *ctrl, link->speed = PHY_LINK_SPEED_ZERO; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -656,7 +798,7 @@ int be_cmd_get_fw_ver(struct be_ctrl_info *ctrl, char *fw_ver) struct be_cmd_req_get_fw_version *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -670,7 +812,7 @@ int be_cmd_get_fw_ver(struct be_ctrl_info *ctrl, char *fw_ver) strncpy(fw_ver, resp->firmware_version_string, FW_VER_LEN); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -681,7 +823,7 @@ int be_cmd_modify_eqd(struct be_ctrl_info *ctrl, u32 eq_id, u32 eqd) struct be_cmd_req_modify_eq_delay *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -696,7 +838,7 @@ int be_cmd_modify_eqd(struct be_ctrl_info *ctrl, u32 eq_id, u32 eqd) status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -707,7 +849,7 @@ int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, u16 *vtag_array, struct be_cmd_req_vlan_config *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -726,7 +868,7 @@ int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, u16 *vtag_array, status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -736,7 +878,7 @@ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) struct be_cmd_req_promiscuous_config *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -751,7 +893,7 @@ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -762,7 +904,7 @@ int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, struct be_cmd_req_mcast_mac_config *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -780,7 +922,7 @@ int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -790,7 +932,7 @@ int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc) struct be_cmd_req_set_flow_control *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -804,7 +946,7 @@ int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc) status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -814,7 +956,7 @@ int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, u32 *tx_fc, u32 *rx_fc) struct be_cmd_req_get_flow_control *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -831,7 +973,7 @@ int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, u32 *tx_fc, u32 *rx_fc) *rx_fc = le16_to_cpu(resp->rx_flow_control); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -841,7 +983,7 @@ int be_cmd_query_fw_cfg(struct be_ctrl_info *ctrl, u32 *port_num) struct be_cmd_req_query_fw_cfg *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -856,6 +998,6 @@ int be_cmd_query_fw_cfg(struct be_ctrl_info *ctrl, u32 *port_num) *port_num = le32_to_cpu(resp->phys_port); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index e499e2d5b8c..0a9189defc2 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -101,6 +101,7 @@ struct be_mcc_mailbox { #define OPCODE_COMMON_FIRMWARE_CONFIG 42 #define OPCODE_COMMON_NTWK_INTERFACE_CREATE 50 #define OPCODE_COMMON_NTWK_INTERFACE_DESTROY 51 +#define OPCODE_COMMON_MCC_DESTROY 53 #define OPCODE_COMMON_CQ_DESTROY 54 #define OPCODE_COMMON_EQ_DESTROY 55 #define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58 @@ -269,6 +270,38 @@ struct be_cmd_resp_cq_create { u16 rsvd0; } __packed; +/******************** Create MCCQ ***************************/ +/* Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field */ +struct amap_mcc_context { + u8 con_index[14]; + u8 rsvd0[2]; + u8 ring_size[4]; + u8 fetch_wrb; + u8 fetch_r2t; + u8 cq_id[10]; + u8 prod_index[14]; + u8 fid[8]; + u8 pdid[9]; + u8 valid; + u8 rsvd1[32]; + u8 rsvd2[32]; +} __packed; + +struct be_cmd_req_mcc_create { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 rsvd0; + u8 context[sizeof(struct amap_mcc_context) / 8]; + struct phys_addr pages[8]; +} __packed; + +struct be_cmd_resp_mcc_create { + struct be_cmd_resp_hdr hdr; + u16 id; + u16 rsvd0; +} __packed; + /******************** Create TxQ ***************************/ #define BE_ETH_TX_RING_TYPE_STANDARD 2 #define BE_ULP1_NUM 1 @@ -341,7 +374,8 @@ enum { QTYPE_EQ = 1, QTYPE_CQ, QTYPE_TXQ, - QTYPE_RXQ + QTYPE_RXQ, + QTYPE_MCCQ }; struct be_cmd_req_q_destroy { @@ -657,6 +691,9 @@ extern int be_cmd_cq_create(struct be_ctrl_info *ctrl, struct be_queue_info *cq, struct be_queue_info *eq, bool sol_evts, bool no_delay, int num_cqe_dma_coalesce); +extern int be_cmd_mccq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *mccq, + struct be_queue_info *cq); extern int be_cmd_txq_create(struct be_ctrl_info *ctrl, struct be_queue_info *txq, struct be_queue_info *cq); @@ -686,3 +723,4 @@ extern int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, extern int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, u32 *tx_fc, u32 *rx_fc); extern int be_cmd_query_fw_cfg(struct be_ctrl_info *ctrl, u32 *port_num); +extern void be_process_mcc(struct be_ctrl_info *ctrl); diff --git a/drivers/net/benet/be_hw.h b/drivers/net/benet/be_hw.h index b132aa4893c..b02e805c1db 100644 --- a/drivers/net/benet/be_hw.h +++ b/drivers/net/benet/be_hw.h @@ -61,7 +61,7 @@ /* Clear the interrupt for this eq */ #define DB_EQ_CLR_SHIFT (9) /* bit 9 */ /* Must be 1 */ -#define DB_EQ_EVNT_SHIFT (10) /* bit 10 */ +#define DB_EQ_EVNT_SHIFT (10) /* bit 10 */ /* Number of event entries processed */ #define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */ /* Rearm bit */ @@ -88,6 +88,12 @@ /* Number of rx frags posted */ #define DB_RQ_NUM_POSTED_SHIFT (24) /* bits 24 - 31 */ +/********** MCC door bell ************/ +#define DB_MCCQ_OFFSET 0x140 +#define DB_MCCQ_RING_ID_MASK 0x7FF /* bits 0 - 10 */ +/* Number of entries posted */ +#define DB_MCCQ_NUM_POSTED_SHIFT (16) /* bits 16 - 29 */ + /* * BE descriptors: host memory data structures whose formats * are hardwired in BE silicon. diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 66bb56874d9..a4ce80e776b 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -60,26 +60,6 @@ static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q, return 0; } -static inline void *queue_head_node(struct be_queue_info *q) -{ - return q->dma_mem.va + q->head * q->entry_size; -} - -static inline void *queue_tail_node(struct be_queue_info *q) -{ - return q->dma_mem.va + q->tail * q->entry_size; -} - -static inline void queue_head_inc(struct be_queue_info *q) -{ - index_inc(&q->head, q->len); -} - -static inline void queue_tail_inc(struct be_queue_info *q) -{ - index_inc(&q->tail, q->len); -} - static void be_intr_set(struct be_ctrl_info *ctrl, bool enable) { u8 __iomem *addr = ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET; @@ -127,7 +107,7 @@ static void be_eq_notify(struct be_ctrl_info *ctrl, u16 qid, iowrite32(val, ctrl->db + DB_EQ_OFFSET); } -static void be_cq_notify(struct be_ctrl_info *ctrl, u16 qid, +void be_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, u16 num_popped) { u32 val = 0; @@ -960,10 +940,8 @@ static void be_post_rx_frags(struct be_adapter *adapter) return; } -static struct be_eth_tx_compl * -be_tx_compl_get(struct be_adapter *adapter) +static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq) { - struct be_queue_info *tx_cq = &adapter->tx_obj.cq; struct be_eth_tx_compl *txcp = queue_tail_node(tx_cq); if (txcp->dw[offsetof(struct amap_eth_tx_compl, valid) / 32] == 0) @@ -1051,6 +1029,59 @@ static void be_tx_q_clean(struct be_adapter *adapter) } } +static void be_mcc_queues_destroy(struct be_adapter *adapter) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &adapter->ctrl; + + q = &ctrl->mcc_obj.q; + if (q->created) + be_cmd_q_destroy(ctrl, q, QTYPE_MCCQ); + be_queue_free(adapter, q); + + q = &ctrl->mcc_obj.cq; + if (q->created) + be_cmd_q_destroy(ctrl, q, QTYPE_CQ); + be_queue_free(adapter, q); +} + +/* Must be called only after TX qs are created as MCC shares TX EQ */ +static int be_mcc_queues_create(struct be_adapter *adapter) +{ + struct be_queue_info *q, *cq; + struct be_ctrl_info *ctrl = &adapter->ctrl; + + /* Alloc MCC compl queue */ + cq = &ctrl->mcc_obj.cq; + if (be_queue_alloc(adapter, cq, MCC_CQ_LEN, + sizeof(struct be_mcc_cq_entry))) + goto err; + + /* Ask BE to create MCC compl queue; share TX's eq */ + if (be_cmd_cq_create(ctrl, cq, &adapter->tx_eq.q, false, true, 0)) + goto mcc_cq_free; + + /* Alloc MCC queue */ + q = &ctrl->mcc_obj.q; + if (be_queue_alloc(adapter, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb))) + goto mcc_cq_destroy; + + /* Ask BE to create MCC queue */ + if (be_cmd_mccq_create(ctrl, q, cq)) + goto mcc_q_free; + + return 0; + +mcc_q_free: + be_queue_free(adapter, q); +mcc_cq_destroy: + be_cmd_q_destroy(ctrl, cq, QTYPE_CQ); +mcc_cq_free: + be_queue_free(adapter, cq); +err: + return -1; +} + static void be_tx_queues_destroy(struct be_adapter *adapter) { struct be_queue_info *q; @@ -1263,7 +1294,7 @@ static irqreturn_t be_msix_rx(int irq, void *dev) return IRQ_HANDLED; } -static irqreturn_t be_msix_tx(int irq, void *dev) +static irqreturn_t be_msix_tx_mcc(int irq, void *dev) { struct be_adapter *adapter = dev; @@ -1324,40 +1355,51 @@ int be_poll_rx(struct napi_struct *napi, int budget) return work_done; } -/* For TX we don't honour budget; consume everything */ -int be_poll_tx(struct napi_struct *napi, int budget) +void be_process_tx(struct be_adapter *adapter) { - struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi); - struct be_adapter *adapter = - container_of(tx_eq, struct be_adapter, tx_eq); - struct be_tx_obj *tx_obj = &adapter->tx_obj; - struct be_queue_info *tx_cq = &tx_obj->cq; - struct be_queue_info *txq = &tx_obj->q; + struct be_queue_info *txq = &adapter->tx_obj.q; + struct be_queue_info *tx_cq = &adapter->tx_obj.cq; struct be_eth_tx_compl *txcp; u32 num_cmpl = 0; u16 end_idx; - while ((txcp = be_tx_compl_get(adapter))) { + while ((txcp = be_tx_compl_get(tx_cq))) { end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, wrb_index, txcp); be_tx_compl_process(adapter, end_idx); num_cmpl++; } - /* As Tx wrbs have been freed up, wake up netdev queue if - * it was stopped due to lack of tx wrbs. - */ - if (netif_queue_stopped(adapter->netdev) && + if (num_cmpl) { + be_cq_notify(&adapter->ctrl, tx_cq->id, true, num_cmpl); + + /* As Tx wrbs have been freed up, wake up netdev queue if + * it was stopped due to lack of tx wrbs. + */ + if (netif_queue_stopped(adapter->netdev) && atomic_read(&txq->used) < txq->len / 2) { - netif_wake_queue(adapter->netdev); + netif_wake_queue(adapter->netdev); + } + + drvr_stats(adapter)->be_tx_events++; + drvr_stats(adapter)->be_tx_compl += num_cmpl; } +} + +/* As TX and MCC share the same EQ check for both TX and MCC completions. + * For TX/MCC we don't honour budget; consume everything + */ +static int be_poll_tx_mcc(struct napi_struct *napi, int budget) +{ + struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi); + struct be_adapter *adapter = + container_of(tx_eq, struct be_adapter, tx_eq); napi_complete(napi); - be_cq_notify(&adapter->ctrl, tx_cq->id, true, num_cmpl); + be_process_tx(adapter); - drvr_stats(adapter)->be_tx_events++; - drvr_stats(adapter)->be_tx_compl += num_cmpl; + be_process_mcc(&adapter->ctrl); return 1; } @@ -1419,7 +1461,7 @@ static int be_msix_register(struct be_adapter *adapter) sprintf(tx_eq->desc, "%s-tx", netdev->name); vec = be_msix_vec_get(adapter, tx_eq->q.id); - status = request_irq(vec, be_msix_tx, 0, tx_eq->desc, adapter); + status = request_irq(vec, be_msix_tx_mcc, 0, tx_eq->desc, adapter); if (status) goto err; @@ -1495,6 +1537,34 @@ static int be_open(struct net_device *netdev) struct be_ctrl_info *ctrl = &adapter->ctrl; struct be_eq_obj *rx_eq = &adapter->rx_eq; struct be_eq_obj *tx_eq = &adapter->tx_eq; + + /* First time posting */ + be_post_rx_frags(adapter); + + napi_enable(&rx_eq->napi); + napi_enable(&tx_eq->napi); + + be_irq_register(adapter); + + be_intr_set(ctrl, true); + + /* The evt queues are created in unarmed state; arm them */ + be_eq_notify(ctrl, rx_eq->q.id, true, false, 0); + be_eq_notify(ctrl, tx_eq->q.id, true, false, 0); + + /* Rx compl queue may be in unarmed state; rearm it */ + be_cq_notify(ctrl, adapter->rx_obj.cq.id, true, 0); + + be_link_status_update(adapter); + + schedule_delayed_work(&adapter->work, msecs_to_jiffies(100)); + return 0; +} + +static int be_setup(struct be_adapter *adapter) +{ + struct be_ctrl_info *ctrl = &adapter->ctrl; + struct net_device *netdev = adapter->netdev; u32 if_flags; int status; @@ -1521,29 +1591,14 @@ static int be_open(struct net_device *netdev) if (status != 0) goto tx_qs_destroy; - /* First time posting */ - be_post_rx_frags(adapter); - - napi_enable(&rx_eq->napi); - napi_enable(&tx_eq->napi); - - be_irq_register(adapter); - - be_intr_set(ctrl, true); - - /* The evt queues are created in the unarmed state; arm them */ - be_eq_notify(ctrl, rx_eq->q.id, true, false, 0); - be_eq_notify(ctrl, tx_eq->q.id, true, false, 0); - - /* The compl queues are created in the unarmed state; arm them */ - be_cq_notify(ctrl, adapter->rx_obj.cq.id, true, 0); - be_cq_notify(ctrl, adapter->tx_obj.cq.id, true, 0); - - be_link_status_update(adapter); + status = be_mcc_queues_create(adapter); + if (status != 0) + goto rx_qs_destroy; - schedule_delayed_work(&adapter->work, msecs_to_jiffies(100)); return 0; +rx_qs_destroy: + be_rx_queues_destroy(adapter); tx_qs_destroy: be_tx_queues_destroy(adapter); if_destroy: @@ -1552,6 +1607,19 @@ do_none: return status; } +static int be_clear(struct be_adapter *adapter) +{ + struct be_ctrl_info *ctrl = &adapter->ctrl; + + be_rx_queues_destroy(adapter); + be_tx_queues_destroy(adapter); + + be_cmd_if_destroy(ctrl, adapter->if_handle); + + be_mcc_queues_destroy(adapter); + return 0; +} + static int be_close(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -1581,10 +1649,6 @@ static int be_close(struct net_device *netdev) napi_disable(&rx_eq->napi); napi_disable(&tx_eq->napi); - be_rx_queues_destroy(adapter); - be_tx_queues_destroy(adapter); - - be_cmd_if_destroy(ctrl, adapter->if_handle); return 0; } @@ -1673,7 +1737,7 @@ static void be_netdev_init(struct net_device *netdev) netif_napi_add(netdev, &adapter->rx_eq.napi, be_poll_rx, BE_NAPI_WEIGHT); - netif_napi_add(netdev, &adapter->tx_eq.napi, be_poll_tx, + netif_napi_add(netdev, &adapter->tx_eq.napi, be_poll_tx_mcc, BE_NAPI_WEIGHT); netif_carrier_off(netdev); @@ -1755,7 +1819,9 @@ static int be_ctrl_init(struct be_adapter *adapter) mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16); mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox)); - spin_lock_init(&ctrl->cmd_lock); + spin_lock_init(&ctrl->mbox_lock); + spin_lock_init(&ctrl->mcc_lock); + spin_lock_init(&ctrl->mcc_cq_lock); val = ioread32(ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET); ctrl->pci_func = (val >> MEMBAR_CTRL_INT_CTRL_PFUNC_SHIFT) & @@ -1793,6 +1859,8 @@ static void __devexit be_remove(struct pci_dev *pdev) unregister_netdev(adapter->netdev); + be_clear(adapter); + be_stats_cleanup(adapter); be_ctrl_cleanup(adapter); @@ -1890,13 +1958,18 @@ static int __devinit be_probe(struct pci_dev *pdev, be_netdev_init(netdev); SET_NETDEV_DEV(netdev, &adapter->pdev->dev); + status = be_setup(adapter); + if (status) + goto stats_clean; status = register_netdev(netdev); if (status != 0) - goto stats_clean; + goto unsetup; dev_info(&pdev->dev, "%s port %d\n", nic_name(pdev), adapter->port_num); return 0; +unsetup: + be_clear(adapter); stats_clean: be_stats_cleanup(adapter); ctrl_clean: @@ -1921,6 +1994,7 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state) if (netif_running(netdev)) { rtnl_lock(); be_close(netdev); + be_clear(adapter); rtnl_unlock(); } @@ -1947,6 +2021,7 @@ static int be_resume(struct pci_dev *pdev) if (netif_running(netdev)) { rtnl_lock(); + be_setup(adapter); be_open(netdev); rtnl_unlock(); } -- cgit v1.2.3 From 6ac7b687cb3acc437a586794949a43f5249956bb Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Thu, 18 Jun 2009 00:05:54 +0000 Subject: be2net: Use MCC queue for cmds that may be called in BH context Currenlty multicast_set and promiscuous_config cmds -- that may be called in BH context -- use the blocking MCC mbox to post cmds. An mbox cmd is protected via a spin_lock(cmd_lock) and not spin_lock_bh() as it is undesirable to disable BHs while a blocking mbox cmd is in progress (and take long to finish.) This can lockup a cmd in progress in process context. So, these two cmds in BH context must use the MCC queue to post cmds. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/benet/be_cmds.c | 69 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index f1ec191f0c0..e4ad5e67fde 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -17,7 +17,7 @@ #include "be.h" -void be_mcc_notify(struct be_ctrl_info *ctrl) +static void be_mcc_notify(struct be_ctrl_info *ctrl) { struct be_queue_info *mccq = &ctrl->mcc_obj.q; u32 val = 0; @@ -101,6 +101,28 @@ void be_process_mcc(struct be_ctrl_info *ctrl) spin_unlock_bh(&ctrl->mcc_cq_lock); } +/* Wait till no more pending mcc requests are present */ +static void be_mcc_wait_compl(struct be_ctrl_info *ctrl) +{ +#define mcc_timeout 50000 /* 5s timeout */ + int i; + for (i = 0; i < mcc_timeout; i++) { + be_process_mcc(ctrl); + if (atomic_read(&ctrl->mcc_obj.q.used) == 0) + break; + udelay(100); + } + if (i == mcc_timeout) + printk(KERN_WARNING DRV_NAME "mcc poll timed out\n"); +} + +/* Notify MCC requests and wait for completion */ +static void be_mcc_notify_wait(struct be_ctrl_info *ctrl) +{ + be_mcc_notify(ctrl); + be_mcc_wait_compl(ctrl); +} + static int be_mbox_db_ready_wait(void __iomem *db) { int cnt = 0, wait = 5; @@ -872,14 +894,18 @@ int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, u16 *vtag_array, return status; } +/* Use MCC for this command as it may be called in BH context */ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) { - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_cmd_req_promiscuous_config *req = embedded_payload(wrb); - int status; + struct be_mcc_wrb *wrb; + struct be_cmd_req_promiscuous_config *req; - spin_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); + spin_lock_bh(&ctrl->mcc_lock); + + wrb = wrb_from_mcc(&ctrl->mcc_obj.q); + BUG_ON(!wrb); + + req = embedded_payload(wrb); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -891,21 +917,29 @@ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) else req->port0_promiscuous = en; - status = be_mbox_db_ring(ctrl); + be_mcc_notify_wait(ctrl); - spin_unlock(&ctrl->mbox_lock); - return status; + spin_unlock_bh(&ctrl->mcc_lock); + return 0; } +/* + * Use MCC for this command as it may be called in BH context + * (mc == NULL) => multicast promiscous + */ int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, u32 num, bool promiscuous) { - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_cmd_req_mcast_mac_config *req = embedded_payload(wrb); - int status; +#define BE_MAX_MC 32 /* set mcast promisc if > 32 */ + struct be_mcc_wrb *wrb; + struct be_cmd_req_mcast_mac_config *req; - spin_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); + spin_lock_bh(&ctrl->mcc_lock); + + wrb = wrb_from_mcc(&ctrl->mcc_obj.q); + BUG_ON(!wrb); + + req = embedded_payload(wrb); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -920,10 +954,11 @@ int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, memcpy(req->mac, mac_table, ETH_ALEN * num); } - status = be_mbox_db_ring(ctrl); + be_mcc_notify_wait(ctrl); - spin_unlock(&ctrl->mbox_lock); - return status; + spin_unlock_bh(&ctrl->mcc_lock); + + return 0; } int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc) -- cgit v1.2.3 From 24307eef74bd38e3fc6a6df8f8a1bfc48967f9f6 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Thu, 18 Jun 2009 00:09:25 +0000 Subject: be2net: cleanup multicast_set cmd to avoid mc_list copy Cleanup multicast_set method to avoid an extra copy of mc_list and unwanted promiscuos sets to BE. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/benet/be.h | 1 + drivers/net/benet/be_cmds.c | 19 +++++++++++------- drivers/net/benet/be_cmds.h | 4 ++-- drivers/net/benet/be_main.c | 49 ++++++++++++++++----------------------------- 4 files changed, 32 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index ef5be133ce6..94b75cb072f 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -274,6 +274,7 @@ struct be_adapter { struct be_link_info link; u32 port_num; + bool promiscuous; }; extern struct ethtool_ops be_ethtool_ops; diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index e4ad5e67fde..4a2e1f518f7 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -927,8 +927,8 @@ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) * Use MCC for this command as it may be called in BH context * (mc == NULL) => multicast promiscous */ -int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, - u32 num, bool promiscuous) +int be_cmd_multicast_set(struct be_ctrl_info *ctrl, u32 if_id, + struct dev_mc_list *mc_list, u32 mc_count) { #define BE_MAX_MC 32 /* set mcast promisc if > 32 */ struct be_mcc_wrb *wrb; @@ -947,11 +947,16 @@ int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, OPCODE_COMMON_NTWK_MULTICAST_SET, sizeof(*req)); req->interface_id = if_id; - req->promiscuous = promiscuous; - if (!promiscuous) { - req->num_mac = cpu_to_le16(num); - if (num) - memcpy(req->mac, mac_table, ETH_ALEN * num); + if (mc_list && mc_count <= BE_MAX_MC) { + int i; + struct dev_mc_list *mc; + + req->num_mac = cpu_to_le16(mc_count); + + for (mc = mc_list, i = 0; mc; mc = mc->next, i++) + memcpy(req->mac[i].byte, mc->dmi_addr, ETH_ALEN); + } else { + req->promiscuous = 1; } be_mcc_notify_wait(ctrl); diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index 0a9189defc2..a567aa437ec 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -716,8 +716,8 @@ extern int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, bool promiscuous); extern int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en); -extern int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, - u8 *mac_table, u32 num, bool promiscuous); +extern int be_cmd_multicast_set(struct be_ctrl_info *ctrl, u32 if_id, + struct dev_mc_list *mc_list, u32 mc_count); extern int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc); extern int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index a4ce80e776b..3dc68034c21 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -549,47 +549,32 @@ static void be_vlan_rem_vid(struct net_device *netdev, u16 vid) be_vid_config(netdev); } -static void be_set_multicast_filter(struct net_device *netdev) +static void be_set_multicast_list(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); - struct dev_mc_list *mc_ptr; - u8 mac_addr[32][ETH_ALEN]; - int i = 0; + struct be_ctrl_info *ctrl = &adapter->ctrl; - if (netdev->flags & IFF_ALLMULTI) { - /* set BE in Multicast promiscuous */ - be_cmd_mcast_mac_set(&adapter->ctrl, - adapter->if_handle, NULL, 0, true); - return; + if (netdev->flags & IFF_PROMISC) { + be_cmd_promiscuous_config(ctrl, adapter->port_num, 1); + adapter->promiscuous = true; + goto done; } - for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { - memcpy(&mac_addr[i][0], mc_ptr->dmi_addr, ETH_ALEN); - if (++i >= 32) { - be_cmd_mcast_mac_set(&adapter->ctrl, - adapter->if_handle, &mac_addr[0][0], i, false); - i = 0; - } - + /* BE was previously in promiscous mode; disable it */ + if (adapter->promiscuous) { + adapter->promiscuous = false; + be_cmd_promiscuous_config(ctrl, adapter->port_num, 0); } - if (i) { - /* reset the promiscuous mode also. */ - be_cmd_mcast_mac_set(&adapter->ctrl, - adapter->if_handle, &mac_addr[0][0], i, false); + if (netdev->flags & IFF_ALLMULTI) { + be_cmd_multicast_set(ctrl, adapter->if_handle, NULL, 0); + goto done; } -} - -static void be_set_multicast_list(struct net_device *netdev) -{ - struct be_adapter *adapter = netdev_priv(netdev); - if (netdev->flags & IFF_PROMISC) { - be_cmd_promiscuous_config(&adapter->ctrl, adapter->port_num, 1); - } else { - be_cmd_promiscuous_config(&adapter->ctrl, adapter->port_num, 0); - be_set_multicast_filter(netdev); - } + be_cmd_multicast_set(ctrl, adapter->if_handle, netdev->mc_list, + netdev->mc_count); +done: + return; } static void be_rx_rate_update(struct be_adapter *adapter) -- cgit v1.2.3 From a8f447bda3ee00e3a3ab080c48db40078ea65221 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Thu, 18 Jun 2009 00:10:27 +0000 Subject: be2net: receive asynchronous link status notifications from BE Rcv and process ansync link status notifications from BE instead of polling for link status in the be_worker thread. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/benet/be.h | 6 +++++- drivers/net/benet/be_cmds.c | 34 +++++++++++++++++++++++++++------- drivers/net/benet/be_cmds.h | 36 +++++++++++++++++++++++++++++------- drivers/net/benet/be_main.c | 37 +++++++++++++++++++------------------ 4 files changed, 80 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index 94b75cb072f..f703758f0a6 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -163,6 +163,10 @@ struct be_ctrl_info { struct be_mcc_obj mcc_obj; spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */ spinlock_t mcc_cq_lock; + + /* MCC Async callback */ + void (*async_cb)(void *adapter, bool link_up); + void *adapter_ctxt; }; #include "be_cmds.h" @@ -272,7 +276,7 @@ struct be_adapter { u32 if_handle; /* Used to configure filtering */ u32 pmac_id; /* MAC addr handle used by BE card */ - struct be_link_info link; + bool link_up; u32 port_num; bool promiscuous; }; diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index 4a2e1f518f7..583517ed56f 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -69,6 +69,20 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl, return 0; } +/* Link state evt is a string of bytes; no need for endian swapping */ +static void be_async_link_state_process(struct be_ctrl_info *ctrl, + struct be_async_event_link_state *evt) +{ + ctrl->async_cb(ctrl->adapter_ctxt, + evt->port_link_status == ASYNC_EVENT_LINK_UP ? true : false); +} + +static inline bool is_link_state_evt(u32 trailer) +{ + return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & + ASYNC_TRAILER_EVENT_CODE_MASK) == + ASYNC_EVENT_CODE_LINK_STATE); +} static struct be_mcc_cq_entry *be_mcc_compl_get(struct be_ctrl_info *ctrl) { @@ -89,7 +103,14 @@ void be_process_mcc(struct be_ctrl_info *ctrl) spin_lock_bh(&ctrl->mcc_cq_lock); while ((compl = be_mcc_compl_get(ctrl))) { - if (!(compl->flags & CQE_FLAGS_ASYNC_MASK)) { + if (compl->flags & CQE_FLAGS_ASYNC_MASK) { + /* Interpret flags as an async trailer */ + BUG_ON(!is_link_state_evt(compl->flags)); + + /* Interpret compl as a async link evt */ + be_async_link_state_process(ctrl, + (struct be_async_event_link_state *) compl); + } else { be_mcc_compl_process(ctrl, compl); atomic_dec(&ctrl->mcc_obj.q.used); } @@ -786,13 +807,15 @@ int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd) } int be_cmd_link_status_query(struct be_ctrl_info *ctrl, - struct be_link_info *link) + bool *link_up) { struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); struct be_cmd_req_link_status *req = embedded_payload(wrb); int status; spin_lock(&ctrl->mbox_lock); + + *link_up = false; memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -803,11 +826,8 @@ int be_cmd_link_status_query(struct be_ctrl_info *ctrl, status = be_mbox_db_ring(ctrl); if (!status) { struct be_cmd_resp_link_status *resp = embedded_payload(wrb); - link->speed = resp->mac_speed; - link->duplex = resp->mac_duplex; - link->fault = resp->mac_fault; - } else { - link->speed = PHY_LINK_SPEED_ZERO; + if (resp->mac_speed != PHY_LINK_SPEED_ZERO) + *link_up = true; } spin_unlock(&ctrl->mbox_lock); diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index a567aa437ec..747626da7b4 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -76,6 +76,34 @@ struct be_mcc_cq_entry { u32 flags; /* dword 3 */ }; +/* When the async bit of mcc_compl is set, the last 4 bytes of + * mcc_compl is interpreted as follows: + */ +#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */ +#define ASYNC_TRAILER_EVENT_CODE_MASK 0xFF +#define ASYNC_EVENT_CODE_LINK_STATE 0x1 +struct be_async_event_trailer { + u32 code; +}; + +enum { + ASYNC_EVENT_LINK_DOWN = 0x0, + ASYNC_EVENT_LINK_UP = 0x1 +}; + +/* When the event code of an async trailer is link-state, the mcc_compl + * must be interpreted as follows + */ +struct be_async_event_link_state { + u8 physical_port; + u8 port_link_status; + u8 port_duplex; + u8 port_speed; + u8 port_fault; + u8 rsvd0[7]; + struct be_async_event_trailer trailer; +} __packed; + struct be_mcc_mailbox { struct be_mcc_wrb wrb; struct be_mcc_cq_entry cqe; @@ -580,12 +608,6 @@ struct be_cmd_req_link_status { u32 rsvd; }; -struct be_link_info { - u8 duplex; - u8 speed; - u8 fault; -}; - enum { PHY_LINK_DUPLEX_NONE = 0x0, PHY_LINK_DUPLEX_HALF = 0x1, @@ -704,7 +726,7 @@ extern int be_cmd_rxq_create(struct be_ctrl_info *ctrl, extern int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, int type); extern int be_cmd_link_status_query(struct be_ctrl_info *ctrl, - struct be_link_info *link); + bool *link_up); extern int be_cmd_reset(struct be_ctrl_info *ctrl); extern int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd); diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 3dc68034c21..66c10c87f51 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -214,28 +214,24 @@ static void netdev_stats_update(struct be_adapter *adapter) dev_stats->tx_window_errors = 0; } -static void be_link_status_update(struct be_adapter *adapter) +void be_link_status_update(void *ctxt, bool link_up) { - struct be_link_info *prev = &adapter->link; - struct be_link_info now = { 0 }; + struct be_adapter *adapter = ctxt; struct net_device *netdev = adapter->netdev; - be_cmd_link_status_query(&adapter->ctrl, &now); - /* If link came up or went down */ - if (now.speed != prev->speed && (now.speed == PHY_LINK_SPEED_ZERO || - prev->speed == PHY_LINK_SPEED_ZERO)) { - if (now.speed == PHY_LINK_SPEED_ZERO) { - netif_stop_queue(netdev); - netif_carrier_off(netdev); - printk(KERN_INFO "%s: Link down\n", netdev->name); - } else { + if (adapter->link_up != link_up) { + if (link_up) { netif_start_queue(netdev); netif_carrier_on(netdev); printk(KERN_INFO "%s: Link up\n", netdev->name); + } else { + netif_stop_queue(netdev); + netif_carrier_off(netdev); + printk(KERN_INFO "%s: Link down\n", netdev->name); } + adapter->link_up = link_up; } - *prev = now; } /* Update the EQ delay n BE based on the RX frags consumed / sec */ @@ -1395,9 +1391,6 @@ static void be_worker(struct work_struct *work) container_of(work, struct be_adapter, work.work); int status; - /* Check link */ - be_link_status_update(adapter); - /* Get Stats */ status = be_cmd_get_stats(&adapter->ctrl, &adapter->stats.cmd); if (!status) @@ -1522,6 +1515,8 @@ static int be_open(struct net_device *netdev) struct be_ctrl_info *ctrl = &adapter->ctrl; struct be_eq_obj *rx_eq = &adapter->rx_eq; struct be_eq_obj *tx_eq = &adapter->tx_eq; + bool link_up; + int status; /* First time posting */ be_post_rx_frags(adapter); @@ -1540,7 +1535,10 @@ static int be_open(struct net_device *netdev) /* Rx compl queue may be in unarmed state; rearm it */ be_cq_notify(ctrl, adapter->rx_obj.cq.id, true, 0); - be_link_status_update(adapter); + status = be_cmd_link_status_query(ctrl, &link_up); + if (status) + return status; + be_link_status_update(adapter, link_up); schedule_delayed_work(&adapter->work, msecs_to_jiffies(100)); return 0; @@ -1617,7 +1615,7 @@ static int be_close(struct net_device *netdev) netif_stop_queue(netdev); netif_carrier_off(netdev); - adapter->link.speed = PHY_LINK_SPEED_ZERO; + adapter->link_up = false; be_intr_set(ctrl, false); @@ -1808,6 +1806,9 @@ static int be_ctrl_init(struct be_adapter *adapter) spin_lock_init(&ctrl->mcc_lock); spin_lock_init(&ctrl->mcc_cq_lock); + ctrl->async_cb = be_link_status_update; + ctrl->adapter_ctxt = adapter; + val = ioread32(ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET); ctrl->pci_func = (val >> MEMBAR_CTRL_INT_CTRL_PFUNC_SHIFT) & MEMBAR_CTRL_INT_CTRL_PFUNC_MASK; -- cgit v1.2.3 From 68924920cb457ac44b14ca38343954bdcee046fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sj=C3=B6quist?= Date: Thu, 18 Jun 2009 01:50:52 +0000 Subject: cdc_ether: additional PID's to the whitelist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds five PID's to the whitelist set of devices. Devices added to the whitelist: Dell Wireless 5530 HSPA Ericsson Mobile Broadband Module variants (F3507g, F3607gw and F3307) Toshiba F3507g Signed-off-by: Jonas Sjöquist Signed-off-by: David S. Miller --- drivers/net/usb/Kconfig | 4 +++- drivers/net/usb/cdc_ether.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 3717569828b..a906d399813 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -169,10 +169,12 @@ config USB_NET_CDCETHER The Linux-USB CDC Ethernet Gadget driver is an open implementation. This driver should work with at least the following devices: + * Dell Wireless 5530 HSPA * Ericsson PipeRider (all variants) + * Ericsson Mobile Broadband Module (all variants) * Motorola (DM100 and SB4100) * Broadcom Cable Modem (reference design) - * Toshiba PCX1100U + * Toshiba (PCX1100U and F3507g) * ... This driver creates an interface named "ethX", where X depends on diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 01fd528306e..4a6aff57940 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -533,6 +533,31 @@ static const struct usb_device_id products [] = { USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1900, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3507g ver. 2 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1902, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3607gw */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1904, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3307 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1906, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Toshiba F3507g */ + USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130b, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Dell F3507g */ + USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8147, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, }, { }, // END }; -- cgit v1.2.3 From 679e8a0f0ae3333e94b1d374d07775fce9066025 Mon Sep 17 00:00:00 2001 From: Andy Gospodarek Date: Thu, 18 Jun 2009 11:57:37 +0000 Subject: e1000e: stop unnecessary polling when using msi-x The last hunk of this commit: commit 12d04a3c12b420f23398b4d650127642469a60a6 Author: Alexander Duyck Date: Wed Mar 25 22:05:03 2009 +0000 e1000e: commonize tx cleanup routine to match e1000 & igb changed the logic for determining if we should call napi_complete or not at then end of a napi poll. If the NIC is using MSI-X with no work to do in ->poll, net_rx_action can just spin indefinitely on older kernels and for 2 jiffies on newer kernels since napi_complete is never called and budget isn't decremented. Discovered and verified while testing driver backport to an older kernel. Signed-off-by: Andy Gospodarek Acked-by: Alexander Duyck Signed-off-by: David S. Miller --- drivers/net/e1000e/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 677f60490f6..679885a122b 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -1997,7 +1997,7 @@ static int e1000_clean(struct napi_struct *napi, int budget) struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter, napi); struct e1000_hw *hw = &adapter->hw; struct net_device *poll_dev = adapter->netdev; - int tx_cleaned = 0, work_done = 0; + int tx_cleaned = 1, work_done = 0; adapter = netdev_priv(poll_dev); -- cgit v1.2.3 From 40c27eeac42600b21d483087ff3885b31e6857c9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2009 03:49:49 +0000 Subject: r8169: remove unused variable all references got removed by 865c652d6be9929927cabdc54b137b7541eb6612 (r8169: remove non-napi code). Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- drivers/net/r8169.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 4e22462684c..4b53b58d75f 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -51,9 +51,6 @@ #define TX_BUFFS_AVAIL(tp) \ (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1) -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static const int max_interrupt_work = 20; - /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static const int multicast_filter_limit = 32; -- cgit v1.2.3 From 6877f54e6a3326c99aaf84b7bff6a3019da0b847 Mon Sep 17 00:00:00 2001 From: Prabhanjan Sarnaik Date: Thu, 18 Jun 2009 11:35:02 +0000 Subject: mv643xx_eth: fix unicast filter programming in promiscuous mode The Unicast Promiscious Mode (UPM) bit in the mv643xx_eth port configuration register doesn't do exactly what its name would suggest: setting this bit merely enables reception of all unicast frames with a destination address that differs from our local MAC address in bits [47:4]. In particular, it doesn't have any effect on unicast frames with a destination address that matches our MAC address in bits [47:4] -- these will still be tested against the 16-entry unicast address filter table. Therefore, if the interface is set to promiscuous mode, just setting the unicast promiscuous bit isn't enough -- we need to set all filter bits in the unicast filter table to 1 as well. Reported-by: Sachin Sanap Signed-off-by: Prabhanjan Sarnaik Tested-by: Siddarth Gore Tested-by: Mahavir Jain Signed-off-by: Lennert Buytenhek Cc: stable@kernel.org Signed-off-by: David S. Miller --- drivers/net/mv643xx_eth.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 745ae8b4a2e..0f32db3e92a 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1750,12 +1750,12 @@ static void mv643xx_eth_program_unicast_filter(struct net_device *dev) uc_addr_set(mp, dev->dev_addr); - port_config = rdlp(mp, PORT_CONFIG); + port_config = rdlp(mp, PORT_CONFIG) & ~UNICAST_PROMISCUOUS_MODE; + nibbles = uc_addr_filter_mask(dev); if (!nibbles) { port_config |= UNICAST_PROMISCUOUS_MODE; - wrlp(mp, PORT_CONFIG, port_config); - return; + nibbles = 0xffff; } for (i = 0; i < 16; i += 4) { @@ -1776,7 +1776,6 @@ static void mv643xx_eth_program_unicast_filter(struct net_device *dev) wrl(mp, off, v); } - port_config &= ~UNICAST_PROMISCUOUS_MODE; wrlp(mp, PORT_CONFIG, port_config); } -- cgit v1.2.3 From 8d96e7960b6b520eb52be6e1eb7c794da5db9555 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 15 Jun 2009 21:36:13 +0200 Subject: iwmc3200wifi: check for iwm_priv_init error We need to check for iwm_priv_init() errors and do proper cleanups. Otherwise we may fail to catch the create_singlethread_workqueue() error which will cause a kernel oops when destroy_workqueue() later. Signed-off-by: Zhu Yi Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/iwm.h | 1 + drivers/net/wireless/iwmc3200wifi/main.c | 10 ++++++++++ drivers/net/wireless/iwmc3200wifi/netdev.c | 19 ++++++++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 635c16ee618..2237448e042 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -317,6 +317,7 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, void iwm_if_free(struct iwm_priv *iwm); int iwm_mode_to_nl80211_iftype(int mode); int iwm_priv_init(struct iwm_priv *iwm); +void iwm_priv_deinit(struct iwm_priv *iwm); void iwm_reset(struct iwm_priv *iwm); void iwm_tx_credit_init_pools(struct iwm_priv *iwm, struct iwm_umac_notif_alive *alive); diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 6a2640f16b6..4d3c423d8ff 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -219,6 +219,16 @@ int iwm_priv_init(struct iwm_priv *iwm) return 0; } +void iwm_priv_deinit(struct iwm_priv *iwm) +{ + int i; + + for (i = 0; i < IWM_TX_QUEUES; i++) + destroy_workqueue(iwm->txq[i].wq); + + destroy_workqueue(iwm->rx_wq); +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index 68e2c3b6c7a..88dd82649b4 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -114,14 +114,20 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, iwm = wdev_to_iwm(wdev); iwm->bus_ops = if_ops; iwm->wdev = wdev; - iwm_priv_init(iwm); + + ret = iwm_priv_init(iwm); + if (ret) { + dev_err(dev, "failed to init iwm_priv\n"); + goto out_wdev; + } + wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode); ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES); if (!ndev) { dev_err(dev, "no memory for network device instance\n"); - goto out_wdev; + goto out_priv; } ndev->netdev_ops = &iwm_netdev_ops; @@ -141,6 +147,9 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, out_ndev: free_netdev(ndev); + out_priv: + iwm_priv_deinit(iwm); + out_wdev: iwm_wdev_free(iwm); return ERR_PTR(ret); @@ -148,15 +157,11 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, void iwm_if_free(struct iwm_priv *iwm) { - int i; - if (!iwm_to_ndev(iwm)) return; unregister_netdev(iwm_to_ndev(iwm)); free_netdev(iwm_to_ndev(iwm)); iwm_wdev_free(iwm); - destroy_workqueue(iwm->rx_wq); - for (i = 0; i < IWM_TX_QUEUES; i++) - destroy_workqueue(iwm->txq[i].wq); + iwm_priv_deinit(iwm); } -- cgit v1.2.3 From d7e057dca3f1b76ff44dd16fefcd493a52614aad Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 15 Jun 2009 21:36:14 +0200 Subject: iwmc3200wifi: add iwm_if_add and iwm_if_remove We used to do alloc_netdev and register_netdev at the same time in iwm_if_alloc. But some bus related structures will only be initialized after iwm_priv is allocated. This caused a race condition that the netdev might be registered earlier. The patch adds iwm_if_add and iwm_if_remove so that the bus layer could register the device after all initialization is done. Signed-off-by: Zhu Yi Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/iwm.h | 2 ++ drivers/net/wireless/iwmc3200wifi/netdev.c | 32 +++++++++++++++++++----------- drivers/net/wireless/iwmc3200wifi/sdio.c | 9 +++++++++ 3 files changed, 31 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 2237448e042..4aa0ad1932f 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -315,6 +315,8 @@ extern const struct iw_handler_def iwm_iw_handler_def; void *iwm_if_alloc(int sizeof_bus, struct device *dev, struct iwm_if_ops *if_ops); void iwm_if_free(struct iwm_priv *iwm); +int iwm_if_add(struct iwm_priv *iwm); +void iwm_if_remove(struct iwm_priv *iwm); int iwm_mode_to_nl80211_iftype(int mode); int iwm_priv_init(struct iwm_priv *iwm); void iwm_priv_deinit(struct iwm_priv *iwm); diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index 88dd82649b4..aaa20c6885c 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -123,8 +123,7 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode); - ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, - IWM_TX_QUEUES); + ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES); if (!ndev) { dev_err(dev, "no memory for network device instance\n"); goto out_priv; @@ -134,19 +133,10 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, ndev->wireless_handlers = &iwm_iw_handler_def; ndev->ieee80211_ptr = wdev; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); - ret = register_netdev(ndev); - if (ret < 0) { - dev_err(dev, "Failed to register netdev: %d\n", ret); - goto out_ndev; - } - wdev->netdev = ndev; return iwm; - out_ndev: - free_netdev(ndev); - out_priv: iwm_priv_deinit(iwm); @@ -160,8 +150,26 @@ void iwm_if_free(struct iwm_priv *iwm) if (!iwm_to_ndev(iwm)) return; - unregister_netdev(iwm_to_ndev(iwm)); free_netdev(iwm_to_ndev(iwm)); iwm_wdev_free(iwm); iwm_priv_deinit(iwm); } + +int iwm_if_add(struct iwm_priv *iwm) +{ + struct net_device *ndev = iwm_to_ndev(iwm); + int ret; + + ret = register_netdev(ndev); + if (ret < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret); + return ret; + } + + return 0; +} + +void iwm_if_remove(struct iwm_priv *iwm) +{ + unregister_netdev(iwm_to_ndev(iwm)); +} diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c index b54da677b37..c0405715647 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.c +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c @@ -454,10 +454,18 @@ static int iwm_sdio_probe(struct sdio_func *func, INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker); + ret = iwm_if_add(iwm); + if (ret) { + dev_err(dev, "add SDIO interface failed\n"); + goto destroy_wq; + } + dev_info(dev, "IWM SDIO probe\n"); return 0; + destroy_wq: + destroy_workqueue(hw->isr_wq); debugfs_exit: iwm_debugfs_exit(iwm); if_free: @@ -472,6 +480,7 @@ static void iwm_sdio_remove(struct sdio_func *func) struct device *dev = &func->dev; iwm_debugfs_exit(iwm); + iwm_if_remove(iwm); iwm_if_free(iwm); destroy_workqueue(hw->isr_wq); -- cgit v1.2.3 From 4e9aa52e36a7beb4c0163324a3de759d7cf2e442 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 15 Jun 2009 21:59:48 +0200 Subject: iwmc3200wifi: fix potential kernel oops on module removal The iwm_if_free() is called before destroy_workqueue for isr_wq on device remove method. But if there is still some pending work in the isr_wq, the required data structures are already freed at this point. This leeds a kernel oops. The patch fixes this problem by moving iwm_if_free after destroy_workqueue. Signed-off-by: Zhu Yi Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c index c0405715647..916681837fd 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.c +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c @@ -479,10 +479,10 @@ static void iwm_sdio_remove(struct sdio_func *func) struct iwm_priv *iwm = hw_to_iwm(hw); struct device *dev = &func->dev; - iwm_debugfs_exit(iwm); iwm_if_remove(iwm); - iwm_if_free(iwm); destroy_workqueue(hw->isr_wq); + iwm_debugfs_exit(iwm); + iwm_if_free(iwm); sdio_set_drvdata(func, NULL); -- cgit v1.2.3 From 68810c5dc5f6bbaa0bbf6acce7cb56d97a1c8fd0 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 15 Jun 2009 21:59:49 +0200 Subject: iwmc3200wifi: add a mutex to protect iwm_reset_worker The patch adds a mutex to protect the iwm_reset_worker against netdev ndo_open and ndo_stop because all of them call iwm_up and iwm_down in the implementation. Note the latter two are already protected by rtnl. So if iwm_reset_worker is not required in the future, the mutex can also be removed. Signed-off-by: Zhu Yi Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/iwm.h | 1 + drivers/net/wireless/iwmc3200wifi/main.c | 54 ++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 4aa0ad1932f..77c339f8516 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -288,6 +288,7 @@ struct iwm_priv { u8 *eeprom; struct timer_list watchdog; struct work_struct reset_worker; + struct mutex mutex; struct rfkill *rfkill; char private[0] __attribute__((__aligned__(NETDEV_ALIGN))); diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 4d3c423d8ff..8be206d5822 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -112,6 +112,9 @@ static void iwm_statistics_request(struct work_struct *work) iwm_send_umac_stats_req(iwm, 0); } +int __iwm_up(struct iwm_priv *iwm); +int __iwm_down(struct iwm_priv *iwm); + static void iwm_reset_worker(struct work_struct *work) { struct iwm_priv *iwm; @@ -120,6 +123,19 @@ static void iwm_reset_worker(struct work_struct *work) iwm = container_of(work, struct iwm_priv, reset_worker); + /* + * XXX: The iwm->mutex is introduced purely for this reset work, + * because the other users for iwm_up and iwm_down are only netdev + * ndo_open and ndo_stop which are already protected by rtnl. + * Please remove iwm->mutex together if iwm_reset_worker() is not + * required in the future. + */ + if (!mutex_trylock(&iwm->mutex)) { + IWM_WARN(iwm, "We are in the middle of interface bringing " + "UP/DOWN. Skip driver resetting.\n"); + return; + } + if (iwm->umac_profile_active) { profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL); if (profile) @@ -128,10 +144,10 @@ static void iwm_reset_worker(struct work_struct *work) IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); } - iwm_down(iwm); + __iwm_down(iwm); while (retry++ < 3) { - ret = iwm_up(iwm); + ret = __iwm_up(iwm); if (!ret) break; @@ -142,7 +158,7 @@ static void iwm_reset_worker(struct work_struct *work) IWM_WARN(iwm, "iwm_up() failed: %d\n", ret); kfree(profile); - return; + goto out; } if (profile) { @@ -151,6 +167,9 @@ static void iwm_reset_worker(struct work_struct *work) iwm_send_mlme_profile(iwm); kfree(profile); } + + out: + mutex_unlock(&iwm->mutex); } static void iwm_watchdog(unsigned long data) @@ -215,6 +234,7 @@ int iwm_priv_init(struct iwm_priv *iwm) init_timer(&iwm->watchdog); iwm->watchdog.function = iwm_watchdog; iwm->watchdog.data = (unsigned long)iwm; + mutex_init(&iwm->mutex); return 0; } @@ -476,7 +496,7 @@ void iwm_link_off(struct iwm_priv *iwm) iwm_rx_free(iwm); - cancel_delayed_work(&iwm->stats_request); + cancel_delayed_work_sync(&iwm->stats_request); memset(wstats, 0, sizeof(struct iw_statistics)); wstats->qual.updated = IW_QUAL_ALL_INVALID; @@ -521,7 +541,7 @@ static int iwm_channels_init(struct iwm_priv *iwm) return 0; } -int iwm_up(struct iwm_priv *iwm) +int __iwm_up(struct iwm_priv *iwm) { int ret; struct iwm_notif *notif_reboot, *notif_ack = NULL; @@ -657,7 +677,18 @@ int iwm_up(struct iwm_priv *iwm) return -EIO; } -int iwm_down(struct iwm_priv *iwm) +int iwm_up(struct iwm_priv *iwm) +{ + int ret; + + mutex_lock(&iwm->mutex); + ret = __iwm_up(iwm); + mutex_unlock(&iwm->mutex); + + return ret; +} + +int __iwm_down(struct iwm_priv *iwm) { int ret; @@ -688,3 +719,14 @@ int iwm_down(struct iwm_priv *iwm) return 0; } + +int iwm_down(struct iwm_priv *iwm) +{ + int ret; + + mutex_lock(&iwm->mutex); + ret = __iwm_down(iwm); + mutex_unlock(&iwm->mutex); + + return ret; +} -- cgit v1.2.3 From f72151fb6820e90ce12a15e2768aa41150c5186d Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Tue, 16 Jun 2009 03:15:56 +0100 Subject: zd1211rw: adding 083a:e503 as a ZD1211B device Hans Pontar reported success on the sourceforge zd1211-devs mailing list. The device is branded "Arcor Easy Stick A 50 WLAN" (device manufactured by SMC for a German ISP - SMC model name: WN4501H-LF-IR). General information and Windows driver are available under (German only): http://www.arcor.de/hilfe/neu/index.php?sid=&aktion=anzeigen&rubrik=004018140&id=487 Device details: USB-IDs: Vendor: 0x083A Device: 0xE503 Chip ID: zd1211b chip 083a:e503 v4810 high 00-1d-19 AL2230S_RF pa0 g--N- Signed-off-by: Hin-Tak Leung Tested-by: Hans Pontar Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index f0e5e943f6e..14a19baff21 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -67,6 +67,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, -- cgit v1.2.3 From f0214843ba23d9bf6dc6b8ad2c6ee27b60f0322e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 16 Jun 2009 11:59:23 +0300 Subject: ath9k: Fix PCI FATAL interrupts by restoring RETRY_TIMEOUT disabling An earlier commit, 'ath9k: remove dummy PCI "retry timeout" fix', removed code that was documented to disable RETRY_TIMEOUT register (PCI reg 0x41) since it was claimed to be a no-op. However, it turns out that there are some combinations of hosts and ath9k-supported cards for which this is not a no-op (reg 0x41 has value 0x80, not 0) and this code (or something similar) is needed. In such cases, the driver may be next to unusable due to very frequent PCI FATAL interrupts from the card. Reverting the earlier commit, i.e., restoring the RETRY_TIMEOUT disabling, seems to resolve the issue. Since the removal of this code was not based on any known issue and was purely a cleanup change, the safest option here is to just revert that commit. Should there be desire to clean this up in the future, the change will need to be tested with a more complete coverage of cards and host systems. http://bugzilla.kernel.org/show_bug.cgi?id=13483 Cc: stable@kernel.org Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/pci.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index ccdf20a2e9b..170c5b32e49 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -87,6 +87,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct ath_softc *sc; struct ieee80211_hw *hw; u8 csz; + u32 val; int ret = 0; struct ath_hw *ah; @@ -133,6 +134,14 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + ret = pci_request_region(pdev, 0, "ath9k"); if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); @@ -239,12 +248,21 @@ static int ath_pci_resume(struct pci_dev *pdev) struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + u32 val; int err; err = pci_enable_device(pdev); if (err) return err; pci_restore_state(pdev); + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); /* Enable LED */ ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, -- cgit v1.2.3 From 06d5caf47ef4fbd9efdceae33293c42778cb7b0c Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Tue, 16 Jun 2009 15:39:51 +0100 Subject: rfkill: don't restore software blocked state on persistent devices The setting of the "persistent" flag is also made more explicit using a new rfkill_init_sw_state() function, instead of special-casing rfkill_set_sw_state() when it is called before registration. Suspend is a bit of a corner case so we try to get away without adding another hack to rfkill-input - it's going to be removed soon. If the state does change over suspend, users will simply have to prod rfkill-input twice in order to toggle the state. Userspace policy agents will be able to implement a more consistent user experience. For example, they can avoid the above problem if they toggle devices individually. Then there would be no "global state" to get out of sync. Currently there are only two rfkill drivers with persistent soft-blocked state. thinkpad-acpi already checks the software state on resume. eeepc-laptop will require modification. Signed-off-by: Alan Jenkins CC: Marcel Holtmann Acked-by: Henrique de Moraes Holschuh Signed-off-by: John W. Linville --- drivers/platform/x86/eeepc-laptop.c | 8 ++++---- drivers/platform/x86/thinkpad_acpi.c | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 03bf522bd7a..01682eca436 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -675,8 +675,8 @@ static int eeepc_hotk_add(struct acpi_device *device) if (!ehotk->eeepc_wlan_rfkill) goto wlan_fail; - rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, - get_acpi(CM_ASL_WLAN) != 1); + rfkill_init_sw_state(ehotk->eeepc_wlan_rfkill, + get_acpi(CM_ASL_WLAN) != 1); result = rfkill_register(ehotk->eeepc_wlan_rfkill); if (result) goto wlan_fail; @@ -693,8 +693,8 @@ static int eeepc_hotk_add(struct acpi_device *device) if (!ehotk->eeepc_bluetooth_rfkill) goto bluetooth_fail; - rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill, - get_acpi(CM_ASL_BLUETOOTH) != 1); + rfkill_init_sw_state(ehotk->eeepc_bluetooth_rfkill, + get_acpi(CM_ASL_BLUETOOTH) != 1); result = rfkill_register(ehotk->eeepc_bluetooth_rfkill); if (result) goto bluetooth_fail; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 86e958539f4..40d64c03278 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1163,8 +1163,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, { struct tpacpi_rfk *atp_rfk; int res; - bool initial_sw_state = false; - int initial_sw_status; + bool sw_state = false; + int sw_status; BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); @@ -1185,17 +1185,17 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, atp_rfk->id = id; atp_rfk->ops = tp_rfkops; - initial_sw_status = (tp_rfkops->get_status)(); - if (initial_sw_status < 0) { + sw_status = (tp_rfkops->get_status)(); + if (sw_status < 0) { printk(TPACPI_ERR "failed to read initial state for %s, error %d\n", - name, initial_sw_status); + name, sw_status); } else { - initial_sw_state = (initial_sw_status == TPACPI_RFK_RADIO_OFF); + sw_state = (sw_status == TPACPI_RFK_RADIO_OFF); if (set_default) { /* try to keep the initial state, since we ask the * firmware to preserve it across S5 in NVRAM */ - rfkill_set_sw_state(atp_rfk->rfkill, initial_sw_state); + rfkill_init_sw_state(atp_rfk->rfkill, sw_state); } } rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); -- cgit v1.2.3 From 96e9cfeb9692b0bc6e03f9b6f9cb3c67a40b76d1 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Tue, 16 Jun 2009 14:53:52 +0100 Subject: eeepc-laptop: read rfkill soft-blocked state on resume This will respect state changes over hibernation, e.g. if the user disables the wireless in the BIOS setup screen. It reveals an issue where ACPI silently kills the wireless on suspend. Normally, the BIOS restores the correct state from non-volatile storage on boot. But when hibernation is aborted, the wireless would remain killed. Fortunately we can work around this in the resume handler by simply writing back the same value we read from NVS. Signed-off-by: Alan Jenkins Signed-off-by: John W. Linville --- drivers/platform/x86/eeepc-laptop.c | 42 +++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 01682eca436..8153b3e5918 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -180,6 +180,7 @@ static struct key_entry eeepc_keymap[] = { */ static int eeepc_hotk_add(struct acpi_device *device); static int eeepc_hotk_remove(struct acpi_device *device, int type); +static int eeepc_hotk_resume(struct acpi_device *device); static const struct acpi_device_id eeepc_device_ids[] = { {EEEPC_HOTK_HID, 0}, @@ -194,6 +195,7 @@ static struct acpi_driver eeepc_hotk_driver = { .ops = { .add = eeepc_hotk_add, .remove = eeepc_hotk_remove, + .resume = eeepc_hotk_resume, }, }; @@ -512,15 +514,12 @@ static int notify_brn(void) return -1; } -static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +static void eeepc_rfkill_hotplug(void) { struct pci_dev *dev; struct pci_bus *bus = pci_find_bus(0, 1); bool blocked; - if (event != ACPI_NOTIFY_BUS_CHECK) - return; - if (!bus) { printk(EEEPC_WARNING "Unable to find PCI bus 1?\n"); return; @@ -551,6 +550,14 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked); } +static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +{ + if (event != ACPI_NOTIFY_BUS_CHECK) + return; + + eeepc_rfkill_hotplug(); +} + static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) { static struct key_entry *key; @@ -734,6 +741,33 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) return 0; } +static int eeepc_hotk_resume(struct acpi_device *device) +{ + if (ehotk->eeepc_wlan_rfkill) { + bool wlan; + + /* Workaround - it seems that _PTS disables the wireless + without notification or changing the value read by WLAN. + Normally this is fine because the correct value is restored + from the non-volatile storage on resume, but we need to do + it ourself if case suspend is aborted, or we lose wireless. + */ + wlan = get_acpi(CM_ASL_WLAN); + set_acpi(CM_ASL_WLAN, wlan); + + rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, + wlan != 1); + + eeepc_rfkill_hotplug(); + } + + if (ehotk->eeepc_bluetooth_rfkill) + rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill, + get_acpi(CM_ASL_BLUETOOTH) != 1); + + return 0; +} + /* * Hwmon */ -- cgit v1.2.3 From 8451d22dad40a66416b8d9c0952efa09ec5398c5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 16 Jun 2009 11:59:23 +0300 Subject: ath5k: avoid PCI FATAL interrupts by restoring RETRY_TIMEOUT disabling This reverts 'ath5k: remove dummy PCI "retry timeout" fix' on the same theory as in 'ath9k: Fix PCI FATAL interrupts by restoring RETRY_TIMEOUT disabling'. Reported-by: Bob Copeland Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 55f7de09d13..fc5ba0e3a68 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -686,6 +686,13 @@ ath5k_pci_resume(struct pci_dev *pdev) if (err) return err; + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + */ + pci_write_config_byte(pdev, 0x41, 0); + err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); if (err) { ATH5K_ERR(sc, "request_irq failed\n"); -- cgit v1.2.3 From a878417cc576720d3c9ff5399522d06f226bad7d Mon Sep 17 00:00:00 2001 From: Troy Moure Date: Wed, 17 Jun 2009 11:51:56 +0100 Subject: acer-wmi: fix rfkill conversion "rfkill: rewrite" incorrectly reversed the meaning of 'state' in acer_rfkill_update() when it changed rfkill_force_state() to rfkill_set_sw_state(). Fix it. Signed-off-by: Troy Moure Signed-off-by: John W. Linville --- drivers/platform/x86/acer-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 09a503e5da6..be2fd6f9163 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -958,12 +958,12 @@ static void acer_rfkill_update(struct work_struct *ignored) status = get_u32(&state, ACER_CAP_WIRELESS); if (ACPI_SUCCESS(status)) - rfkill_set_sw_state(wireless_rfkill, !!state); + rfkill_set_sw_state(wireless_rfkill, !state); if (has_cap(ACER_CAP_BLUETOOTH)) { status = get_u32(&state, ACER_CAP_BLUETOOTH); if (ACPI_SUCCESS(status)) - rfkill_set_sw_state(bluetooth_rfkill, !!state); + rfkill_set_sw_state(bluetooth_rfkill, !state); } schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); -- cgit v1.2.3 From 58f5fffdc3b8567b3fa8ed77d75699db87e2d1d4 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 17 Jun 2009 20:53:20 +0200 Subject: ath9k: wait for beacon frame along with CAB Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index f99f3a76df3..cece1c4c6bd 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -539,11 +539,14 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) if (ath_beacon_dtim_pending_cab(skb)) { /* * Remain awake waiting for buffered broadcast/multicast - * frames. + * frames. If the last broadcast/multicast frame is not + * received properly, the next beacon frame will work as + * a backup trigger for returning into NETWORK SLEEP state, + * so we are waiting for it as well. */ DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating " "buffered broadcast/multicast frame(s)\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_CAB; + sc->sc_flags |= SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_BEACON; return; } -- cgit v1.2.3 From 38ab422e64d991472ce21ffdd7a37e8a02279697 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 17 Jun 2009 20:53:21 +0200 Subject: ath9k: restore PS mode, before we put the chip into FULL SLEEP state. We want to put the chip into FULL SLEEP state, when we are disabling the radio, but the the current code always change it to AWAKE/NETWORK SLEEP. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9f49a3251d4..66a6c1f5022 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1196,8 +1196,8 @@ void ath_radio_disable(struct ath_softc *sc) ath9k_hw_phy_disable(ah); ath9k_hw_configpcipowersave(ah, 1); - ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); ath9k_ps_restore(sc); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); } /*******************/ -- cgit v1.2.3 From eab0cd493c08632ef10624d0169849c973951c66 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 19 Jun 2009 01:06:45 +0200 Subject: ath5k: fix beacon_int handling 73ca5203366235f8a43e490767284ba8cfd8c479 (ath5k: remove conf->beacon_int usage) removed bintval setting from ath5k_config. We need to init the interval earlier and don't touch it in add_interface anymore. Otherwise it will be set only once by upper layer through bss_info_changed but not on second and further hostap executions. We ended up having bintval 1000 which rendered the AP useless on many clients. Signed-off-by: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Cc: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index fc5ba0e3a68..ea045151f95 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -538,6 +538,7 @@ ath5k_pci_probe(struct pci_dev *pdev, sc->iobase = mem; /* So we can unmap it on detach */ sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ sc->opmode = NL80211_IFTYPE_STATION; + sc->bintval = 1000; mutex_init(&sc->lock); spin_lock_init(&sc->rxbuflock); spin_lock_init(&sc->txbuflock); @@ -2755,9 +2756,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, goto end; } - /* Set to a reasonable value. Note that this will - * be set to mac80211's value at ath5k_config(). */ - sc->bintval = 1000; ath5k_hw_set_lladdr(sc->ah, conf->mac_addr); ret = 0; -- cgit v1.2.3 From 83b462c656813e002843ddb061c8cc99149cab14 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 20 Jun 2009 01:20:30 -0700 Subject: Net: qla3xxx, remove sleeping in atomic We cannot sleep in ql_reset_work under spinlock, unlock before sleep, relock after. Signed-off-by: Jiri Slaby Signed-off-by: David S. Miller --- drivers/net/qla3xxx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index 8a823ecc99a..bbc6d4d3cc9 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -3837,7 +3837,9 @@ static void ql_reset_work(struct work_struct *work) 16) | ISP_CONTROL_RI)); } + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); ssleep(1); + spin_lock_irqsave(&qdev->hw_lock, hw_flags); } while (--max_wait_time); spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); -- cgit v1.2.3 From 6be832529a8129c9d90a1d3a78c5d503a710b6fc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 20 Jun 2009 01:21:53 -0700 Subject: usbnet cdc_subset: fix issues talking to PXA gadgets The host-side CDC subset driver is binding more specifically than it should ... only to PXA 210/25x/26x Linux-USB gadgets. Loosen that restriction to match the gadget driver driver. This will various PXA 27x and PXA 3xx devices happier when talking to Linux hosts, potentially others. Signed-off-by: David Brownell Tested-by: Aric D. Blumer Signed-off-by: David S. Miller --- drivers/net/usb/cdc_subset.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index c66b9c324f5..ca39ace0b0e 100644 --- a/drivers/net/usb/cdc_subset.c +++ b/drivers/net/usb/cdc_subset.c @@ -307,9 +307,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x1286, 0x8001), // "blob" bootloader .driver_info = (unsigned long) &blob_info, }, { - // Linux Ethernet/RNDIS gadget on pxa210/25x/26x, second config - // e.g. Gumstix, current OpenZaurus, ... - USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203), + // Linux Ethernet/RNDIS gadget, mostly on PXA, second config + // e.g. Gumstix, current OpenZaurus, ... or anything else + // that just enables this gadget option. + USB_DEVICE (0x0525, 0xa4a2), .driver_info = (unsigned long) &linuxdev_info, }, #endif -- cgit v1.2.3 From 7e23091347664bf357ca651545c93e99fafc7b40 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sat, 20 Jun 2009 22:15:31 +0000 Subject: mlx4_en: Counting all the dropped packets on the TX side Reporting the counter's value through 'ethtool -S' Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_tx.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index 5dc7466ad03..c0177c364bb 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -515,14 +515,12 @@ static int get_real_size(struct sk_buff *skb, struct net_device *dev, else { if (netif_msg_tx_err(priv)) en_warn(priv, "Non-linear headers\n"); - dev_kfree_skb_any(skb); return 0; } } if (unlikely(*lso_header_size > MAX_LSO_HDR_SIZE)) { if (netif_msg_tx_err(priv)) en_warn(priv, "LSO header size too big\n"); - dev_kfree_skb_any(skb); return 0; } } else { @@ -622,7 +620,7 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) } real_size = get_real_size(skb, dev, &lso_header_size); if (unlikely(!real_size)) - return NETDEV_TX_OK; + goto tx_drop; /* Allign descriptor to TXBB size */ desc_size = ALIGN(real_size, TXBB_SIZE); @@ -630,8 +628,7 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(nr_txbb > MAX_DESC_TXBBS)) { if (netif_msg_tx_err(priv)) en_warn(priv, "Oversized header or SG list\n"); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto tx_drop; } tx_ind = skb->queue_mapping; @@ -657,8 +654,7 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(!priv->port_up)) { if (netif_msg_tx_err(priv)) en_warn(priv, "xmit: port down!\n"); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto tx_drop; } /* Track current inflight packets for performance analysis */ @@ -785,5 +781,10 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) mlx4_en_xmit_poll(priv, tx_ind); return 0; + +tx_drop: + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + return NETDEV_TX_OK; } -- cgit v1.2.3 From d4ddbaa6a9a09c019fc1a7fed5a0fa403ac437b9 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sat, 20 Jun 2009 22:15:39 +0000 Subject: mlx4_en: Removed redundant skb->len check We don't need this check in the transmit function Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_tx.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index c0177c364bb..e63132361a9 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -614,10 +614,6 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) int lso_header_size; void *fragptr; - if (unlikely(!skb->len)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } real_size = get_real_size(skb, dev, &lso_header_size); if (unlikely(!real_size)) goto tx_drop; -- cgit v1.2.3 From a11faac79fdbf771ed1ab310f6ef44b389423fe7 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sat, 20 Jun 2009 22:15:46 +0000 Subject: mlx4_en: using stop/start_all_queues After we moved to be a multi queue device, need to stop/start all of our transmit queues. Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_netdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index e02bafdd368..20a34cb6392 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -668,7 +668,7 @@ int mlx4_en_start_port(struct net_device *dev) queue_work(mdev->workqueue, &priv->mcast_task); priv->port_up = true; - netif_start_queue(dev); + netif_tx_start_all_queues(dev); return 0; mac_err: @@ -700,7 +700,7 @@ void mlx4_en_stop_port(struct net_device *dev) en_dbg(DRV, priv, "stop port called while port already down\n"); return; } - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); /* Synchronize with tx routine */ netif_tx_lock_bh(dev); -- cgit v1.2.3 From 3c05f5ef7c09291e51ae327e854bf43cb8e55a55 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sat, 20 Jun 2009 22:15:52 +0000 Subject: mlx4_en: Cancel port_up check in transmit function When closing the port, we stop all transmit queues under the transmit lock. It ensures that we will not attempt to transmit new packets after the physical port was closed. Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_netdev.c | 4 ++-- drivers/net/mlx4/en_tx.c | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index 20a34cb6392..03a557cc3b7 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -700,14 +700,14 @@ void mlx4_en_stop_port(struct net_device *dev) en_dbg(DRV, priv, "stop port called while port already down\n"); return; } - netif_tx_stop_all_queues(dev); /* Synchronize with tx routine */ netif_tx_lock_bh(dev); - priv->port_up = false; + netif_tx_stop_all_queues(dev); netif_tx_unlock_bh(dev); /* close port*/ + priv->port_up = false; mlx4_CLOSE_PORT(mdev->dev, priv->port); /* Unregister Mac address for the port */ diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index e63132361a9..99a6a36dc27 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -646,13 +646,6 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - /* Now that we know what Tx ring to use */ - if (unlikely(!priv->port_up)) { - if (netif_msg_tx_err(priv)) - en_warn(priv, "xmit: port down!\n"); - goto tx_drop; - } - /* Track current inflight packets for performance analysis */ AVG_PERF_COUNTER(priv->pstats.inflight_avg, (u32) (ring->prod - ring->cons - 1)); -- cgit v1.2.3 From 7237b400554c9bb5ba0091b5e39f4620f3dd5637 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sat, 20 Jun 2009 22:16:02 +0000 Subject: mlx4_en: Removed redundant check on lso header size This check that verifies that the LSO header along with control segment and first data segment do not cross 128 bytes is no longer required. Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_tx.c | 5 ----- drivers/net/mlx4/mlx4_en.h | 1 - 2 files changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index 99a6a36dc27..08c43f2ae72 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -518,11 +518,6 @@ static int get_real_size(struct sk_buff *skb, struct net_device *dev, return 0; } } - if (unlikely(*lso_header_size > MAX_LSO_HDR_SIZE)) { - if (netif_msg_tx_err(priv)) - en_warn(priv, "LSO header size too big\n"); - return 0; - } } else { *lso_header_size = 0; if (!is_inline(skb, NULL)) diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index d43a9e4c2ae..ad861db66f1 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -99,7 +99,6 @@ #define RSS_FACTOR 2 #define TXBB_SIZE 64 #define HEADROOM (2048 / TXBB_SIZE + 1) -#define MAX_LSO_HDR_SIZE 92 #define STAMP_STRIDE 64 #define STAMP_DWORDS (STAMP_STRIDE / 4) #define STAMP_SHIFT 31 -- cgit v1.2.3 From 0314db69d7564859890ff75e3f71cb4079b29869 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sat, 20 Jun 2009 22:16:10 +0000 Subject: mlx4_en: Remove redundant refill code on RX Our RX rings are always full, there is no need to check whether we need to fill them or not. If we fail to allocate a new socket buffer, the incoming packet is dropped an the ring remains full. Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_netdev.c | 2 - drivers/net/mlx4/en_rx.c | 96 -------------------------------------------- drivers/net/mlx4/mlx4_en.h | 4 -- 3 files changed, 102 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index 03a557cc3b7..93f4abd990a 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -881,7 +881,6 @@ void mlx4_en_destroy_netdev(struct net_device *dev) mlx4_free_hwq_res(mdev->dev, &priv->res, MLX4_EN_PAGE_SIZE); cancel_delayed_work(&priv->stats_task); - cancel_delayed_work(&priv->refill_task); /* flush any pending task for this netdev */ flush_workqueue(mdev->workqueue); @@ -986,7 +985,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, spin_lock_init(&priv->stats_lock); INIT_WORK(&priv->mcast_task, mlx4_en_do_set_multicast); INIT_WORK(&priv->mac_task, mlx4_en_do_set_mac); - INIT_DELAYED_WORK(&priv->refill_task, mlx4_en_rx_refill); INIT_WORK(&priv->watchdog_task, mlx4_en_restart); INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate); INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats); diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index 5a14899c1e2..91bdfdfd431 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c @@ -269,31 +269,6 @@ reduce_rings: return 0; } -static int mlx4_en_fill_rx_buf(struct net_device *dev, - struct mlx4_en_rx_ring *ring) -{ - struct mlx4_en_priv *priv = netdev_priv(dev); - int num = 0; - int err; - - while ((u32) (ring->prod - ring->cons) < ring->actual_size) { - err = mlx4_en_prepare_rx_desc(priv, ring, ring->prod & - ring->size_mask); - if (err) { - if (netif_msg_rx_err(priv)) - en_warn(priv, "Failed preparing rx descriptor\n"); - priv->port_stats.rx_alloc_failed++; - break; - } - ++num; - ++ring->prod; - } - if ((u32) (ring->prod - ring->cons) == ring->actual_size) - ring->full = 1; - - return num; -} - static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring) { @@ -312,42 +287,6 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, } } - -void mlx4_en_rx_refill(struct work_struct *work) -{ - struct delayed_work *delay = to_delayed_work(work); - struct mlx4_en_priv *priv = container_of(delay, struct mlx4_en_priv, - refill_task); - struct mlx4_en_dev *mdev = priv->mdev; - struct net_device *dev = priv->dev; - struct mlx4_en_rx_ring *ring; - int need_refill = 0; - int i; - - mutex_lock(&mdev->state_lock); - if (!mdev->device_up || !priv->port_up) - goto out; - - /* We only get here if there are no receive buffers, so we can't race - * with Rx interrupts while filling buffers */ - for (i = 0; i < priv->rx_ring_num; i++) { - ring = &priv->rx_ring[i]; - if (ring->need_refill) { - if (mlx4_en_fill_rx_buf(dev, ring)) { - ring->need_refill = 0; - mlx4_en_update_rx_prod_db(ring); - } else - need_refill = 1; - } - } - if (need_refill) - queue_delayed_work(mdev->workqueue, &priv->refill_task, HZ); - -out: - mutex_unlock(&mdev->state_lock); -} - - int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, u32 size, u16 stride) { @@ -457,9 +396,6 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) ring_ind--; goto err_allocator; } - - /* Fill Rx buffers */ - ring->full = 0; } err = mlx4_en_fill_rx_buffers(priv); if (err) @@ -647,33 +583,6 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, return skb; } -static void mlx4_en_copy_desc(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, - int from, int to, int num) -{ - struct skb_frag_struct *skb_frags_from; - struct skb_frag_struct *skb_frags_to; - struct mlx4_en_rx_desc *rx_desc_from; - struct mlx4_en_rx_desc *rx_desc_to; - int from_index, to_index; - int nr, i; - - for (i = 0; i < num; i++) { - from_index = (from + i) & ring->size_mask; - to_index = (to + i) & ring->size_mask; - skb_frags_from = ring->rx_info + (from_index << priv->log_rx_info); - skb_frags_to = ring->rx_info + (to_index << priv->log_rx_info); - rx_desc_from = ring->buf + (from_index << ring->log_stride); - rx_desc_to = ring->buf + (to_index << ring->log_stride); - - for (nr = 0; nr < priv->num_frags; nr++) { - skb_frags_to[nr].page = skb_frags_from[nr].page; - skb_frags_to[nr].page_offset = skb_frags_from[nr].page_offset; - rx_desc_to->data[nr].addr = rx_desc_from->data[nr].addr; - } - } -} - int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) { @@ -821,11 +730,6 @@ out: wmb(); /* ensure HW sees CQ consumer before we post new buffers */ ring->cons = cq->mcq.cons_index; ring->prod += polled; /* Polled descriptors were realocated in place */ - if (unlikely(!ring->full)) { - mlx4_en_copy_desc(priv, ring, ring->cons - polled, - ring->prod - polled, polled); - mlx4_en_fill_rx_buf(dev, ring); - } mlx4_en_update_rx_prod_db(ring); return polled; } diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index ad861db66f1..c7c5e86804f 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -295,8 +295,6 @@ struct mlx4_en_rx_ring { u32 prod; u32 cons; u32 buf_size; - int need_refill; - int full; void *buf; void *rx_info; unsigned long bytes; @@ -494,7 +492,6 @@ struct mlx4_en_priv { struct mlx4_en_cq rx_cq[MAX_RX_RINGS]; struct work_struct mcast_task; struct work_struct mac_task; - struct delayed_work refill_task; struct work_struct watchdog_task; struct work_struct linkstate_task; struct delayed_work stats_task; @@ -564,7 +561,6 @@ void mlx4_en_set_default_rss_map(struct mlx4_en_priv *priv, int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv); void mlx4_en_release_rss_steer(struct mlx4_en_priv *priv); int mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring); -void mlx4_en_rx_refill(struct work_struct *work); void mlx4_en_rx_irq(struct mlx4_cq *mcq); int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, u8 port, u64 mac, u64 clear, u8 mode); -- cgit v1.2.3 From f6b24caaf933a466397915a08e30e885a32f905a Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Sun, 21 Jun 2009 22:42:30 -0700 Subject: via-velocity: Fix velocity driver unmapping incorrect size. When a packet is greater than ETH_ZLEN, we end up assigning the boolean result of a comparison to the size we unmap. Signed-off-by: Dave Jones Signed-off-by: David S. Miller --- drivers/net/via-velocity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index b02f7adff5d..3ba35956327 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1847,7 +1847,7 @@ static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_ */ if (tdinfo->skb_dma) { - pktlen = (skb->len > ETH_ZLEN ? : ETH_ZLEN); + pktlen = max_t(unsigned int, skb->len, ETH_ZLEN); for (i = 0; i < tdinfo->nskb_dma; i++) { #ifdef VELOCITY_ZERO_COPY_SUPPORT pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], le16_to_cpu(td->tdesc1.len), PCI_DMA_TODEVICE); -- cgit v1.2.3