mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
dmeventd: new initialization of plugin threads
Rework thread creation code to better use resources. New code will not leak 'timeout' registered thread on error path. Also if the thread already exist - avoid creation of thread object and it's later destruction. If the race is noticed during adding new monitoring thread, such thread is put on cleanup list and -EEXIST is reported.
This commit is contained in:
parent
362558cd66
commit
76ea01dd20
@ -1,5 +1,6 @@
|
||||
Version 1.02.110 -
|
||||
======================================
|
||||
Reworked thread initialization for dmeventd plugins.
|
||||
Dmeventd handles snapshot overflow for now equally as invalid.
|
||||
Convert dmeventd to use common logging macro system from libdm.
|
||||
Return -ENOMEM when device registration fails instead of 0 (=success).
|
||||
|
@ -989,91 +989,6 @@ static int _active(struct message_data *message_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register for an event.
|
||||
*
|
||||
* Only one caller at a time here, because we use
|
||||
* a FIFO and lock it against multiple accesses.
|
||||
*/
|
||||
static int _register_for_event(struct message_data *message_data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct thread_status *thread, *thread_new = NULL;
|
||||
struct dso_data *dso_data;
|
||||
|
||||
if (!(dso_data = _lookup_dso(message_data)) &&
|
||||
!(dso_data = _load_dso(message_data))) {
|
||||
stack;
|
||||
#ifdef ELIBACC
|
||||
ret = -ELIBACC;
|
||||
#else
|
||||
ret = -ENODEV;
|
||||
#endif
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Preallocate thread status struct to avoid deadlock. */
|
||||
if (!(thread_new = _alloc_thread_status(message_data, dso_data))) {
|
||||
stack;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_fill_device_data(thread_new)) {
|
||||
stack;
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If creation of timeout thread fails (as it may), we fail
|
||||
here completely. The client is responsible for either
|
||||
retrying later or trying to register without timeout
|
||||
events. However, if timeout thread cannot be started, it
|
||||
usually means we are so starved on resources that we are
|
||||
almost as good as dead already... */
|
||||
if ((thread_new->events & DM_EVENT_TIMEOUT) &&
|
||||
(ret = -_register_for_timeout(thread_new)))
|
||||
goto out;
|
||||
|
||||
_lock_mutex();
|
||||
if (!(thread = _lookup_thread_status(message_data))) {
|
||||
_unlock_mutex();
|
||||
|
||||
if (!_do_register_device(thread_new)) {
|
||||
ret = -ENOMEM;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
thread = thread_new;
|
||||
thread_new = NULL;
|
||||
|
||||
/* Try to create the monitoring thread for this device. */
|
||||
_lock_mutex();
|
||||
if ((ret = -_create_thread(thread))) {
|
||||
_unlock_mutex();
|
||||
_do_unregister_device(thread);
|
||||
_free_thread_status(thread);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LINK_THREAD(thread);
|
||||
}
|
||||
|
||||
/* Or event # into events bitfield. */
|
||||
thread->events |= message_data->events_field;
|
||||
_unlock_mutex();
|
||||
|
||||
out:
|
||||
/*
|
||||
* Deallocate thread status after releasing
|
||||
* the lock in case we haven't used it.
|
||||
*/
|
||||
if (thread_new)
|
||||
_free_thread_status(thread_new);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister for an event.
|
||||
*
|
||||
@ -1127,6 +1042,91 @@ static int _unregister_for_event(struct message_data *message_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register for an event.
|
||||
*
|
||||
* Only one caller at a time here, because we use
|
||||
* a FIFO and lock it against multiple accesses.
|
||||
*/
|
||||
static int _register_for_event(struct message_data *message_data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct thread_status *thread;
|
||||
struct dso_data *dso_data;
|
||||
|
||||
if (!(dso_data = _lookup_dso(message_data)) &&
|
||||
!(dso_data = _load_dso(message_data))) {
|
||||
stack;
|
||||
#ifdef ELIBACC
|
||||
ret = -ELIBACC;
|
||||
#else
|
||||
ret = -ENODEV;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
_lock_mutex();
|
||||
|
||||
if ((thread = _lookup_thread_status(message_data)))
|
||||
/* Or event # into events bitfield. */
|
||||
thread->events |= message_data->events_field;
|
||||
|
||||
_unlock_mutex();
|
||||
|
||||
if (!thread) {
|
||||
if (!(thread = _alloc_thread_status(message_data, dso_data))) {
|
||||
stack;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!_fill_device_data(thread)) {
|
||||
ret = -ENODEV;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if (!_do_register_device(thread)) {
|
||||
ret = -ENOMEM;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if ((ret = -_create_thread(thread))) {
|
||||
_do_unregister_device(thread);
|
||||
goto_out;
|
||||
}
|
||||
|
||||
_lock_mutex();
|
||||
if (_lookup_thread_status(message_data)) {
|
||||
DEBUGLOG("Race, uuid already registered, marking Thr %x unused.",
|
||||
(int)thread->thread);
|
||||
thread->status = DM_THREAD_SHUTDOWN;
|
||||
thread->events = 0;
|
||||
LINK(thread, &_thread_registry_unused);
|
||||
_unlock_mutex();
|
||||
ret = -EEXIST; /* race ? */
|
||||
goto_out;
|
||||
}
|
||||
|
||||
LINK_THREAD(thread);
|
||||
_unlock_mutex();
|
||||
}
|
||||
|
||||
/* If creation of timeout thread fails (as it may), we fail
|
||||
here completely. The client is responsible for either
|
||||
retrying later or trying to register without timeout
|
||||
events. However, if timeout thread cannot be started, it
|
||||
usually means we are so starved on resources that we are
|
||||
almost as good as dead already... */
|
||||
if ((thread->events & DM_EVENT_TIMEOUT) &&
|
||||
(ret = -_register_for_timeout(thread)))
|
||||
_unregister_for_event(message_data);
|
||||
|
||||
return ret;
|
||||
out:
|
||||
_free_thread_status(thread);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get registered device.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user