summaryrefslogtreecommitdiffstats
path: root/drivers/net/cnic.c
diff options
context:
space:
mode:
authorAnton Vorontsov <avorontsov@ru.mvista.com>2009-09-23 03:49:27 +0400
committerAnton Vorontsov <avorontsov@ru.mvista.com>2009-09-23 03:49:27 +0400
commitf056878332a91ed984a116bad4e7d49aefff9e6e (patch)
tree572f4757c8e7811d45e0be0c2ae529c78fb63441 /drivers/net/cnic.c
parent3961f7c3cf247eee5df7fabadc7a40f2deeb98f3 (diff)
parent7fa07729e439a6184bd824746d06a49cca553f15 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: drivers/power/wm97xx_battery.c
Diffstat (limited to 'drivers/net/cnic.c')
-rw-r--r--drivers/net/cnic.c245
1 files changed, 177 insertions, 68 deletions
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
index 4d1515f45ba..d45eacb7670 100644
--- a/drivers/net/cnic.c
+++ b/drivers/net/cnic.c
@@ -138,6 +138,16 @@ static struct cnic_dev *cnic_from_netdev(struct net_device *netdev)
return NULL;
}
+static inline void ulp_get(struct cnic_ulp_ops *ulp_ops)
+{
+ atomic_inc(&ulp_ops->ref_count);
+}
+
+static inline void ulp_put(struct cnic_ulp_ops *ulp_ops)
+{
+ atomic_dec(&ulp_ops->ref_count);
+}
+
static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -227,7 +237,7 @@ static int cnic_send_nlmsg(struct cnic_local *cp, u32 type,
}
rcu_read_lock();
- ulp_ops = rcu_dereference(cp->ulp_ops[CNIC_ULP_ISCSI]);
+ ulp_ops = rcu_dereference(cnic_ulp_tbl[CNIC_ULP_ISCSI]);
if (ulp_ops)
ulp_ops->iscsi_nl_send_msg(cp->dev, msg_type, buf, len);
rcu_read_unlock();
@@ -319,6 +329,20 @@ static int cnic_abort_prep(struct cnic_sock *csk)
return 0;
}
+static void cnic_uio_stop(void)
+{
+ struct cnic_dev *dev;
+
+ read_lock(&cnic_dev_lock);
+ list_for_each_entry(dev, &cnic_dev_list, list) {
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (cp->cnic_uinfo)
+ cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
+ }
+ read_unlock(&cnic_dev_lock);
+}
+
int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
{
struct cnic_dev *dev;
@@ -344,6 +368,7 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
}
read_unlock(&cnic_dev_lock);
+ atomic_set(&ulp_ops->ref_count, 0);
rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops);
mutex_unlock(&cnic_lock);
@@ -365,6 +390,8 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
int cnic_unregister_driver(int ulp_type)
{
struct cnic_dev *dev;
+ struct cnic_ulp_ops *ulp_ops;
+ int i = 0;
if (ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
@@ -372,7 +399,8 @@ int cnic_unregister_driver(int ulp_type)
return -EINVAL;
}
mutex_lock(&cnic_lock);
- if (!cnic_ulp_tbl[ulp_type]) {
+ ulp_ops = cnic_ulp_tbl[ulp_type];
+ if (!ulp_ops) {
printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not "
"been registered\n", ulp_type);
goto out_unlock;
@@ -390,10 +418,21 @@ int cnic_unregister_driver(int ulp_type)
}
read_unlock(&cnic_dev_lock);
+ if (ulp_type == CNIC_ULP_ISCSI)
+ cnic_uio_stop();
+
rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL);
mutex_unlock(&cnic_lock);
synchronize_rcu();
+ while ((atomic_read(&ulp_ops->ref_count) != 0) && (i < 20)) {
+ msleep(100);
+ i++;
+ }
+
+ if (atomic_read(&ulp_ops->ref_count) != 0)
+ printk(KERN_WARNING PFX "%s: Failed waiting for ref count to go"
+ " to zero.\n", dev->netdev->name);
return 0;
out_unlock:
@@ -449,6 +488,7 @@ EXPORT_SYMBOL(cnic_register_driver);
static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
{
struct cnic_local *cp = dev->cnic_priv;
+ int i = 0;
if (ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
@@ -469,6 +509,15 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
synchronize_rcu();
+ while (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type]) &&
+ i < 20) {
+ msleep(100);
+ i++;
+ }
+ if (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type]))
+ printk(KERN_WARNING PFX "%s: Failed waiting for ULP up call"
+ " to complete.\n", dev->netdev->name);
+
return 0;
}
EXPORT_SYMBOL(cnic_unregister_driver);
@@ -632,7 +681,6 @@ static void cnic_free_resc(struct cnic_dev *dev)
int i = 0;
if (cp->cnic_uinfo) {
- cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
while (cp->uio_dev != -1 && i < 15) {
msleep(100);
i++;
@@ -726,42 +774,34 @@ static int cnic_alloc_context(struct cnic_dev *dev)
return 0;
}
-static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
+static int cnic_alloc_l2_rings(struct cnic_dev *dev, int pages)
{
struct cnic_local *cp = dev->cnic_priv;
- struct uio_info *uinfo;
- int ret;
-
- ret = cnic_alloc_dma(dev, &cp->kwq_info, KWQ_PAGE_CNT, 1);
- if (ret)
- goto error;
- cp->kwq = (struct kwqe **) cp->kwq_info.pg_arr;
-
- ret = cnic_alloc_dma(dev, &cp->kcq_info, KCQ_PAGE_CNT, 1);
- if (ret)
- goto error;
- cp->kcq = (struct kcqe **) cp->kcq_info.pg_arr;
-
- ret = cnic_alloc_context(dev);
- if (ret)
- goto error;
- cp->l2_ring_size = 2 * BCM_PAGE_SIZE;
+ cp->l2_ring_size = pages * BCM_PAGE_SIZE;
cp->l2_ring = pci_alloc_consistent(dev->pcidev, cp->l2_ring_size,
&cp->l2_ring_map);
if (!cp->l2_ring)
- goto error;
+ return -ENOMEM;
cp->l2_buf_size = (cp->l2_rx_ring_size + 1) * cp->l2_single_buf_size;
cp->l2_buf_size = PAGE_ALIGN(cp->l2_buf_size);
cp->l2_buf = pci_alloc_consistent(dev->pcidev, cp->l2_buf_size,
&cp->l2_buf_map);
if (!cp->l2_buf)
- goto error;
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int cnic_alloc_uio(struct cnic_dev *dev) {
+ struct cnic_local *cp = dev->cnic_priv;
+ struct uio_info *uinfo;
+ int ret;
uinfo = kzalloc(sizeof(*uinfo), GFP_ATOMIC);
if (!uinfo)
- goto error;
+ return -ENOMEM;
uinfo->mem[0].addr = dev->netdev->base_addr;
uinfo->mem[0].internal_addr = dev->regview;
@@ -769,10 +809,15 @@ static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
uinfo->mem[0].memtype = UIO_MEM_PHYS;
uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK;
- if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX)
- uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9;
- else
- uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE;
+ if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
+ if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX)
+ uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9;
+ else
+ uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE;
+
+ uinfo->name = "bnx2_cnic";
+ }
+
uinfo->mem[1].memtype = UIO_MEM_LOGICAL;
uinfo->mem[2].addr = (unsigned long) cp->l2_ring;
@@ -783,7 +828,6 @@ static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
uinfo->mem[3].size = cp->l2_buf_size;
uinfo->mem[3].memtype = UIO_MEM_LOGICAL;
- uinfo->name = "bnx2_cnic";
uinfo->version = CNIC_MODULE_VERSION;
uinfo->irq = UIO_IRQ_CUSTOM;
@@ -795,10 +839,39 @@ static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
ret = uio_register_device(&dev->pcidev->dev, uinfo);
if (ret) {
kfree(uinfo);
- goto error;
+ return ret;
}
cp->cnic_uinfo = uinfo;
+ return 0;
+}
+
+static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int ret;
+
+ ret = cnic_alloc_dma(dev, &cp->kwq_info, KWQ_PAGE_CNT, 1);
+ if (ret)
+ goto error;
+ cp->kwq = (struct kwqe **) cp->kwq_info.pg_arr;
+
+ ret = cnic_alloc_dma(dev, &cp->kcq_info, KCQ_PAGE_CNT, 1);
+ if (ret)
+ goto error;
+ cp->kcq = (struct kcqe **) cp->kcq_info.pg_arr;
+
+ ret = cnic_alloc_context(dev);
+ if (ret)
+ goto error;
+
+ ret = cnic_alloc_l2_rings(dev, 2);
+ if (ret)
+ goto error;
+
+ ret = cnic_alloc_uio(dev);
+ if (ret)
+ goto error;
return 0;
@@ -1057,18 +1130,26 @@ static void cnic_ulp_stop(struct cnic_dev *dev)
struct cnic_local *cp = dev->cnic_priv;
int if_type;
- rcu_read_lock();
+ if (cp->cnic_uinfo)
+ cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
+
for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
- if (!ulp_ops)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cp->ulp_ops[if_type];
+ if (!ulp_ops) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+ mutex_unlock(&cnic_lock);
if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type]))
ulp_ops->cnic_stop(cp->ulp_handle[if_type]);
+
+ clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
}
- rcu_read_unlock();
}
static void cnic_ulp_start(struct cnic_dev *dev)
@@ -1076,18 +1157,23 @@ static void cnic_ulp_start(struct cnic_dev *dev)
struct cnic_local *cp = dev->cnic_priv;
int if_type;
- rcu_read_lock();
for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
- if (!ulp_ops || !ulp_ops->cnic_start)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cp->ulp_ops[if_type];
+ if (!ulp_ops || !ulp_ops->cnic_start) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+ mutex_unlock(&cnic_lock);
if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type]))
ulp_ops->cnic_start(cp->ulp_handle[if_type]);
+
+ clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
}
- rcu_read_unlock();
}
static int cnic_ctl(void *data, struct cnic_ctl_info *info)
@@ -1097,22 +1183,18 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info)
switch (info->cmd) {
case CNIC_CTL_STOP_CMD:
cnic_hold(dev);
- mutex_lock(&cnic_lock);
cnic_ulp_stop(dev);
cnic_stop_hw(dev);
- mutex_unlock(&cnic_lock);
cnic_put(dev);
break;
case CNIC_CTL_START_CMD:
cnic_hold(dev);
- mutex_lock(&cnic_lock);
if (!cnic_start_hw(dev))
cnic_ulp_start(dev);
- mutex_unlock(&cnic_lock);
cnic_put(dev);
break;
default:
@@ -1126,19 +1208,23 @@ static void cnic_ulp_init(struct cnic_dev *dev)
int i;
struct cnic_local *cp = dev->cnic_priv;
- rcu_read_lock();
for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
- if (!ulp_ops || !ulp_ops->cnic_init)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cnic_ulp_tbl[i];
+ if (!ulp_ops || !ulp_ops->cnic_init) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ ulp_get(ulp_ops);
+ mutex_unlock(&cnic_lock);
if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i]))
ulp_ops->cnic_init(dev);
+ ulp_put(ulp_ops);
}
- rcu_read_unlock();
}
static void cnic_ulp_exit(struct cnic_dev *dev)
@@ -1146,19 +1232,23 @@ static void cnic_ulp_exit(struct cnic_dev *dev)
int i;
struct cnic_local *cp = dev->cnic_priv;
- rcu_read_lock();
for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
- if (!ulp_ops || !ulp_ops->cnic_exit)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cnic_ulp_tbl[i];
+ if (!ulp_ops || !ulp_ops->cnic_exit) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ ulp_get(ulp_ops);
+ mutex_unlock(&cnic_lock);
if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i]))
ulp_ops->cnic_exit(dev);
+ ulp_put(ulp_ops);
}
- rcu_read_unlock();
}
static int cnic_cm_offload_pg(struct cnic_sock *csk)
@@ -2374,21 +2464,45 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev)
return 0;
}
-static int cnic_start_hw(struct cnic_dev *dev)
+static int cnic_register_netdev(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
struct cnic_eth_dev *ethdev = cp->ethdev;
int err;
- if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
- return -EALREADY;
+ if (!ethdev)
+ return -ENODEV;
+
+ if (ethdev->drv_state & CNIC_DRV_STATE_REGD)
+ return 0;
err = ethdev->drv_register_cnic(dev->netdev, cp->cnic_ops, dev);
- if (err) {
+ if (err)
printk(KERN_ERR PFX "%s: register_cnic failed\n",
dev->netdev->name);
- goto err2;
- }
+
+ return err;
+}
+
+static void cnic_unregister_netdev(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ if (!ethdev)
+ return;
+
+ ethdev->drv_unregister_cnic(dev->netdev);
+}
+
+static int cnic_start_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ int err;
+
+ if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+ return -EALREADY;
dev->regview = ethdev->io_base;
cp->chip_id = ethdev->chip_id;
@@ -2419,18 +2533,13 @@ static int cnic_start_hw(struct cnic_dev *dev)
return 0;
err1:
- ethdev->drv_unregister_cnic(dev->netdev);
cp->free_resc(dev);
pci_dev_put(dev->pcidev);
-err2:
return err;
}
static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
{
- struct cnic_local *cp = dev->cnic_priv;
- struct cnic_eth_dev *ethdev = cp->ethdev;
-
cnic_disable_bnx2_int_sync(dev);
cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
@@ -2442,8 +2551,6 @@ static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
cnic_setup_5709_context(dev, 0);
cnic_free_irq(dev);
- ethdev->drv_unregister_cnic(dev->netdev);
-
cnic_free_resc(dev);
}
@@ -2524,7 +2631,7 @@ static struct cnic_dev *init_bnx2_cnic(struct net_device *dev)
probe = symbol_get(bnx2_cnic_probe);
if (probe) {
ethdev = (*probe)(dev);
- symbol_put_addr(probe);
+ symbol_put(bnx2_cnic_probe);
}
if (!ethdev)
return NULL;
@@ -2627,10 +2734,12 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
else if (event == NETDEV_UNREGISTER)
cnic_ulp_exit(dev);
else if (event == NETDEV_UP) {
- mutex_lock(&cnic_lock);
+ if (cnic_register_netdev(dev) != 0) {
+ cnic_put(dev);
+ goto done;
+ }
if (!cnic_start_hw(dev))
cnic_ulp_start(dev);
- mutex_unlock(&cnic_lock);
}
rcu_read_lock();
@@ -2649,10 +2758,9 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
rcu_read_unlock();
if (event == NETDEV_GOING_DOWN) {
- mutex_lock(&cnic_lock);
cnic_ulp_stop(dev);
cnic_stop_hw(dev);
- mutex_unlock(&cnic_lock);
+ cnic_unregister_netdev(dev);
} else if (event == NETDEV_UNREGISTER) {
write_lock(&cnic_dev_lock);
list_del_init(&dev->list);
@@ -2684,6 +2792,7 @@ static void cnic_release(void)
}
cnic_ulp_exit(dev);
+ cnic_unregister_netdev(dev);
list_del_init(&dev->list);
cnic_free_dev(dev);
}