Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[pandora-kernel.git] / drivers / scsi / cxgb3i / cxgb3i_offload.c
index c2e434e..4d8654c 100644 (file)
@@ -94,29 +94,30 @@ static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata)
        if (!cdata)
                goto error_out;
 
-       if (c3cn->saddr.sin_port != 0) {
-               idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
-               if (idx < 0 || idx >= cxgb3_max_connect)
-                       return 0;
-               if (!test_and_set_bit(idx, cdata->sport_map))
-                       return -EADDRINUSE;
+       if (c3cn->saddr.sin_port) {
+               cxgb3i_log_error("connect, sin_port NON-ZERO %u.\n",
+                                c3cn->saddr.sin_port);
+               return -EADDRINUSE;
        }
 
-       /* the sport_map_next may not be accurate but that is okay, sport_map
-          should be */
-       start = idx = cdata->sport_map_next;
+       spin_lock_bh(&cdata->lock);
+       start = idx = cdata->sport_next;
        do {
                if (++idx >= cxgb3_max_connect)
                        idx = 0;
-               if (!(test_and_set_bit(idx, cdata->sport_map))) {
+               if (!cdata->sport_conn[idx]) {
                        c3cn->saddr.sin_port = htons(cxgb3_sport_base + idx);
-                       cdata->sport_map_next = idx;
+                       cdata->sport_next = idx;
+                       cdata->sport_conn[idx] = c3cn;
+                       spin_unlock_bh(&cdata->lock);
+
                        c3cn_conn_debug("%s reserve port %u.\n",
                                        cdata->cdev->name,
                                        cxgb3_sport_base + idx);
                        return 0;
                }
        } while (idx != start);
+       spin_unlock_bh(&cdata->lock);
 
 error_out:
        return -EADDRNOTAVAIL;
@@ -124,15 +125,19 @@ error_out:
 
 static void c3cn_put_port(struct s3_conn *c3cn)
 {
-       struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(c3cn->cdev);
+       if (!c3cn->cdev)
+               return;
 
        if (c3cn->saddr.sin_port) {
+               struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(c3cn->cdev);
                int idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
 
                c3cn->saddr.sin_port = 0;
                if (idx < 0 || idx >= cxgb3_max_connect)
                        return;
-               clear_bit(idx, cdata->sport_map);
+               spin_lock_bh(&cdata->lock);
+               cdata->sport_conn[idx] = NULL;
+               spin_unlock_bh(&cdata->lock);
                c3cn_conn_debug("%s, release port %u.\n",
                                cdata->cdev->name, cxgb3_sport_base + idx);
        }
@@ -1305,11 +1310,7 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn)
        struct t3cdev *cdev = c3cn->cdev;
        unsigned int tid = c3cn->tid;
 
-       if (!cdev)
-               return;
-
        c3cn->qset = 0;
-
        c3cn_free_cpl_skbs(c3cn);
 
        if (c3cn->wr_avail != c3cn->wr_max) {
@@ -1317,18 +1318,22 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn)
                reset_wr_list(c3cn);
        }
 
-       if (c3cn->l2t) {
-               l2t_release(L2DATA(cdev), c3cn->l2t);
-               c3cn->l2t = NULL;
-       }
-
-       if (c3cn->state == C3CN_STATE_CONNECTING) /* we have ATID */
-               s3_free_atid(cdev, tid);
-       else {          /* we have TID */
-               cxgb3_remove_tid(cdev, (void *)c3cn, tid);
-               c3cn_put(c3cn);
+       if (cdev) {
+               if (c3cn->l2t) {
+                       l2t_release(L2DATA(cdev), c3cn->l2t);
+                       c3cn->l2t = NULL;
+               }
+               if (c3cn->state == C3CN_STATE_CONNECTING)
+                       /* we have ATID */
+                       s3_free_atid(cdev, tid);
+               else {
+                       /* we have TID */
+                       cxgb3_remove_tid(cdev, (void *)c3cn, tid);
+                       c3cn_put(c3cn);
+               }
        }
 
+       c3cn->dst_cache = NULL;
        c3cn->cdev = NULL;
 }
 
@@ -1417,17 +1422,18 @@ static void c3cn_active_close(struct s3_conn *c3cn)
 }
 
 /**
- * cxgb3i_c3cn_release - close and release an iscsi tcp connection
+ * cxgb3i_c3cn_release - close and release an iscsi tcp connection and any
+ *     resource held
  * @c3cn: the iscsi tcp connection
  */
 void cxgb3i_c3cn_release(struct s3_conn *c3cn)
 {
        c3cn_conn_debug("c3cn 0x%p, s %u, f 0x%lx.\n",
                        c3cn, c3cn->state, c3cn->flags);
-       if (likely(c3cn->state != C3CN_STATE_CONNECTING))
-               c3cn_active_close(c3cn);
-       else
+       if (unlikely(c3cn->state == C3CN_STATE_CONNECTING))
                c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED);
+       else if (likely(c3cn->state != C3CN_STATE_CLOSED))
+               c3cn_active_close(c3cn);
        c3cn_put(c3cn);
 }
 
@@ -1656,7 +1662,6 @@ int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin)
        c3cn_set_state(c3cn, C3CN_STATE_CLOSED);
        ip_rt_put(rt);
        c3cn_put_port(c3cn);
-       c3cn->daddr.sin_port = 0;
        return err;
 }
 
@@ -1776,10 +1781,25 @@ out_err:
 static void sdev_data_cleanup(struct cxgb3i_sdev_data *cdata)
 {
        struct adap_ports *ports = &cdata->ports;
+       struct s3_conn *c3cn;
        int i;
 
+       for (i = 0; i < cxgb3_max_connect; i++) {
+               if (cdata->sport_conn[i]) {
+                       c3cn = cdata->sport_conn[i];
+                       cdata->sport_conn[i] = NULL;
+
+                       spin_lock_bh(&c3cn->lock);
+                       c3cn->cdev = NULL;
+                       c3cn_set_flag(c3cn, C3CN_OFFLOAD_DOWN);
+                       c3cn_closed(c3cn);
+                       spin_unlock_bh(&c3cn->lock);
+               }
+       }
+
        for (i = 0; i < ports->nports; i++)
                NDEV2CDATA(ports->lldevs[i]) = NULL;
+
        cxgb3i_free_big_mem(cdata);
 }
 
@@ -1821,21 +1841,27 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client)
        struct cxgb3i_sdev_data *cdata;
        struct ofld_page_info rx_page_info;
        unsigned int wr_len;
-       int mapsize = DIV_ROUND_UP(cxgb3_max_connect,
-                                  8 * sizeof(unsigned long));
+       int mapsize = cxgb3_max_connect * sizeof(struct s3_conn *);
        int i;
 
        cdata =  cxgb3i_alloc_big_mem(sizeof(*cdata) + mapsize, GFP_KERNEL);
-       if (!cdata)
+       if (!cdata) {
+               cxgb3i_log_warn("t3dev 0x%p, offload up, OOM %d.\n",
+                               cdev, mapsize);
                return;
+       }
 
        if (cdev->ctl(cdev, GET_WR_LEN, &wr_len) < 0 ||
            cdev->ctl(cdev, GET_PORTS, &cdata->ports) < 0 ||
-           cdev->ctl(cdev, GET_RX_PAGE_INFO, &rx_page_info) < 0)
+           cdev->ctl(cdev, GET_RX_PAGE_INFO, &rx_page_info) < 0) {
+               cxgb3i_log_warn("t3dev 0x%p, offload up, ioctl failed.\n",
+                               cdev);
                goto free_cdata;
+       }
 
        s3_init_wr_tab(wr_len);
 
+       spin_lock_init(&cdata->lock);
        INIT_LIST_HEAD(&cdata->list);
        cdata->cdev = cdev;
        cdata->client = client;
@@ -1847,6 +1873,7 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client)
        list_add_tail(&cdata->list, &cdata_list);
        write_unlock(&cdata_rwlock);
 
+       cxgb3i_log_info("t3dev 0x%p, offload up, added.\n", cdev);
        return;
 
 free_cdata:
@@ -1861,6 +1888,8 @@ void cxgb3i_sdev_remove(struct t3cdev *cdev)
 {
        struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(cdev);
 
+       cxgb3i_log_info("t3dev 0x%p, offload down, remove.\n", cdev);
+
        write_lock(&cdata_rwlock);
        list_del(&cdata->list);
        write_unlock(&cdata_rwlock);