diff options
Diffstat (limited to 'drivers/net/cnic.c')
-rw-r--r-- | drivers/net/cnic.c | 110 |
1 files changed, 106 insertions, 4 deletions
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index ee66c48e28b..b12bba795f2 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -81,6 +81,8 @@ static struct cnic_ops cnic_bnx2x_ops = { .cnic_ctl = cnic_ctl, }; +static struct workqueue_struct *cnic_wq; + static void cnic_shutdown_rings(struct cnic_dev *); static void cnic_init_rings(struct cnic_dev *); static int cnic_cm_set_pg(struct cnic_sock *); @@ -1629,10 +1631,11 @@ static int cnic_bnx2x_iscsi_ofld1(struct cnic_dev *dev, struct kwqe *wqes[], struct iscsi_kwqe_conn_offload1 *req1; struct iscsi_kwqe_conn_offload2 *req2; struct cnic_local *cp = dev->cnic_priv; + struct cnic_context *ctx; struct iscsi_kcqe kcqe; struct kcqe *cqes[1]; u32 l5_cid; - int ret; + int ret = 0; if (num < 2) { *work = num; @@ -1656,9 +1659,15 @@ static int cnic_bnx2x_iscsi_ofld1(struct cnic_dev *dev, struct kwqe *wqes[], kcqe.iscsi_conn_id = l5_cid; kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE; + ctx = &cp->ctx_tbl[l5_cid]; + if (test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags)) { + kcqe.completion_status = + ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY; + goto done; + } + if (atomic_inc_return(&cp->iscsi_conn) > dev->max_iscsi_conn) { atomic_dec(&cp->iscsi_conn); - ret = 0; goto done; } ret = cnic_alloc_bnx2x_conn_resc(dev, l5_cid); @@ -1748,8 +1757,16 @@ static int cnic_bnx2x_iscsi_destroy(struct cnic_dev *dev, struct kwqe *kwqe) if (!test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags)) goto skip_cfc_delete; - while (!time_after(jiffies, ctx->timestamp + (2 * HZ))) - msleep(250); + if (!time_after(jiffies, ctx->timestamp + (2 * HZ))) { + unsigned long delta = ctx->timestamp + (2 * HZ) - jiffies; + + if (delta > (2 * HZ)) + delta = 0; + + set_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags); + queue_delayed_work(cnic_wq, &cp->delete_task, delta); + goto destroy_reply; + } ret = cnic_bnx2x_destroy_ramrod(dev, l5_cid); @@ -1757,7 +1774,9 @@ skip_cfc_delete: cnic_free_bnx2x_conn_resc(dev, l5_cid); atomic_dec(&cp->iscsi_conn); + clear_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags); +destroy_reply: memset(&kcqe, 0, sizeof(kcqe)); kcqe.op_code = ISCSI_KCQE_OPCODE_DESTROY_CONN; kcqe.iscsi_conn_id = l5_cid; @@ -2748,6 +2767,13 @@ static int cnic_cm_create(struct cnic_dev *dev, int ulp_type, u32 cid, if (l5_cid >= MAX_CM_SK_TBL_SZ) return -EINVAL; + if (cp->ctx_tbl) { + struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; + + if (test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags)) + return -EAGAIN; + } + csk1 = &cp->csk_tbl[l5_cid]; if (atomic_read(&csk1->ref_count)) return -EAGAIN; @@ -3299,6 +3325,32 @@ static void cnic_close_bnx2x_conn(struct cnic_sock *csk, u32 opcode) static void cnic_cm_stop_bnx2x_hw(struct cnic_dev *dev) { + struct cnic_local *cp = dev->cnic_priv; + int i; + + if (!cp->ctx_tbl) + return; + + if (!netif_running(dev->netdev)) + return; + + for (i = 0; i < cp->max_cid_space; i++) { + struct cnic_context *ctx = &cp->ctx_tbl[i]; + + while (test_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags)) + msleep(10); + + if (test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags)) + netdev_warn(dev->netdev, "CID %x not deleted\n", + ctx->cid); + } + + cancel_delayed_work(&cp->delete_task); + flush_workqueue(cnic_wq); + + if (atomic_read(&cp->iscsi_conn) != 0) + netdev_warn(dev->netdev, "%d iSCSI connections not destroyed\n", + atomic_read(&cp->iscsi_conn)); } static int cnic_cm_init_bnx2x_hw(struct cnic_dev *dev) @@ -3333,6 +3385,46 @@ static int cnic_cm_init_bnx2x_hw(struct cnic_dev *dev) return 0; } +static void cnic_delete_task(struct work_struct *work) +{ + struct cnic_local *cp; + struct cnic_dev *dev; + u32 i; + int need_resched = 0; + + cp = container_of(work, struct cnic_local, delete_task.work); + dev = cp->dev; + + for (i = 0; i < cp->max_cid_space; i++) { + struct cnic_context *ctx = &cp->ctx_tbl[i]; + + if (!test_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags) || + !test_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags)) + continue; + + if (!time_after(jiffies, ctx->timestamp + (2 * HZ))) { + need_resched = 1; + continue; + } + + if (!test_and_clear_bit(CTX_FL_DELETE_WAIT, &ctx->ctx_flags)) + continue; + + cnic_bnx2x_destroy_ramrod(dev, i); + + cnic_free_bnx2x_conn_resc(dev, i); + if (ctx->ulp_proto_id == CNIC_ULP_ISCSI) + atomic_dec(&cp->iscsi_conn); + + clear_bit(CTX_FL_OFFLD_START, &ctx->ctx_flags); + } + + if (need_resched) + queue_delayed_work(cnic_wq, &cp->delete_task, + msecs_to_jiffies(10)); + +} + static int cnic_cm_open(struct cnic_dev *dev) { struct cnic_local *cp = dev->cnic_priv; @@ -3347,6 +3439,8 @@ static int cnic_cm_open(struct cnic_dev *dev) if (err) goto err_out; + INIT_DELAYED_WORK(&cp->delete_task, cnic_delete_task); + dev->cm_create = cnic_cm_create; dev->cm_destroy = cnic_cm_destroy; dev->cm_connect = cnic_cm_connect; @@ -4735,6 +4829,13 @@ static int __init cnic_init(void) return rc; } + cnic_wq = create_singlethread_workqueue("cnic_wq"); + if (!cnic_wq) { + cnic_release(); + unregister_netdevice_notifier(&cnic_netdev_notifier); + return -ENOMEM; + } + return 0; } @@ -4742,6 +4843,7 @@ static void __exit cnic_exit(void) { unregister_netdevice_notifier(&cnic_netdev_notifier); cnic_release(); + destroy_workqueue(cnic_wq); } module_init(cnic_init); |