hulk inclusion category: bugfix bugzilla: 188286 CVE: NA, https://gitee.com/openeuler/kernel/issues/I67L59
----------------------------------------
iscsi_cls_conn introduces four members: lock, flags, cleanup_work, and state. Use iscsi_cls_conn_wrapper to avoid kabi changes.
Signed-off-by: Zhong Jinghua zhongjinghua@huawei.com --- drivers/scsi/scsi_transport_iscsi.c | 108 +++++++++++++++++----------- include/scsi/scsi_transport_iscsi.h | 11 ++- 2 files changed, 75 insertions(+), 44 deletions(-)
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index b52ada64e692..42c129d06322 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2207,14 +2207,16 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session);
static void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag) { + struct iscsi_cls_conn_wrapper *conn_wrapper = conn_to_wrapper(conn); + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn.\n");
switch (flag) { case STOP_CONN_RECOVER: - WRITE_ONCE(conn->state, ISCSI_CONN_FAILED); + WRITE_ONCE(conn_wrapper->state, ISCSI_CONN_FAILED); break; case STOP_CONN_TERM: - WRITE_ONCE(conn->state, ISCSI_CONN_DOWN); + WRITE_ONCE(conn_wrapper->state, ISCSI_CONN_DOWN); break; default: iscsi_cls_conn_printk(KERN_ERR, conn, "invalid stop flag %d\n", @@ -2229,10 +2231,11 @@ static void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag) static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active) { struct iscsi_cls_session *session = iscsi_conn_to_session(conn); + struct iscsi_cls_conn_wrapper *conn_wrapper = conn_to_wrapper(conn); struct iscsi_endpoint *ep;
ISCSI_DBG_TRANS_CONN(conn, "disconnect ep.\n"); - WRITE_ONCE(conn->state, ISCSI_CONN_FAILED); + WRITE_ONCE(conn_wrapper->state, ISCSI_CONN_FAILED);
if (!conn->ep || !session->transport->ep_disconnect) return; @@ -2248,17 +2251,19 @@ static void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn, struct iscsi_endpoint *ep, bool is_active) { + struct iscsi_cls_conn_wrapper *conn_wrapper = conn_to_wrapper(conn); + /* Check if this was a conn error and the kernel took ownership */ - spin_lock_irq(&conn->lock); - if (!test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { - spin_unlock_irq(&conn->lock); + spin_lock_irq(&conn_wrapper->lock); + if (!test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn_wrapper->flags)) { + spin_unlock_irq(&conn_wrapper->lock); iscsi_ep_disconnect(conn, is_active); } else { - spin_unlock_irq(&conn->lock); + spin_unlock_irq(&conn_wrapper->lock); ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n"); mutex_unlock(&conn->ep_mutex);
- flush_work(&conn->cleanup_work); + flush_work(&conn_wrapper->cleanup_work); /* * Userspace is now done with the EP so we can release the ref * iscsi_cleanup_conn_work_fn took. @@ -2273,11 +2278,13 @@ static int iscsi_if_stop_conn(struct iscsi_transport *transport, { int flag = ev->u.stop_conn.flag; struct iscsi_cls_conn *conn; + struct iscsi_cls_conn_wrapper *conn_wrapper;
conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); if (!conn) return -EINVAL;
+ conn_wrapper = conn_to_wrapper(conn); ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n"); /* * If this is a termination we have to call stop_conn with that flag @@ -2285,7 +2292,7 @@ static int iscsi_if_stop_conn(struct iscsi_transport *transport, * avoid the extra run. */ if (flag == STOP_CONN_TERM) { - cancel_work_sync(&conn->cleanup_work); + cancel_work_sync(&conn_wrapper->cleanup_work); iscsi_stop_conn(conn, flag); } else { /* @@ -2301,23 +2308,24 @@ static int iscsi_if_stop_conn(struct iscsi_transport *transport, /* * Figure out if it was the kernel or userspace initiating this. */ - spin_lock_irq(&conn->lock); - if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { - spin_unlock_irq(&conn->lock); + spin_lock_irq(&conn_wrapper->lock); + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, + &conn_wrapper->flags)) { + spin_unlock_irq(&conn_wrapper->lock); iscsi_stop_conn(conn, flag); } else { - spin_unlock_irq(&conn->lock); + spin_unlock_irq(&conn_wrapper->lock); ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n"); - flush_work(&conn->cleanup_work); + flush_work(&conn_wrapper->cleanup_work); } /* * Only clear for recovery to avoid extra cleanup runs during * termination. */ - spin_lock_irq(&conn->lock); - clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags); - spin_unlock_irq(&conn->lock); + spin_lock_irq(&conn_wrapper->lock); + clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn_wrapper->flags); + spin_unlock_irq(&conn_wrapper->lock); } ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop done.\n"); return 0; @@ -2325,8 +2333,10 @@ static int iscsi_if_stop_conn(struct iscsi_transport *transport,
static void iscsi_cleanup_conn_work_fn(struct work_struct *work) { - struct iscsi_cls_conn *conn = container_of(work, struct iscsi_cls_conn, - cleanup_work); + struct iscsi_cls_conn_wrapper *conn_wrapper = container_of(work, + struct iscsi_cls_conn_wrapper, + cleanup_work); + struct iscsi_cls_conn *conn = &conn_wrapper->conn; struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
mutex_lock(&conn->ep_mutex); @@ -2372,20 +2382,23 @@ iscsi_alloc_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) { struct iscsi_transport *transport = session->transport; struct iscsi_cls_conn *conn; + struct iscsi_cls_conn_wrapper *conn_wrapper;
- conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL); - if (!conn) + conn_wrapper = kzalloc(sizeof(*conn_wrapper) + dd_size, GFP_KERNEL); + if (!conn_wrapper) return NULL; + + conn = &conn_wrapper->conn; if (dd_size) - conn->dd_data = &conn[1]; + conn->dd_data = &conn_wrapper[1];
mutex_init(&conn->ep_mutex); - spin_lock_init(&conn->lock); + spin_lock_init(&conn_wrapper->lock); INIT_LIST_HEAD(&conn->conn_list); - INIT_WORK(&conn->cleanup_work, iscsi_cleanup_conn_work_fn); + INIT_WORK(&conn_wrapper->cleanup_work, iscsi_cleanup_conn_work_fn); conn->transport = transport; conn->cid = cid; - WRITE_ONCE(conn->state, ISCSI_CONN_DOWN); + WRITE_ONCE(conn_wrapper->state, ISCSI_CONN_DOWN);
/* this is released in the dev's release function */ if (!get_device(&session->dev)) @@ -2473,14 +2486,17 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) { struct iscsi_transport *transport = session->transport; struct iscsi_cls_conn *conn; + struct iscsi_cls_conn_wrapper *conn_wrapper; unsigned long flags; int err;
- conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL); - if (!conn) + conn_wrapper = kzalloc(sizeof(*conn_wrapper) + dd_size, GFP_KERNEL); + if (!conn_wrapper) return NULL; + + conn = &conn_wrapper->conn; if (dd_size) - conn->dd_data = &conn[1]; + conn->dd_data = &conn_wrapper[1];
mutex_init(&conn->ep_mutex); INIT_LIST_HEAD(&conn->conn_list); @@ -2662,25 +2678,26 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) struct sk_buff *skb; struct iscsi_uevent *ev; struct iscsi_internal *priv; + struct iscsi_cls_conn_wrapper *conn_wrapper = conn_to_wrapper(conn); int len = nlmsg_total_size(sizeof(*ev)); unsigned long flags; int state;
- spin_lock_irqsave(&conn->lock, flags); + spin_lock_irqsave(&conn_wrapper->lock, flags); /* * Userspace will only do a stop call if we are at least bound. And, we * only need to do the in kernel cleanup if in the UP state so cmds can * be released to upper layers. If in other states just wait for * userspace to avoid races that can leave the cleanup_work queued. */ - state = READ_ONCE(conn->state); + state = READ_ONCE(conn_wrapper->state); switch (state) { case ISCSI_CONN_BOUND: case ISCSI_CONN_UP: if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, - &conn->flags)) { + &conn_wrapper->flags)) { queue_work(iscsi_conn_cleanup_workq, - &conn->cleanup_work); + &conn_wrapper->cleanup_work); } break; default: @@ -2688,7 +2705,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) state); break; } - spin_unlock_irqrestore(&conn->lock, flags); + spin_unlock_irqrestore(&conn_wrapper->lock, flags);
priv = iscsi_if_transport_lookup(conn->transport); if (!priv) @@ -3018,13 +3035,15 @@ static int iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) { struct iscsi_cls_conn *conn; + struct iscsi_cls_conn_wrapper *conn_wrapper;
conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); if (!conn) return -EINVAL;
+ conn_wrapper = conn_to_wrapper(conn); ISCSI_DBG_TRANS_CONN(conn, "Flushing cleanup during destruction\n"); - flush_work(&conn->cleanup_work); + flush_work(&conn_wrapper->cleanup_work); ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
if (transport->destroy_conn) @@ -3037,6 +3056,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) { char *data = (char*)ev + sizeof(*ev); struct iscsi_cls_conn *conn; + struct iscsi_cls_conn_wrapper *conn_wrapper; struct iscsi_cls_session *session; int err = 0, value = 0, state;
@@ -3048,6 +3068,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) if (!conn || !session) return -EINVAL;
+ conn_wrapper = conn_to_wrapper(conn); switch (ev->u.set_param.param) { case ISCSI_PARAM_SESS_RECOVERY_TMO: sscanf(data, "%d", &value); @@ -3055,7 +3076,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) session->recovery_tmo = value; break; default: - state = READ_ONCE(conn->state); + state = READ_ONCE(conn_wrapper->state); if (state == ISCSI_CONN_BOUND || state == ISCSI_CONN_UP) { err = transport->set_param(conn, ev->u.set_param.param, data, ev->u.set_param.len); @@ -3794,6 +3815,7 @@ static int iscsi_if_transport_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev = nlmsg_data(nlh); struct iscsi_cls_session *session; struct iscsi_cls_conn *conn = NULL; + struct iscsi_cls_conn_wrapper *conn_wrapper; struct iscsi_endpoint *ep; uint32_t pdu_len; int err = 0; @@ -3830,15 +3852,16 @@ static int iscsi_if_transport_conn(struct iscsi_transport *transport, if (!conn) return -EINVAL;
+ conn_wrapper = conn_to_wrapper(conn); mutex_lock(&conn->ep_mutex); - spin_lock_irq(&conn->lock); - if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { - spin_unlock_irq(&conn->lock); + spin_lock_irq(&conn_wrapper->lock); + if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn_wrapper->flags)) { + spin_unlock_irq(&conn_wrapper->lock); mutex_unlock(&conn->ep_mutex); ev->r.retcode = -ENOTCONN; return 0; } - spin_unlock_irq(&conn->lock); + spin_unlock_irq(&conn_wrapper->lock);
switch (nlh->nlmsg_type) { case ISCSI_UEVENT_BIND_CONN: @@ -3852,7 +3875,7 @@ static int iscsi_if_transport_conn(struct iscsi_transport *transport, ev->u.b_conn.transport_eph, ev->u.b_conn.is_leading); if (!ev->r.retcode) - WRITE_ONCE(conn->state, ISCSI_CONN_BOUND); + WRITE_ONCE(conn_wrapper->state, ISCSI_CONN_BOUND);
if (ev->r.retcode || !transport->ep_connect) break; @@ -3871,7 +3894,7 @@ static int iscsi_if_transport_conn(struct iscsi_transport *transport, case ISCSI_UEVENT_START_CONN: ev->r.retcode = transport->start_conn(conn); if (!ev->r.retcode) - WRITE_ONCE(conn->state, ISCSI_CONN_UP); + WRITE_ONCE(conn_wrapper->state, ISCSI_CONN_UP);
break; case ISCSI_UEVENT_SEND_PDU: @@ -4162,8 +4185,9 @@ static ssize_t show_conn_state(struct device *dev, struct device_attribute *attr, char *buf) { struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); + struct iscsi_cls_conn_wrapper *conn_wrapper = conn_to_wrapper(conn); const char *state = "unknown"; - int conn_state = READ_ONCE(conn->state); + int conn_state = READ_ONCE(conn_wrapper->state);
if (conn_state >= 0 && conn_state < ARRAY_SIZE(connection_state_names)) diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 584b3eaa9936..7b9b2b46820e 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -223,15 +223,22 @@ struct iscsi_cls_conn { struct mutex ep_mutex; struct iscsi_endpoint *ep;
+ struct device dev; /* sysfs transport/container device */ +}; + +struct iscsi_cls_conn_wrapper { + struct iscsi_cls_conn conn; + /* Used when accessing flags and queueing work. */ spinlock_t lock; unsigned long flags; struct work_struct cleanup_work; - - struct device dev; /* sysfs transport/container device */ enum iscsi_connection_state state; };
+#define conn_to_wrapper(ic_conn) \ + container_of(ic_conn, struct iscsi_cls_conn_wrapper, conn) + #define iscsi_dev_to_conn(_dev) \ container_of(_dev, struct iscsi_cls_conn, dev)