# HG changeset patch # User Martin Troxler # Date 1294223611 -3600 # Node ID 2bd8ad8bf41fd74f611ddde2981df4c9b6415c46 # Parent 5ef6507fc77afe05a2ec3273374058d65b629cd7 Sii write wait_event() deadlock fixed: use wait_event_interruptible and refcount ec_master_sii_write_request_t objects with kref diff -r 5ef6507fc77a -r 2bd8ad8bf41f master/cdev.c --- a/master/cdev.c Wed Jan 05 09:50:35 2011 +0100 +++ b/master/cdev.c Wed Jan 05 11:33:31 2011 +0100 @@ -1072,7 +1072,8 @@ ec_slave_t *slave; unsigned int byte_size; uint16_t *words; - ec_sii_write_request_t request; + ec_sii_write_request_t* request; + int retval; if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { return -EFAULT; @@ -1088,58 +1089,57 @@ return -ENOMEM; } + request = kmalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + kref_init(&request->refcount); + // init SII write request + INIT_LIST_HEAD(&request->list); + request->words = words; // now "owned" by request, see ec_master_sii_write_request_release + request->offset = data.offset; + request->nwords = data.nwords; + if (copy_from_user(words, (void __user *) data.words, byte_size)) { - kfree(words); - return -EFAULT; - } - - if (ec_mutex_lock_interruptible(&master->master_mutex)) - return -EINTR; - + kref_put(&request->refcount,ec_master_sii_write_request_release); + return -EFAULT; + } + + if (ec_mutex_lock_interruptible(&master->master_mutex)) { + kref_put(&request->refcount,ec_master_sii_write_request_release); + return -EINTR; + } if (!(slave = ec_master_find_slave( master, 0, data.slave_position))) { ec_mutex_unlock(&master->master_mutex); EC_MASTER_ERR(master, "Slave %u does not exist!\n", data.slave_position); - kfree(words); - return -EINVAL; - } - - // init SII write request - INIT_LIST_HEAD(&request.list); - request.slave = slave; - request.words = words; - request.offset = data.offset; - request.nwords = data.nwords; - request.state = EC_INT_REQUEST_QUEUED; + kref_put(&request->refcount,ec_master_sii_write_request_release); + return -EINVAL; + } + + request->slave = slave; + request->state = EC_INT_REQUEST_QUEUED; // schedule SII write request. - list_add_tail(&request.list, &master->sii_requests); + list_add_tail(&request->list, &master->sii_requests); + kref_get(&request->refcount); ec_mutex_unlock(&master->master_mutex); // wait for processing through FSM if (wait_event_interruptible(master->sii_queue, - request.state != EC_INT_REQUEST_QUEUED)) { - // interrupted by signal - ec_mutex_lock(&master->master_mutex); - if (request.state == EC_INT_REQUEST_QUEUED) { - // abort request - list_del(&request.list); - ec_mutex_unlock(&master->master_mutex); - kfree(words); - return -EINTR; - } - ec_mutex_unlock(&master->master_mutex); - } - - // wait until master FSM has finished processing - wait_event(master->sii_queue, request.state != EC_INT_REQUEST_BUSY); - - kfree(words); - - return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; + ((request->state == EC_INT_REQUEST_SUCCESS) || (request->state == EC_INT_REQUEST_FAILURE)))) { + // interrupted by signal + kref_put(&request->refcount,ec_master_sii_write_request_release); + return -EINTR; + } + + + retval = request->state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; + kref_put(&request->refcount,ec_master_sii_write_request_release); + + return retval; } /*****************************************************************************/ diff -r 5ef6507fc77a -r 2bd8ad8bf41f master/fsm_master.c --- a/master/fsm_master.c Wed Jan 05 09:50:35 2011 +0100 +++ b/master/fsm_master.c Wed Jan 05 11:33:31 2011 +0100 @@ -1068,6 +1068,7 @@ if (!ec_fsm_sii_success(&fsm->fsm_sii)) { EC_SLAVE_ERR(slave, "Failed to write SII data.\n"); request->state = EC_INT_REQUEST_FAILURE; + kref_put(&request->refcount,ec_master_sii_write_request_release); wake_up(&master->sii_queue); ec_fsm_master_restart(fsm); return; @@ -1096,6 +1097,7 @@ // TODO: Evaluate other SII contents! request->state = EC_INT_REQUEST_SUCCESS; + kref_put(&request->refcount,ec_master_sii_write_request_release); wake_up(&master->sii_queue); // check for another SII write request @@ -1228,6 +1230,19 @@ /*****************************************************************************/ +/** called by kref_put if the SII write request's refcount becomes zero. + * + */ +void ec_master_sii_write_request_release(struct kref *ref) +{ + ec_sii_write_request_t *request = container_of(ref, ec_sii_write_request_t, refcount); + EC_SLAVE_DBG(request->slave, 1, "Releasing SII write request %p.\n",request); + kfree(request->words); + kfree(request); +} + +/*****************************************************************************/ + /** called by kref_put if the SDO request's refcount becomes zero. * */ diff -r 5ef6507fc77a -r 2bd8ad8bf41f master/fsm_master.h --- a/master/fsm_master.h Wed Jan 05 09:50:35 2011 +0100 +++ b/master/fsm_master.h Wed Jan 05 11:33:31 2011 +0100 @@ -57,8 +57,11 @@ size_t nwords; /**< Number of words. */ const uint16_t *words; /**< Pointer to the data words. */ ec_internal_request_state_t state; /**< State of the request. */ + struct kref refcount; } ec_sii_write_request_t; +void ec_master_sii_write_request_release(struct kref *); + /*****************************************************************************/ /** Register request. diff -r 5ef6507fc77a -r 2bd8ad8bf41f master/master.c --- a/master/master.c Wed Jan 05 09:50:35 2011 +0100 +++ b/master/master.c Wed Jan 05 11:33:31 2011 +0100 @@ -397,6 +397,7 @@ EC_MASTER_WARN(master, "Discarding SII request, slave %u about" " to be deleted.\n", request->slave->ring_position); request->state = EC_INT_REQUEST_FAILURE; + kref_put(&request->refcount,ec_master_sii_write_request_release); wake_up(&master->sii_queue); }