Commit 2a90030f authored by Karen Xie's avatar Karen Xie Committed by James Bottomley

[SCSI] cxgb3i: close all tcp connections upon chip reset

Keep track of offloaded tcp connections per adapter. Close all of the
connections upon reset.
Signed-off-by: default avatarKaren Xie <kxie@chelsio.com>
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent ed6f7744
...@@ -94,29 +94,30 @@ static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata) ...@@ -94,29 +94,30 @@ static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata)
if (!cdata) if (!cdata)
goto error_out; goto error_out;
if (c3cn->saddr.sin_port != 0) { if (c3cn->saddr.sin_port) {
idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base; cxgb3i_log_error("connect, sin_port NON-ZERO %u.\n",
if (idx < 0 || idx >= cxgb3_max_connect) c3cn->saddr.sin_port);
return 0; return -EADDRINUSE;
if (!test_and_set_bit(idx, cdata->sport_map))
return -EADDRINUSE;
} }
/* the sport_map_next may not be accurate but that is okay, sport_map spin_lock_bh(&cdata->lock);
should be */ start = idx = cdata->sport_next;
start = idx = cdata->sport_map_next;
do { do {
if (++idx >= cxgb3_max_connect) if (++idx >= cxgb3_max_connect)
idx = 0; 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); 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", c3cn_conn_debug("%s reserve port %u.\n",
cdata->cdev->name, cdata->cdev->name,
cxgb3_sport_base + idx); cxgb3_sport_base + idx);
return 0; return 0;
} }
} while (idx != start); } while (idx != start);
spin_unlock_bh(&cdata->lock);
error_out: error_out:
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
...@@ -124,15 +125,19 @@ static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata) ...@@ -124,15 +125,19 @@ static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata)
static void c3cn_put_port(struct s3_conn *c3cn) 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) { 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; int idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
c3cn->saddr.sin_port = 0; c3cn->saddr.sin_port = 0;
if (idx < 0 || idx >= cxgb3_max_connect) if (idx < 0 || idx >= cxgb3_max_connect)
return; 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", c3cn_conn_debug("%s, release port %u.\n",
cdata->cdev->name, cxgb3_sport_base + idx); cdata->cdev->name, cxgb3_sport_base + idx);
} }
...@@ -1305,11 +1310,7 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn) ...@@ -1305,11 +1310,7 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn)
struct t3cdev *cdev = c3cn->cdev; struct t3cdev *cdev = c3cn->cdev;
unsigned int tid = c3cn->tid; unsigned int tid = c3cn->tid;
if (!cdev)
return;
c3cn->qset = 0; c3cn->qset = 0;
c3cn_free_cpl_skbs(c3cn); c3cn_free_cpl_skbs(c3cn);
if (c3cn->wr_avail != c3cn->wr_max) { if (c3cn->wr_avail != c3cn->wr_max) {
...@@ -1317,18 +1318,22 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn) ...@@ -1317,18 +1318,22 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn)
reset_wr_list(c3cn); reset_wr_list(c3cn);
} }
if (c3cn->l2t) { if (cdev) {
l2t_release(L2DATA(cdev), c3cn->l2t); if (c3cn->l2t) {
c3cn->l2t = NULL; l2t_release(L2DATA(cdev), c3cn->l2t);
} c3cn->l2t = NULL;
}
if (c3cn->state == C3CN_STATE_CONNECTING) /* we have ATID */ if (c3cn->state == C3CN_STATE_CONNECTING)
s3_free_atid(cdev, tid); /* we have ATID */
else { /* we have TID */ s3_free_atid(cdev, tid);
cxgb3_remove_tid(cdev, (void *)c3cn, tid); else {
c3cn_put(c3cn); /* we have TID */
cxgb3_remove_tid(cdev, (void *)c3cn, tid);
c3cn_put(c3cn);
}
} }
c3cn->dst_cache = NULL;
c3cn->cdev = NULL; c3cn->cdev = NULL;
} }
...@@ -1417,17 +1422,18 @@ static void c3cn_active_close(struct s3_conn *c3cn) ...@@ -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 * @c3cn: the iscsi tcp connection
*/ */
void cxgb3i_c3cn_release(struct s3_conn *c3cn) void cxgb3i_c3cn_release(struct s3_conn *c3cn)
{ {
c3cn_conn_debug("c3cn 0x%p, s %u, f 0x%lx.\n", c3cn_conn_debug("c3cn 0x%p, s %u, f 0x%lx.\n",
c3cn, c3cn->state, c3cn->flags); c3cn, c3cn->state, c3cn->flags);
if (likely(c3cn->state != C3CN_STATE_CONNECTING)) if (unlikely(c3cn->state == C3CN_STATE_CONNECTING))
c3cn_active_close(c3cn);
else
c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED); c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED);
else if (likely(c3cn->state != C3CN_STATE_CLOSED))
c3cn_active_close(c3cn);
c3cn_put(c3cn); c3cn_put(c3cn);
} }
...@@ -1656,7 +1662,6 @@ int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin) ...@@ -1656,7 +1662,6 @@ int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin)
c3cn_set_state(c3cn, C3CN_STATE_CLOSED); c3cn_set_state(c3cn, C3CN_STATE_CLOSED);
ip_rt_put(rt); ip_rt_put(rt);
c3cn_put_port(c3cn); c3cn_put_port(c3cn);
c3cn->daddr.sin_port = 0;
return err; return err;
} }
...@@ -1776,10 +1781,25 @@ int cxgb3i_c3cn_send_pdus(struct s3_conn *c3cn, struct sk_buff *skb) ...@@ -1776,10 +1781,25 @@ int cxgb3i_c3cn_send_pdus(struct s3_conn *c3cn, struct sk_buff *skb)
static void sdev_data_cleanup(struct cxgb3i_sdev_data *cdata) static void sdev_data_cleanup(struct cxgb3i_sdev_data *cdata)
{ {
struct adap_ports *ports = &cdata->ports; struct adap_ports *ports = &cdata->ports;
struct s3_conn *c3cn;
int i; 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++) for (i = 0; i < ports->nports; i++)
NDEV2CDATA(ports->lldevs[i]) = NULL; NDEV2CDATA(ports->lldevs[i]) = NULL;
cxgb3i_free_big_mem(cdata); cxgb3i_free_big_mem(cdata);
} }
...@@ -1821,21 +1841,27 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client) ...@@ -1821,21 +1841,27 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client)
struct cxgb3i_sdev_data *cdata; struct cxgb3i_sdev_data *cdata;
struct ofld_page_info rx_page_info; struct ofld_page_info rx_page_info;
unsigned int wr_len; unsigned int wr_len;
int mapsize = DIV_ROUND_UP(cxgb3_max_connect, int mapsize = cxgb3_max_connect * sizeof(struct s3_conn *);
8 * sizeof(unsigned long));
int i; int i;
cdata = cxgb3i_alloc_big_mem(sizeof(*cdata) + mapsize, GFP_KERNEL); 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; return;
}
if (cdev->ctl(cdev, GET_WR_LEN, &wr_len) < 0 || if (cdev->ctl(cdev, GET_WR_LEN, &wr_len) < 0 ||
cdev->ctl(cdev, GET_PORTS, &cdata->ports) < 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; goto free_cdata;
}
s3_init_wr_tab(wr_len); s3_init_wr_tab(wr_len);
spin_lock_init(&cdata->lock);
INIT_LIST_HEAD(&cdata->list); INIT_LIST_HEAD(&cdata->list);
cdata->cdev = cdev; cdata->cdev = cdev;
cdata->client = client; cdata->client = client;
...@@ -1847,6 +1873,7 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client) ...@@ -1847,6 +1873,7 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client)
list_add_tail(&cdata->list, &cdata_list); list_add_tail(&cdata->list, &cdata_list);
write_unlock(&cdata_rwlock); write_unlock(&cdata_rwlock);
cxgb3i_log_info("t3dev 0x%p, offload up, added.\n", cdev);
return; return;
free_cdata: free_cdata:
...@@ -1861,6 +1888,8 @@ void cxgb3i_sdev_remove(struct t3cdev *cdev) ...@@ -1861,6 +1888,8 @@ void cxgb3i_sdev_remove(struct t3cdev *cdev)
{ {
struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(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); write_lock(&cdata_rwlock);
list_del(&cdata->list); list_del(&cdata->list);
write_unlock(&cdata_rwlock); write_unlock(&cdata_rwlock);
......
...@@ -135,11 +135,11 @@ enum c3cn_flags { ...@@ -135,11 +135,11 @@ enum c3cn_flags {
C3CN_ABORT_RPL_PENDING, /* expecting an abort reply */ C3CN_ABORT_RPL_PENDING, /* expecting an abort reply */
C3CN_TX_DATA_SENT, /* already sent a TX_DATA WR */ C3CN_TX_DATA_SENT, /* already sent a TX_DATA WR */
C3CN_ACTIVE_CLOSE_NEEDED, /* need to be closed */ C3CN_ACTIVE_CLOSE_NEEDED, /* need to be closed */
C3CN_OFFLOAD_DOWN /* offload function off */
}; };
/** /**
* cxgb3i_sdev_data - Per adapter data. * cxgb3i_sdev_data - Per adapter data.
*
* Linked off of each Ethernet device port on the adapter. * Linked off of each Ethernet device port on the adapter.
* Also available via the t3cdev structure since we have pointers to our port * Also available via the t3cdev structure since we have pointers to our port
* net_device's there ... * net_device's there ...
...@@ -148,16 +148,17 @@ enum c3cn_flags { ...@@ -148,16 +148,17 @@ enum c3cn_flags {
* @cdev: t3cdev adapter * @cdev: t3cdev adapter
* @client: CPL client pointer * @client: CPL client pointer
* @ports: array of adapter ports * @ports: array of adapter ports
* @sport_map_next: next index into the port map * @sport_next: next port
* @sport_map: source port map * @sport_conn: source port connection
*/ */
struct cxgb3i_sdev_data { struct cxgb3i_sdev_data {
struct list_head list; struct list_head list;
struct t3cdev *cdev; struct t3cdev *cdev;
struct cxgb3_client *client; struct cxgb3_client *client;
struct adap_ports ports; struct adap_ports ports;
unsigned int sport_map_next; spinlock_t lock;
unsigned long sport_map[0]; unsigned int sport_next;
struct s3_conn *sport_conn[0];
}; };
#define NDEV2CDATA(ndev) (*(struct cxgb3i_sdev_data **)&(ndev)->ec_ptr) #define NDEV2CDATA(ndev) (*(struct cxgb3i_sdev_data **)&(ndev)->ec_ptr)
#define CXGB3_SDEV_DATA(cdev) NDEV2CDATA((cdev)->lldev) #define CXGB3_SDEV_DATA(cdev) NDEV2CDATA((cdev)->lldev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment