Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IB/iser: Don't defer connection failure notification to workqueue

When a connection is terminated asynchronously from the iSCSI layer's
perspective, iSER needs to notify the iSCSI layer that the connection
has failed. This is done using a workqueue (switched to from the iSER
tasklet context). Meanwhile, the connection object (that holds the
work struct) is released. If the workqueue function wasn't called
yet, it will be called later with a NULL pointer, which will crash the
kernel.

The context switch (tasklet to workqueue) is not required, and
everything can be done from the iSER tasklet. This eliminates the NULL
work struct bug (and simplifies the code).

Signed-off-by: Erez Zilber <erezz@voltaire.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>

authored by

Erez Zilber and committed by
Roland Dreier
1d426d64 58e94913

+16 -25
-1
drivers/infiniband/ulp/iser/iscsi_iser.h
··· 245 245 wait_queue_head_t wait; /* waitq for conn/disconn */ 246 246 atomic_t post_recv_buf_count; /* posted rx count */ 247 247 atomic_t post_send_buf_count; /* posted tx count */ 248 - struct work_struct comperror_work; /* conn term sleepable ctx*/ 249 248 char name[ISER_OBJECT_NAME_SIZE]; 250 249 struct iser_page_vec *page_vec; /* represents SG to fmr maps* 251 250 * maps serialized as tx is*/
+16 -24
drivers/infiniband/ulp/iser/iser_verbs.c
··· 48 48 49 49 static void iser_cq_tasklet_fn(unsigned long data); 50 50 static void iser_cq_callback(struct ib_cq *cq, void *cq_context); 51 - static void iser_comp_error_worker(struct work_struct *work); 52 51 53 52 static void iser_cq_event_callback(struct ib_event *cause, void *context) 54 53 { ··· 479 480 init_waitqueue_head(&ib_conn->wait); 480 481 atomic_set(&ib_conn->post_recv_buf_count, 0); 481 482 atomic_set(&ib_conn->post_send_buf_count, 0); 482 - INIT_WORK(&ib_conn->comperror_work, iser_comp_error_worker); 483 483 INIT_LIST_HEAD(&ib_conn->conn_list); 484 484 spin_lock_init(&ib_conn->lock); 485 485 ··· 751 753 return ret_val; 752 754 } 753 755 754 - static void iser_comp_error_worker(struct work_struct *work) 755 - { 756 - struct iser_conn *ib_conn = 757 - container_of(work, struct iser_conn, comperror_work); 758 - 759 - /* getting here when the state is UP means that the conn is being * 760 - * terminated asynchronously from the iSCSI layer's perspective. */ 761 - if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, 762 - ISER_CONN_TERMINATING)) 763 - iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn, 764 - ISCSI_ERR_CONN_FAILED); 765 - 766 - /* complete the termination process if disconnect event was delivered * 767 - * note there are no more non completed posts to the QP */ 768 - if (ib_conn->disc_evt_flag) { 769 - ib_conn->state = ISER_CONN_DOWN; 770 - wake_up_interruptible(&ib_conn->wait); 771 - } 772 - } 773 - 774 756 static void iser_handle_comp_error(struct iser_desc *desc) 775 757 { 776 758 struct iser_dto *dto = &desc->dto; ··· 769 791 } 770 792 771 793 if (atomic_read(&ib_conn->post_recv_buf_count) == 0 && 772 - atomic_read(&ib_conn->post_send_buf_count) == 0) 773 - schedule_work(&ib_conn->comperror_work); 794 + atomic_read(&ib_conn->post_send_buf_count) == 0) { 795 + /* getting here when the state is UP means that the conn is * 796 + * being terminated asynchronously from the iSCSI layer's * 797 + * perspective. */ 798 + if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, 799 + ISER_CONN_TERMINATING)) 800 + iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn, 801 + ISCSI_ERR_CONN_FAILED); 802 + 803 + /* complete the termination process if disconnect event was delivered * 804 + * note there are no more non completed posts to the QP */ 805 + if (ib_conn->disc_evt_flag) { 806 + ib_conn->state = ISER_CONN_DOWN; 807 + wake_up_interruptible(&ib_conn->wait); 808 + } 809 + } 774 810 } 775 811 776 812 static void iser_cq_tasklet_fn(unsigned long data)