dahdi: Give userspace a chance to respond to surprise removal.

* We try very hard to help asterisk understand that we unassign spans.

 * Implement disable_span():
   - Set span + channels to DAHDI_ALARM_NOTOPEN
   - qevent DAHDI_EVENT_REMOVED

 * Use disable_span():
   - in dahdi_unassign_span() and dahdi_unregister_device()
   - with long msleep() so asterisk has a chance to get the message
   - Out of the registration_mutex so we actually context switch.

 * Also return more POLLERR variants (POLLRDHUP is not portable,
   should be tested).

 * Also improve printk(), fix rate_limit increment (was missing)

Signed-off-by: Oron Peled <oron.peled@xorcom.com>
Signed-off-by: Shaun Ruffell <sruffell@digium.com>

git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10285 a0bf4364-ded3-4de4-8d8a-66a801d63aff
This commit is contained in:
Oron Peled 2011-10-26 19:07:59 +00:00 committed by Tzafrir Cohen
parent 8f4a626087
commit 4d10eab759

View File

@ -2160,7 +2160,9 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan)
* file handles to this channel are disassociated with the actual * file handles to this channel are disassociated with the actual
* dahdi_chan. */ * dahdi_chan. */
if (chan->file) { if (chan->file) {
module_printk(KERN_NOTICE, "%s: surprise removal\n", __func__); module_printk(KERN_NOTICE,
"%s: surprise removal: chan %d\n",
__func__, chan->channo);
chan->file->private_data = NULL; chan->file->private_data = NULL;
} }
@ -5499,8 +5501,10 @@ static int dahdi_ioctl_iomux(struct file *file, unsigned long data)
if (unlikely(!chan->file->private_data)) { if (unlikely(!chan->file->private_data)) {
static int rate_limit; static int rate_limit;
if ((rate_limit % 1000) == 0) if ((rate_limit++ % 1000) == 0)
module_printk(KERN_NOTICE, "%s: surprise removal\n", __func__); module_printk(KERN_NOTICE,
"%s: (%d) nodev\n",
__func__, rate_limit);
msleep(5); msleep(5);
ret = -ENODEV; ret = -ENODEV;
break; break;
@ -7022,6 +7026,25 @@ int dahdi_register_device(struct dahdi_device *ddev, struct device *parent)
} }
EXPORT_SYMBOL(dahdi_register_device); EXPORT_SYMBOL(dahdi_register_device);
static void disable_span(struct dahdi_span *span)
{
int x;
unsigned long flags;
spin_lock_irqsave(&span->lock, flags);
span->alarms = DAHDI_ALARM_NOTOPEN;
for (x = 0; x < span->channels; x++) {
/*
* This event may not make it to user space before the channel
* is gone, but let's try.
*/
dahdi_qevent_lock(span->chans[x], DAHDI_EVENT_REMOVED);
}
dahdi_alarm_notify(span);
spin_unlock_irqrestore(&span->lock, flags);
module_printk(KERN_INFO, "%s: span %d\n", __func__, span->spanno);
}
/** /**
* _dahdi_unassign_span() - unassign a DAHDI span * _dahdi_unassign_span() - unassign a DAHDI span
* @span: the DAHDI span * @span: the DAHDI span
@ -7088,11 +7111,28 @@ static int _dahdi_unassign_span(struct dahdi_span *span)
return 0; return 0;
} }
static int open_channel_count(const struct dahdi_span *span)
{
int i;
int open_channels = 0;
struct dahdi_chan *chan;
for (i = 0; i < span->channels; ++i) {
chan = span->chans[i];
if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags))
++open_channels;
}
return open_channels;
}
int dahdi_unassign_span(struct dahdi_span *span) int dahdi_unassign_span(struct dahdi_span *span)
{ {
int ret; int ret;
module_printk(KERN_NOTICE, "%s: %s\n", __func__, span->name); module_printk(KERN_NOTICE, "%s: %s\n", __func__, span->name);
disable_span(span);
if (open_channel_count(span) > 0)
msleep(1000); /* Give user space a chance to read this */
mutex_lock(&registration_mutex); mutex_lock(&registration_mutex);
ret = _dahdi_unassign_span(span); ret = _dahdi_unassign_span(span);
mutex_unlock(&registration_mutex); mutex_unlock(&registration_mutex);
@ -7111,11 +7151,22 @@ void dahdi_unregister_device(struct dahdi_device *ddev)
{ {
struct dahdi_span *s; struct dahdi_span *s;
struct dahdi_span *next; struct dahdi_span *next;
unsigned int spans_with_open_channels = 0;
WARN_ON(!ddev); WARN_ON(!ddev);
might_sleep(); might_sleep();
if (unlikely(!ddev)) if (unlikely(!ddev))
return; return;
list_for_each_entry_safe(s, next, &ddev->spans, device_node) {
disable_span(s);
if (open_channel_count(s) > 0)
++spans_with_open_channels;
}
if (spans_with_open_channels > 0)
msleep(1000); /* give user space a chance to read this */
mutex_lock(&registration_mutex); mutex_lock(&registration_mutex);
list_for_each_entry_safe(s, next, &ddev->spans, device_node) { list_for_each_entry_safe(s, next, &ddev->spans, device_node) {
_dahdi_unassign_span(s); _dahdi_unassign_span(s);
@ -9095,10 +9146,11 @@ static unsigned int dahdi_timer_poll(struct file *file, struct poll_table_struct
} else { } else {
static int rate_limit; static int rate_limit;
if ((rate_limit % 1000) == 0) if ((rate_limit++ % 1000) == 0)
module_printk(KERN_NOTICE, "%s: nodev\n", __func__); module_printk(KERN_NOTICE, "%s: (%d) nodev\n",
__func__, rate_limit);
msleep(5); msleep(5);
return POLLERR | POLLHUP; return POLLERR | POLLHUP | POLLRDHUP | POLLNVAL | POLLPRI;
} }
return ret; return ret;
} }
@ -9114,10 +9166,11 @@ dahdi_chan_poll(struct file *file, struct poll_table_struct *wait_table)
if (unlikely(!c)) { if (unlikely(!c)) {
static int rate_limit; static int rate_limit;
if ((rate_limit % 1000) == 0) if ((rate_limit++ % 1000) == 0)
module_printk(KERN_NOTICE, "%s: nodev\n", __func__); module_printk(KERN_NOTICE, "%s: (%d) nodev\n",
msleep(5); __func__, rate_limit);
return POLLERR | POLLHUP; msleep(20);
return POLLERR | POLLHUP | POLLRDHUP | POLLNVAL | POLLPRI;
} }
poll_wait(file, &c->waitq, wait_table); poll_wait(file, &c->waitq, wait_table);