[svn-commits] kpfleming: branch 1.4 r3667 - in /branches/1.4: zaptel-base.c zconfig.h

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jan 11 15:35:19 CST 2008


Author: kpfleming
Date: Fri Jan 11 15:35:18 2008
New Revision: 3667

URL: http://svn.digium.com/view/zaptel?view=rev&rev=3667
Log:
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

Modified:
    branches/1.4/zaptel-base.c
    branches/1.4/zconfig.h

Modified: branches/1.4/zaptel-base.c
URL: http://svn.digium.com/view/zaptel/branches/1.4/zaptel-base.c?view=diff&rev=3667&r1=3666&r2=3667
==============================================================================
--- branches/1.4/zaptel-base.c (original)
+++ branches/1.4/zaptel-base.c Fri Jan 11 15:35:18 2008
@@ -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 @@
 #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 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 @@
 	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 @@
 {
 	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);
-
-	return 0;
+	write_unlock(&zone_lock);
+
+	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 @@
 		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 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;
 	}
@@ -2601,6 +2631,8 @@
 
 	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 {
@@ -3622,6 +3654,9 @@
 			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 @@
 		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 @@
 		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);

Modified: branches/1.4/zconfig.h
URL: http://svn.digium.com/view/zaptel/branches/1.4/zconfig.h?view=diff&rev=3667&r1=3666&r2=3667
==============================================================================
--- branches/1.4/zconfig.h (original)
+++ branches/1.4/zconfig.h Fri Jan 11 15:35:18 2008
@@ -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.




More information about the svn-commits mailing list