Commit 7a95d267 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by David S. Miller

net: ppp_generic - use idr technique instead of cardmaps

Use idr technique instead of own implemented cardmaps.
It saves us a number of lines and gives an ability
to use library functions.
Signed-off-by: default avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c0700f90
......@@ -27,6 +27,7 @@
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/idr.h>
#include <linux/netdevice.h>
#include <linux/poll.h>
#include <linux/ppp_defs.h>
......@@ -171,36 +172,14 @@ struct channel {
* channel.downl.
*/
/*
* A cardmap represents a mapping from unsigned integers to pointers,
* and provides a fast "find lowest unused number" operation.
* It uses a broad (32-way) tree with a bitmap at each level.
* It is designed to be space-efficient for small numbers of entries
* and time-efficient for large numbers of entries.
*/
#define CARDMAP_ORDER 5
#define CARDMAP_WIDTH (1U << CARDMAP_ORDER)
#define CARDMAP_MASK (CARDMAP_WIDTH - 1)
struct cardmap {
int shift;
unsigned long inuse;
struct cardmap *parent;
void *ptr[CARDMAP_WIDTH];
};
static void *cardmap_get(struct cardmap *map, unsigned int nr);
static int cardmap_set(struct cardmap **map, unsigned int nr, void *ptr);
static unsigned int cardmap_find_first_free(struct cardmap *map);
static void cardmap_destroy(struct cardmap **map);
/*
* all_ppp_mutex protects the all_ppp_units mapping.
* It also ensures that finding a ppp unit in the all_ppp_units map
* and updating its file.refcnt field is atomic.
*/
static DEFINE_MUTEX(all_ppp_mutex);
static struct cardmap *all_ppp_units;
static atomic_t ppp_unit_count = ATOMIC_INIT(0);
static struct idr ppp_units_idr;
/*
* all_channels_lock protects all_channels and last_channel_index,
......@@ -269,6 +248,9 @@ static struct channel *ppp_find_channel(int unit);
static int ppp_connect_channel(struct channel *pch, int unit);
static int ppp_disconnect_channel(struct channel *pch);
static void ppp_destroy_channel(struct channel *pch);
static int unit_get(struct idr *p, void *ptr);
static void unit_put(struct idr *p, int n);
static void *unit_find(struct idr *p, int n);
static struct class *ppp_class;
......@@ -870,6 +852,8 @@ static int __init ppp_init(void)
"ppp");
}
idr_init(&ppp_units_idr);
out:
if (err)
printk(KERN_ERR "failed to register PPP device (%d)\n", err);
......@@ -2440,10 +2424,22 @@ ppp_create_interface(int unit, int *retp)
ret = -EEXIST;
mutex_lock(&all_ppp_mutex);
if (unit < 0)
unit = cardmap_find_first_free(all_ppp_units);
else if (cardmap_get(all_ppp_units, unit) != NULL)
goto out2; /* unit already exists */
if (unit < 0) {
unit = unit_get(&ppp_units_idr, ppp);
if (unit < 0) {
*retp = unit;
goto out2;
}
} else {
if (unit_find(&ppp_units_idr, unit))
goto out2; /* unit already exists */
else {
/* darn, someone is cheatting us? */
*retp = -EINVAL;
goto out2;
}
}
/* Initialize the new ppp unit */
ppp->file.index = unit;
......@@ -2451,23 +2447,18 @@ ppp_create_interface(int unit, int *retp)
ret = register_netdev(dev);
if (ret != 0) {
unit_put(&ppp_units_idr, unit);
printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
dev->name, ret);
goto out2;
}
atomic_inc(&ppp_unit_count);
ret = cardmap_set(&all_ppp_units, unit, ppp);
if (ret != 0)
goto out3;
mutex_unlock(&all_ppp_mutex);
*retp = 0;
return ppp;
out3:
atomic_dec(&ppp_unit_count);
unregister_netdev(dev);
out2:
mutex_unlock(&all_ppp_mutex);
free_netdev(dev);
......@@ -2507,7 +2498,7 @@ static void ppp_shutdown_interface(struct ppp *ppp)
unregister_netdev(dev);
free_netdev(dev);
}
cardmap_set(&all_ppp_units, ppp->file.index, NULL);
unit_put(&ppp_units_idr, ppp->file.index);
ppp->file.dead = 1;
ppp->owner = NULL;
wake_up_interruptible(&ppp->file.rwait);
......@@ -2561,7 +2552,7 @@ static void ppp_destroy_interface(struct ppp *ppp)
static struct ppp *
ppp_find_unit(int unit)
{
return cardmap_get(all_ppp_units, unit);
return unit_find(&ppp_units_idr, unit);
}
/*
......@@ -2679,123 +2670,45 @@ static void __exit ppp_cleanup(void)
/* should never happen */
if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
printk(KERN_ERR "PPP: removing module but units remain!\n");
cardmap_destroy(&all_ppp_units);
unregister_chrdev(PPP_MAJOR, "ppp");
device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
class_destroy(ppp_class);
idr_destroy(&ppp_units_idr);
}
/*
* Cardmap implementation.
* Units handling. Caller must protect concurrent access
* by holding all_ppp_mutex
*/
static void *cardmap_get(struct cardmap *map, unsigned int nr)
/* get new free unit number and associate pointer with it */
static int unit_get(struct idr *p, void *ptr)
{
struct cardmap *p;
int i;
int unit, err;
for (p = map; p != NULL; ) {
if ((i = nr >> p->shift) >= CARDMAP_WIDTH)
return NULL;
if (p->shift == 0)
return p->ptr[i];
nr &= ~(CARDMAP_MASK << p->shift);
p = p->ptr[i];
again:
if (idr_pre_get(p, GFP_KERNEL) == 0) {
printk(KERN_ERR "Out of memory expanding drawable idr\n");
return -ENOMEM;
}
return NULL;
}
static int cardmap_set(struct cardmap **pmap, unsigned int nr, void *ptr)
{
struct cardmap *p;
int i;
err = idr_get_new_above(p, ptr, 0, &unit);
if (err == -EAGAIN)
goto again;
p = *pmap;
if (p == NULL || (nr >> p->shift) >= CARDMAP_WIDTH) {
do {
/* need a new top level */
struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
if (!np)
goto enomem;
np->ptr[0] = p;
if (p != NULL) {
np->shift = p->shift + CARDMAP_ORDER;
p->parent = np;
} else
np->shift = 0;
p = np;
} while ((nr >> p->shift) >= CARDMAP_WIDTH);
*pmap = p;
}
while (p->shift > 0) {
i = (nr >> p->shift) & CARDMAP_MASK;
if (p->ptr[i] == NULL) {
struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
if (!np)
goto enomem;
np->shift = p->shift - CARDMAP_ORDER;
np->parent = p;
p->ptr[i] = np;
}
if (ptr == NULL)
clear_bit(i, &p->inuse);
p = p->ptr[i];
}
i = nr & CARDMAP_MASK;
p->ptr[i] = ptr;
if (ptr != NULL)
set_bit(i, &p->inuse);
else
clear_bit(i, &p->inuse);
return 0;
enomem:
return -ENOMEM;
return unit;
}
static unsigned int cardmap_find_first_free(struct cardmap *map)
/* put unit number back to a pool */
static void unit_put(struct idr *p, int n)
{
struct cardmap *p;
unsigned int nr = 0;
int i;
if ((p = map) == NULL)
return 0;
for (;;) {
i = find_first_zero_bit(&p->inuse, CARDMAP_WIDTH);
if (i >= CARDMAP_WIDTH) {
if (p->parent == NULL)
return CARDMAP_WIDTH << p->shift;
p = p->parent;
i = (nr >> p->shift) & CARDMAP_MASK;
set_bit(i, &p->inuse);
continue;
}
nr = (nr & (~CARDMAP_MASK << p->shift)) | (i << p->shift);
if (p->shift == 0 || p->ptr[i] == NULL)
return nr;
p = p->ptr[i];
}
idr_remove(p, n);
}
static void cardmap_destroy(struct cardmap **pmap)
/* get pointer associated with the number */
static void *unit_find(struct idr *p, int n)
{
struct cardmap *p, *np;
int i;
for (p = *pmap; p != NULL; p = np) {
if (p->shift != 0) {
for (i = 0; i < CARDMAP_WIDTH; ++i)
if (p->ptr[i] != NULL)
break;
if (i < CARDMAP_WIDTH) {
np = p->ptr[i];
p->ptr[i] = NULL;
continue;
}
}
np = p->parent;
kfree(p);
}
*pmap = NULL;
return idr_find(p, n);
}
/* Module/initialization stuff */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment