summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-01-11 21:35:18 +0000
committerkpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-01-11 21:35:18 +0000
commiteecfbb328db7fca69f7a28f8bf1bf1e7b94e2194 (patch)
treebf2ef96d3d749a43040feade783fa1fc27d1fc22
parent78628ea0f9ec7986bb459b0df00ca45c6e18c1d3 (diff)
Implement atomic reference counting for tone zone structures, ensuring that they will never be freed while they are in use by a channel or as the default zone.
In passing, improve default zone handling so that there will never be a default zone value pointing to a zone that hasn't been loaded yet. (closes issue #10593) Reported by: jmhunter Patches were provided by Matti, but a different solution was chosen git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@3667 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r--zaptel-base.c83
-rw-r--r--zconfig.h3
2 files changed, 52 insertions, 34 deletions
diff --git a/zaptel-base.c b/zaptel-base.c
index a7feff6..f40e9a5 100644
--- a/zaptel-base.c
+++ b/zaptel-base.c
@@ -59,6 +59,7 @@
#include <linux/if.h>
#include <linux/if_ppp.h>
#endif
+#include <asm/atomic.h>
#ifndef CONFIG_OLD_HDLC_API
#define NEW_HDLC_INTERFACE
@@ -361,6 +362,7 @@ static spinlock_t bigzaplock = SPIN_LOCK_UNLOCKED;
#endif
struct zt_zone {
+ atomic_t refcount;
char name[40]; /* Informational, only */
int ringcadence[ZT_MAX_CADENCE];
struct zt_tone *tones[ZT_TONE_MAX];
@@ -382,7 +384,7 @@ static int maxchans = 0;
static int maxconfs = 0;
static int maxlinks = 0;
-static int default_zone = DEFAULT_TONE_ZONE;
+static int default_zone = -1;
short __zt_mulaw[256];
short __zt_alaw[256];
@@ -994,6 +996,8 @@ static void close_channel(struct zt_chan *chan)
readchunkpreec = chan->readchunkpreec;
chan->readchunkpreec = NULL;
chan->curtone = NULL;
+ if (chan->curzone)
+ atomic_dec(&chan->curzone->refcount);
chan->curzone = NULL;
chan->cadencepos = 0;
chan->pdialcount = 0;
@@ -1072,18 +1076,35 @@ static int free_tone_zone(int num)
{
struct zt_zone *z;
+ if ((num >= ZT_TONE_ZONE_MAX) || (num < 0))
+ return -EINVAL;
+
+ write_lock(&zone_lock);
z = tone_zones[num];
tone_zones[num] = NULL;
- kfree(z);
+ write_unlock(&zone_lock);
- return 0;
+ if (atomic_read(&z->refcount)) {
+ /* channels are still using this zone so put it back */
+ write_lock(&zone_lock);
+ tone_zones[num] = z;
+ write_unlock(&zone_lock);
+
+ return -EBUSY;
+ } else {
+ kfree(z);
+
+ return 0;
+ }
}
static int zt_register_tone_zone(int num, struct zt_zone *zone)
{
- int res=0;
+ int res = 0;
+
if ((num >= ZT_TONE_ZONE_MAX) || (num < 0))
return -EINVAL;
+
write_lock(&zone_lock);
if (tone_zones[num]) {
res = -EINVAL;
@@ -1092,8 +1113,10 @@ static int zt_register_tone_zone(int num, struct zt_zone *zone)
tone_zones[num] = zone;
}
write_unlock(&zone_lock);
+
if (!res)
printk(KERN_INFO "Registered tone zone %d (%s)\n", num, zone->name);
+
return res;
}
@@ -1147,20 +1170,27 @@ static int start_tone(struct zt_chan *chan, int tone)
static int set_tone_zone(struct zt_chan *chan, int zone)
{
- int res=0;
+ int res = 0;
+ struct zt_zone *z;
+
/* Assumes channel is already locked */
- if ((zone >= ZT_TONE_ZONE_MAX) || (zone < -1))
+
+ if (zone == -1)
+ zone = default_zone;
+
+ if ((zone >= ZT_TONE_ZONE_MAX) || (zone < 0))
return -EINVAL;
read_lock(&zone_lock);
- if (zone == -1) {
- zone = default_zone;
- }
- if (tone_zones[zone]) {
- chan->curzone = tone_zones[zone];
+ if ((z = tone_zones[zone])) {
+ if (chan->curzone)
+ atomic_dec(&chan->curzone->refcount);
+
+ atomic_inc(&z->refcount);
+ chan->curzone = z;
chan->tonezone = zone;
- memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence));
+ memcpy(chan->ringcadence, z->ringcadence, sizeof(chan->ringcadence));
} else {
res = -ENODATA;
}
@@ -2602,6 +2632,8 @@ static int ioctl_load_zone(unsigned long data)
for (x = 0; x < ZT_MAX_CADENCE; x++)
z->ringcadence[x] = th.ringcadence[x];
+ atomic_set(&z->refcount, 0);
+
for (x = 0; x < th.count; x++) {
enum {
REGULAR_TONE,
@@ -3622,6 +3654,9 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd
write_unlock(&zone_lock);
return -EINVAL;
}
+ if ((default_zone != -1) && tone_zones[default_zone])
+ atomic_dec(&tone_zones[default_zone]->refcount);
+ atomic_inc(&tone_zones[j]->refcount);
default_zone = j;
write_unlock(&zone_lock);
break;
@@ -3629,19 +3664,7 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd
return ioctl_load_zone(data);
case ZT_FREEZONE:
get_user(j, (int *) data);
- if ((j < 0) || (j >= ZT_TONE_ZONE_MAX))
- return -EINVAL;
- write_lock(&zone_lock);
-#if 0
- if (j == default_zone) {
- write_unlock(&zone_lock);
- /* XXX: possibly a better return code here */
- return -EINVAL;
- }
-#endif
- free_tone_zone(j);
- write_unlock(&zone_lock);
- break;
+ return free_tone_zone(j);
case ZT_SET_DIALPARAMS:
if (copy_from_user(&tdp, (struct zt_dialparams *) data, sizeof(tdp)))
return -EFAULT;
@@ -3982,19 +4005,17 @@ static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsign
rv = 0;
break;
case ZT_SETTONEZONE:
- get_user(j,(int *)data);
+ get_user(j, (int *) data);
spin_lock_irqsave(&chan->lock, flags);
- rv = set_tone_zone(chan, j);
+ rv = set_tone_zone(chan, j);
spin_unlock_irqrestore(&chan->lock, flags);
return rv;
case ZT_GETTONEZONE:
spin_lock_irqsave(&chan->lock, flags);
if (chan->curzone)
- rv = chan->tonezone;
- else
- rv = default_zone;
+ j = chan->tonezone;
spin_unlock_irqrestore(&chan->lock, flags);
- put_user(rv,(int *)data); /* return value */
+ put_user(j, (int *) data);
break;
case ZT_SENDTONE:
get_user(j,(int *)data);
diff --git a/zconfig.h b/zconfig.h
index 4a5d1ef..a5e18a7 100644
--- a/zconfig.h
+++ b/zconfig.h
@@ -136,9 +136,6 @@
*/
/* #define CONFIG_ZAPTEL_WATCHDOG */
-/* Tone zone info */
-#define DEFAULT_TONE_ZONE 0
-
/*
* Uncomment for Non-standard FXS groundstart start state (A=Low, B=Low)
* particularly for CAC channel bank groundstart FXO ports.