## Automatically generated incremental diff ## From: linux-2.5.70-bk14 ## To: linux-2.5.70-bk15 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.5.70-bk14/Makefile linux-2.5.70-bk15/Makefile --- linux-2.5.70-bk14/Makefile 2003-06-10 04:42:58.000000000 -0700 +++ linux-2.5.70-bk15/Makefile 2003-06-10 04:43:06.000000000 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 70 -EXTRAVERSION = -bk14 +EXTRAVERSION = -bk15 # *DOCUMENTATION* # To see a list of typical targets execute "make help" diff -urN linux-2.5.70-bk14/drivers/md/dm-ioctl.c linux-2.5.70-bk15/drivers/md/dm-ioctl.c --- linux-2.5.70-bk14/drivers/md/dm-ioctl.c 2003-05-26 18:00:26.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm-ioctl.c 2003-06-10 04:43:08.000000000 -0700 @@ -351,7 +351,8 @@ static int populate_table(struct dm_table *table, struct dm_ioctl *args) { - int i = 0, r, first = 1; + int r, first = 1; + unsigned int i = 0; struct dm_target_spec *spec; char *params; void *begin, *end; @@ -380,7 +381,8 @@ } r = dm_table_add_target(table, spec->target_type, - spec->sector_start, spec->length, + (sector_t) spec->sector_start, + (sector_t) spec->length, params); if (r) { DMWARN("internal error adding target to table"); @@ -558,7 +560,7 @@ int r; struct dm_table *t; struct mapped_device *md; - int minor; + unsigned int minor = 0; r = check_name(param->name); if (r) @@ -574,8 +576,8 @@ return r; } - minor = (param->flags & DM_PERSISTENT_DEV_FLAG) ? - minor(to_kdev_t(param->dev)) : -1; + if (param->flags & DM_PERSISTENT_DEV_FLAG) + minor = minor(to_kdev_t(param->dev)); r = dm_create(minor, t, &md); if (r) { @@ -584,7 +586,7 @@ } dm_table_put(t); /* md will have grabbed its own reference */ - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG)); + set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); dm_put(md); @@ -595,9 +597,9 @@ * Build up the status struct for each target */ static int __status(struct mapped_device *md, struct dm_ioctl *param, - char *outbuf, int *len) + char *outbuf, size_t *len) { - int i, num_targets; + unsigned int i, num_targets; struct dm_target_spec *spec; char *outptr; status_type_t type; @@ -657,7 +659,7 @@ static int get_status(struct dm_ioctl *param, struct dm_ioctl *user) { struct mapped_device *md; - int len = 0; + size_t len = 0; int ret; char *outbuf = NULL; @@ -738,7 +740,8 @@ */ static int dep(struct dm_ioctl *param, struct dm_ioctl *user) { - int count, r; + int r; + unsigned int count; struct mapped_device *md; struct list_head *tmp; size_t len = 0; @@ -889,7 +892,7 @@ } dm_table_put(t); /* md will have taken its own reference */ - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG)); + set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); dm_put(md); r = info(param, user); @@ -945,7 +948,7 @@ * As well as checking the version compatibility this always * copies the kernel interface version out. */ -static int check_version(int cmd, struct dm_ioctl *user) +static int check_version(unsigned int cmd, struct dm_ioctl *user) { uint32_t version[3]; int r = 0; @@ -1028,7 +1031,8 @@ static int ctl_ioctl(struct inode *inode, struct file *file, uint command, ulong u) { - int r = 0, cmd; + int r = 0; + unsigned int cmd; struct dm_ioctl *param; struct dm_ioctl *user = (struct dm_ioctl *) u; ioctl_fn fn = NULL; diff -urN linux-2.5.70-bk14/drivers/md/dm-linear.c linux-2.5.70-bk15/drivers/md/dm-linear.c --- linux-2.5.70-bk14/drivers/md/dm-linear.c 2003-05-26 18:00:40.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm-linear.c 2003-06-10 04:43:08.000000000 -0700 @@ -23,7 +23,7 @@ /* * Construct a linear mapping: */ -static int linear_ctr(struct dm_target *ti, int argc, char **argv) +static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct linear_c *lc; @@ -76,7 +76,7 @@ } static int linear_status(struct dm_target *ti, status_type_t type, - char *result, int maxlen) + char *result, unsigned int maxlen) { struct linear_c *lc = (struct linear_c *) ti->private; char b[BDEVNAME_SIZE]; diff -urN linux-2.5.70-bk14/drivers/md/dm-stripe.c linux-2.5.70-bk15/drivers/md/dm-stripe.c --- linux-2.5.70-bk14/drivers/md/dm-stripe.c 2003-05-26 18:00:39.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm-stripe.c 2003-06-10 04:43:08.000000000 -0700 @@ -30,7 +30,7 @@ struct stripe stripe[0]; }; -static inline struct stripe_c *alloc_context(int stripes) +static inline struct stripe_c *alloc_context(unsigned int stripes) { size_t len; @@ -47,7 +47,7 @@ * Parse a single pair */ static int get_stripe(struct dm_target *ti, struct stripe_c *sc, - int stripe, char **argv) + unsigned int stripe, char **argv) { sector_t start; @@ -91,14 +91,15 @@ * Construct a striped mapping. * [ ]+ */ -static int stripe_ctr(struct dm_target *ti, int argc, char **argv) +static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct stripe_c *sc; sector_t width; uint32_t stripes; uint32_t chunk_size; char *end; - int r, i; + int r; + unsigned int i; if (argc < 2) { ti->error = "dm-stripe: Not enough arguments"; @@ -204,11 +205,11 @@ } static int stripe_status(struct dm_target *ti, - status_type_t type, char *result, int maxlen) + status_type_t type, char *result, unsigned int maxlen) { struct stripe_c *sc = (struct stripe_c *) ti->private; int offset; - int i; + unsigned int i; char b[BDEVNAME_SIZE]; switch (type) { diff -urN linux-2.5.70-bk14/drivers/md/dm-table.c linux-2.5.70-bk15/drivers/md/dm-table.c --- linux-2.5.70-bk14/drivers/md/dm-table.c 2003-05-26 18:01:00.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm-table.c 2003-06-10 04:43:08.000000000 -0700 @@ -23,12 +23,12 @@ atomic_t holders; /* btree table */ - int depth; - int counts[MAX_DEPTH]; /* in nodes */ + unsigned int depth; + unsigned int counts[MAX_DEPTH]; /* in nodes */ sector_t *index[MAX_DEPTH]; - int num_targets; - int num_allocated; + unsigned int num_targets; + unsigned int num_allocated; sector_t *highs; struct dm_target *targets; @@ -56,14 +56,6 @@ }; /* - * Ceiling(n / size) - */ -static inline unsigned long div_up(unsigned long n, unsigned long size) -{ - return dm_round_up(n, size) / size; -} - -/* * Similar to ceiling(log_size(n)) */ static unsigned int int_log(unsigned long n, unsigned long base) @@ -71,35 +63,46 @@ int result = 0; while (n > 1) { - n = div_up(n, base); + n = dm_div_up(n, base); result++; } return result; } -#define __HIGH(l, r) if (*(l) < (r)) *(l) = (r) -#define __LOW(l, r) if (*(l) == 0 || *(l) > (r)) *(l) = (r) +/* + * Returns the minimum that is _not_ zero, unless both are zero. + */ +#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r)) /* * Combine two io_restrictions, always taking the lower value. */ - static void combine_restrictions_low(struct io_restrictions *lhs, struct io_restrictions *rhs) { - __LOW(&lhs->max_sectors, rhs->max_sectors); - __LOW(&lhs->max_phys_segments, rhs->max_phys_segments); - __LOW(&lhs->max_hw_segments, rhs->max_hw_segments); - __HIGH(&lhs->hardsect_size, rhs->hardsect_size); - __LOW(&lhs->max_segment_size, rhs->max_segment_size); - __LOW(&lhs->seg_boundary_mask, rhs->seg_boundary_mask); + lhs->max_sectors = + min_not_zero(lhs->max_sectors, rhs->max_sectors); + + lhs->max_phys_segments = + min_not_zero(lhs->max_phys_segments, rhs->max_phys_segments); + + lhs->max_hw_segments = + min_not_zero(lhs->max_hw_segments, rhs->max_hw_segments); + + lhs->hardsect_size = max(lhs->hardsect_size, rhs->hardsect_size); + + lhs->max_segment_size = + min_not_zero(lhs->max_segment_size, rhs->max_segment_size); + + lhs->seg_boundary_mask = + min_not_zero(lhs->seg_boundary_mask, rhs->seg_boundary_mask); } /* * Calculate the index of the child node of the n'th node k'th key. */ -static inline int get_child(int n, int k) +static inline unsigned int get_child(unsigned int n, unsigned int k) { return (n * CHILDREN_PER_NODE) + k; } @@ -107,7 +110,8 @@ /* * Return the n'th node of level l from table t. */ -static inline sector_t *get_node(struct dm_table *t, int l, int n) +static inline sector_t *get_node(struct dm_table *t, + unsigned int l, unsigned int n) { return t->index[l] + (n * KEYS_PER_NODE); } @@ -116,7 +120,7 @@ * Return the highest key that you could lookup from the n'th * node on level l of the btree. */ -static sector_t high(struct dm_table *t, int l, int n) +static sector_t high(struct dm_table *t, unsigned int l, unsigned int n) { for (; l < t->depth - 1; l++) n = get_child(n, CHILDREN_PER_NODE - 1); @@ -131,15 +135,15 @@ * Fills in a level of the btree based on the highs of the level * below it. */ -static int setup_btree_index(int l, struct dm_table *t) +static int setup_btree_index(unsigned int l, struct dm_table *t) { - int n, k; + unsigned int n, k; sector_t *node; - for (n = 0; n < t->counts[l]; n++) { + for (n = 0U; n < t->counts[l]; n++) { node = get_node(t, l, n); - for (k = 0; k < KEYS_PER_NODE; k++) + for (k = 0U; k < KEYS_PER_NODE; k++) node[k] = high(t, l + 1, get_child(n, k)); } @@ -169,7 +173,7 @@ * highs, and targets are managed as dynamic arrays during a * table load. */ -static int alloc_targets(struct dm_table *t, int num) +static int alloc_targets(struct dm_table *t, unsigned int num) { sector_t *n_highs; struct dm_target *n_targets; @@ -237,9 +241,7 @@ void table_destroy(struct dm_table *t) { - int i; - - DMWARN("destroying table"); + unsigned int i; /* destroying the table counts as an event */ dm_table_event(t); @@ -481,13 +483,31 @@ request_queue_t *q = bdev_get_queue((*result)->bdev); struct io_restrictions *rs = &ti->limits; - /* combine the device limits low */ - __LOW(&rs->max_sectors, q->max_sectors); - __LOW(&rs->max_phys_segments, q->max_phys_segments); - __LOW(&rs->max_hw_segments, q->max_hw_segments); - __HIGH(&rs->hardsect_size, q->hardsect_size); - __LOW(&rs->max_segment_size, q->max_segment_size); - __LOW(&rs->seg_boundary_mask, q->seg_boundary_mask); + /* + * Combine the device limits low. + * + * FIXME: if we move an io_restriction struct + * into q this would just be a call to + * combine_restrictions_low() + */ + rs->max_sectors = + min_not_zero(rs->max_sectors, q->max_sectors); + + rs->max_phys_segments = + min_not_zero(rs->max_phys_segments, + q->max_phys_segments); + + rs->max_hw_segments = + min_not_zero(rs->max_hw_segments, q->max_hw_segments); + + rs->hardsect_size = max(rs->hardsect_size, q->hardsect_size); + + rs->max_segment_size = + min_not_zero(rs->max_segment_size, q->max_segment_size); + + rs->seg_boundary_mask = + min_not_zero(rs->seg_boundary_mask, + q->seg_boundary_mask); } return r; @@ -628,12 +648,13 @@ static int setup_indexes(struct dm_table *t) { - int i, total = 0; + int i; + unsigned int total = 0; sector_t *indexes; /* allocate the space for *all* the indexes */ for (i = t->depth - 2; i >= 0; i--) { - t->counts[i] = div_up(t->counts[i + 1], CHILDREN_PER_NODE); + t->counts[i] = dm_div_up(t->counts[i + 1], CHILDREN_PER_NODE); total += t->counts[i]; } @@ -656,10 +677,11 @@ */ int dm_table_complete(struct dm_table *t) { - int leaf_nodes, r = 0; + int r = 0; + unsigned int leaf_nodes; /* how many indexes will the btree have ? */ - leaf_nodes = div_up(t->num_targets, KEYS_PER_NODE); + leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); /* leaf layer has already been set up */ @@ -682,7 +704,7 @@ return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0; } -struct dm_target *dm_table_get_target(struct dm_table *t, int index) +struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index) { if (index > t->num_targets) return NULL; @@ -695,7 +717,7 @@ */ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector) { - int l, n = 0, k = 0; + unsigned int l, n = 0, k = 0; sector_t *node; for (l = 0; l < t->depth; l++) { @@ -744,6 +766,31 @@ add_wait_queue(&t->eventq, wq); } +void dm_table_suspend_targets(struct dm_table *t) +{ + int i; + + for (i = 0; i < t->num_targets; i++) { + struct dm_target *ti = t->targets + i; + + if (ti->type->suspend) + ti->type->suspend(ti); + } +} + +void dm_table_resume_targets(struct dm_table *t) +{ + int i; + + for (i = 0; i < t->num_targets; i++) { + struct dm_target *ti = t->targets + i; + + if (ti->type->resume) + ti->type->resume(ti); + } +} + + EXPORT_SYMBOL(dm_get_device); EXPORT_SYMBOL(dm_put_device); EXPORT_SYMBOL(dm_table_event); diff -urN linux-2.5.70-bk14/drivers/md/dm-target.c linux-2.5.70-bk15/drivers/md/dm-target.c --- linux-2.5.70-bk14/drivers/md/dm-target.c 2003-05-26 18:01:00.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm-target.c 2003-06-10 04:43:08.000000000 -0700 @@ -109,9 +109,10 @@ return -ENOMEM; down_write(&_lock); - if (__find_target_type(t->name)) + if (__find_target_type(t->name)) { + kfree(ti); rv = -EEXIST; - else + } else list_add(&ti->list, &_targets); up_write(&_lock); @@ -146,7 +147,7 @@ * io-err: always fails an io, useful for bringing * up LVs that have holes in them. */ -static int io_err_ctr(struct dm_target *ti, int argc, char **args) +static int io_err_ctr(struct dm_target *ti, unsigned int argc, char **args) { return 0; } diff -urN linux-2.5.70-bk14/drivers/md/dm.c linux-2.5.70-bk15/drivers/md/dm.c --- linux-2.5.70-bk14/drivers/md/dm.c 2003-05-26 18:00:46.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm.c 2003-06-10 04:43:08.000000000 -0700 @@ -17,8 +17,8 @@ static const char *_name = DM_NAME; #define MAX_DEVICES 1024 -static int major = 0; -static int _major = 0; +static unsigned int major = 0; +static unsigned int _major = 0; struct dm_io { struct mapped_device *md; @@ -281,9 +281,6 @@ sector_t offset = sector - ti->begin; sector_t len = ti->len - offset; - /* FIXME: obey io_restrictions ! */ - - /* * Does the target need to split even further ? */ @@ -524,7 +521,7 @@ static spinlock_t _minor_lock = SPIN_LOCK_UNLOCKED; static unsigned long _minor_bits[MAX_DEVICES / BITS_PER_LONG]; -static void free_minor(int minor) +static void free_minor(unsigned int minor) { spin_lock(&_minor_lock); clear_bit(minor, _minor_bits); @@ -534,7 +531,7 @@ /* * See if the device with a specific minor # is free. */ -static int specific_minor(int minor) +static int specific_minor(unsigned int minor) { int r = -EBUSY; @@ -546,21 +543,23 @@ spin_lock(&_minor_lock); if (!test_and_set_bit(minor, _minor_bits)) - r = minor; + r = 0; spin_unlock(&_minor_lock); return r; } -static int next_free_minor(void) +static int next_free_minor(unsigned int *minor) { - int minor, r = -EBUSY; + int r = -EBUSY; + unsigned int m; spin_lock(&_minor_lock); - minor = find_first_zero_bit(_minor_bits, MAX_DEVICES); - if (minor != MAX_DEVICES) { - set_bit(minor, _minor_bits); - r = minor; + m = find_first_zero_bit(_minor_bits, MAX_DEVICES); + if (m != MAX_DEVICES) { + set_bit(m, _minor_bits); + *minor = m; + r = 0; } spin_unlock(&_minor_lock); @@ -570,8 +569,9 @@ /* * Allocate and initialise a blank device with a given minor. */ -static struct mapped_device *alloc_dev(int minor) +static struct mapped_device *alloc_dev(unsigned int minor) { + int r; struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL); if (!md) { @@ -580,13 +580,12 @@ } /* get a minor number for the dev */ - minor = (minor < 0) ? next_free_minor() : specific_minor(minor); - if (minor < 0) { + r = (minor < 0) ? next_free_minor(&minor) : specific_minor(minor); + if (r < 0) { kfree(md); return NULL; } - DMWARN("allocating minor %d.", minor); memset(md, 0, sizeof(*md)); init_rwsem(&md->lock); atomic_set(&md->holders, 1); @@ -597,7 +596,7 @@ md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab, mempool_free_slab, _io_cache); if (!md->io_pool) { - free_minor(md->disk->first_minor); + free_minor(minor); kfree(md); return NULL; } @@ -605,7 +604,7 @@ md->disk = alloc_disk(1); if (!md->disk) { mempool_destroy(md->io_pool); - free_minor(md->disk->first_minor); + free_minor(minor); kfree(md); return NULL; } @@ -661,7 +660,8 @@ /* * Constructor for a new device. */ -int dm_create(int minor, struct dm_table *table, struct mapped_device **result) +int dm_create(unsigned int minor, struct dm_table *table, + struct mapped_device **result) { int r; struct mapped_device *md; @@ -675,6 +675,7 @@ free_dev(md); return r; } + dm_table_resume_targets(md->map); *result = md; return 0; @@ -688,7 +689,8 @@ void dm_put(struct mapped_device *md) { if (atomic_dec_and_test(&md->holders)) { - DMWARN("destroying md"); + if (!test_bit(DMF_SUSPENDED, &md->flags)) + dm_table_suspend_targets(md->map); __unbind(md); free_dev(md); } @@ -778,6 +780,7 @@ down_write(&md->lock); remove_wait_queue(&md->wait, &wait); set_bit(DMF_SUSPENDED, &md->flags); + dm_table_suspend_targets(md->map); up_write(&md->lock); return 0; @@ -794,6 +797,7 @@ return -EINVAL; } + dm_table_resume_targets(md->map); clear_bit(DMF_SUSPENDED, &md->flags); clear_bit(DMF_BLOCK_IO, &md->flags); def = md->deferred; diff -urN linux-2.5.70-bk14/drivers/md/dm.h linux-2.5.70-bk15/drivers/md/dm.h --- linux-2.5.70-bk14/drivers/md/dm.h 2003-05-26 18:00:39.000000000 -0700 +++ linux-2.5.70-bk15/drivers/md/dm.h 2003-06-10 04:43:08.000000000 -0700 @@ -51,7 +51,8 @@ * Functions for manipulating a struct mapped_device. * Drop the reference with dm_put when you finish with the object. *---------------------------------------------------------------*/ -int dm_create(int minor, struct dm_table *table, struct mapped_device **md); +int dm_create(unsigned int minor, struct dm_table *table, + struct mapped_device **md); /* * Reference counting for md. @@ -96,13 +97,15 @@ int dm_table_complete(struct dm_table *t); void dm_table_event(struct dm_table *t); sector_t dm_table_get_size(struct dm_table *t); -struct dm_target *dm_table_get_target(struct dm_table *t, int index); +struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index); struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector); void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q); unsigned int dm_table_get_num_targets(struct dm_table *t); struct list_head *dm_table_get_devices(struct dm_table *t); int dm_table_get_mode(struct dm_table *t); void dm_table_add_wait_queue(struct dm_table *t, wait_queue_t *wq); +void dm_table_suspend_targets(struct dm_table *t); +void dm_table_resume_targets(struct dm_table *t); /*----------------------------------------------------------------- * A registry of target types. @@ -132,6 +135,14 @@ } /* + * Ceiling(n / size) + */ +static inline unsigned long dm_div_up(unsigned long n, unsigned long size) +{ + return dm_round_up(n, size) / size; +} + +/* * The device-mapper can be driven through one of two interfaces; * ioctl or filesystem, depending which patch you have applied. */ diff -urN linux-2.5.70-bk14/drivers/net/Space.c linux-2.5.70-bk15/drivers/net/Space.c --- linux-2.5.70-bk14/drivers/net/Space.c 2003-05-26 18:00:23.000000000 -0700 +++ linux-2.5.70-bk15/drivers/net/Space.c 2003-06-10 04:43:08.000000000 -0700 @@ -105,9 +105,6 @@ /* Detachable devices ("pocket adaptors") */ extern int de620_probe(struct net_device *); -/* FDDI adapters */ -extern int skfp_probe(struct net_device *dev); - /* Fibre Channel adapters */ extern int iph5526_probe(struct net_device *dev); @@ -401,29 +398,6 @@ return -ENODEV; } -#ifdef CONFIG_FDDI -static int __init fddiif_probe(struct net_device *dev) -{ - unsigned long base_addr = dev->base_addr; - - if (base_addr == 1) - return 1; /* ENXIO */ - - if (1 -#ifdef CONFIG_APFDDI - && apfddi_init(dev) -#endif -#ifdef CONFIG_SKFP - && skfp_probe(dev) -#endif - && 1 ) { - return 1; /* -ENODEV or -EAGAIN would be more accurate. */ - } - return 0; -} -#endif - - #ifdef CONFIG_NET_FC static int fcif_probe(struct net_device *dev) { @@ -615,52 +589,6 @@ #endif -#ifdef CONFIG_FDDI -static struct net_device fddi7_dev = { - .name = "fddi7", - .next = NEXT_DEV, - .init = fddiif_probe -}; -static struct net_device fddi6_dev = { - .name = "fddi6", - .next = &fddi7_dev, - .init = fddiif_probe -}; -static struct net_device fddi5_dev = { - .name = "fddi5", - .next = &fddi6_dev, - .init = fddiif_probe -}; -static struct net_device fddi4_dev = { - .name = "fddi4", - .next = &fddi5_dev, - .init = fddiif_probe -}; -static struct net_device fddi3_dev = { - .name = "fddi3", - .next = &fddi4_dev, - .init = fddiif_probe -}; -static struct net_device fddi2_dev = { - .name = "fddi2", - .next = &fddi3_dev, - .init = fddiif_probe -}; -static struct net_device fddi1_dev = { - .name = "fddi1", - .next = &fddi2_dev, - .init = fddiif_probe -}; -static struct net_device fddi0_dev = { - .name = "fddi0", - .next = &fddi1_dev, - .init = fddiif_probe -}; -#undef NEXT_DEV -#define NEXT_DEV (&fddi0_dev) -#endif - - #ifdef CONFIG_NET_FC static struct net_device fc1_dev = { .name = "fc1", diff -urN linux-2.5.70-bk14/drivers/net/skfp/skfddi.c linux-2.5.70-bk15/drivers/net/skfp/skfddi.c --- linux-2.5.70-bk14/drivers/net/skfp/skfddi.c 2003-05-26 18:00:21.000000000 -0700 +++ linux-2.5.70-bk15/drivers/net/skfp/skfddi.c 2003-06-10 04:43:08.000000000 -0700 @@ -2539,72 +2539,25 @@ } // drv_reset_indication - -//--------------- functions for use as a module ---------------- - -#ifdef MODULE -/************************ - * - * Note now that module autoprobing is allowed under PCI. The - * IRQ lines will not be auto-detected; instead I'll rely on the BIOSes - * to "do the right thing". - * - ************************/ -#define LP(a) ((struct s_smc*)(a)) static struct net_device *mdev; -/************************ - * - * init_module - * - * If compiled as a module, find - * adapters and initialize them. - * - ************************/ -int init_module(void) +static int __init skfd_init(void) { struct net_device *p; - PRINTK(KERN_INFO "FDDI init module\n"); if ((mdev = insert_device(NULL, skfp_probe)) == NULL) return -ENOMEM; - for (p = mdev; p != NULL; p = LP(p->priv)->os.next_module) { - PRINTK(KERN_INFO "device to register: %s\n", p->name); + for (p = mdev; p != NULL; p = ((struct s_smc *)p->priv)->os.next_module) { if (register_netdev(p) != 0) { printk("skfddi init_module failed\n"); return -EIO; } } - PRINTK(KERN_INFO "+++++ exit with success +++++\n"); return 0; -} // init_module +} -/************************ - * - * cleanup_module - * - * Release all resources claimed by this module. - * - ************************/ -void cleanup_module(void) -{ - PRINTK(KERN_INFO "cleanup_module\n"); - while (mdev != NULL) { - mdev = unlink_modules(mdev); - } - return; -} // cleanup_module - - -/************************ - * - * unlink_modules - * - * Unregister devices and release their memory. - * - ************************/ static struct net_device *unlink_modules(struct net_device *p) { struct net_device *next = NULL; @@ -2638,5 +2591,11 @@ return next; } // unlink_modules +static void __exit skfd_exit(void) +{ + while (mdev) + mdev = unlink_modules(mdev); +} -#endif /* MODULE */ +module_init(skfd_init); +module_exit(skfd_exit); diff -urN linux-2.5.70-bk14/fs/namei.c linux-2.5.70-bk15/fs/namei.c --- linux-2.5.70-bk14/fs/namei.c 2003-06-10 04:43:01.000000000 -0700 +++ linux-2.5.70-bk15/fs/namei.c 2003-06-10 04:43:11.000000000 -0700 @@ -1631,7 +1631,9 @@ error = dir->i_op->unlink(dir, dentry); } up(&dentry->d_inode->i_sem); - if (!error) { + + /* We don't d_delete() NFS sillyrenamed files--they still exist. */ + if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { d_delete(dentry); inode_dir_notify(dir, DN_DELETE); } diff -urN linux-2.5.70-bk14/fs/nfs/unlink.c linux-2.5.70-bk15/fs/nfs/unlink.c --- linux-2.5.70-bk14/fs/nfs/unlink.c 2003-05-26 18:00:58.000000000 -0700 +++ linux-2.5.70-bk15/fs/nfs/unlink.c 2003-06-10 04:43:11.000000000 -0700 @@ -150,8 +150,7 @@ /** * nfs_async_unlink - asynchronous unlinking of a file - * @dir: directory in which the file resides. - * @name: name of the file to unlink. + * @dentry: dentry to unlink */ int nfs_async_unlink(struct dentry *dentry) @@ -190,7 +189,7 @@ } /** - * nfs_complete_remove - Initialize completion of the sillydelete + * nfs_complete_unlink - Initialize completion of the sillydelete * @dentry: dentry to delete * * Since we're most likely to be called by dentry_iput(), we diff -urN linux-2.5.70-bk14/include/linux/device-mapper.h linux-2.5.70-bk15/include/linux/device-mapper.h --- linux-2.5.70-bk14/include/linux/device-mapper.h 2003-05-26 18:00:43.000000000 -0700 +++ linux-2.5.70-bk15/include/linux/device-mapper.h 2003-06-10 04:43:11.000000000 -0700 @@ -17,7 +17,8 @@ * In the constructor the target parameter will already have the * table, type, begin and len fields filled in. */ -typedef int (*dm_ctr_fn) (struct dm_target *target, int argc, char **argv); +typedef int (*dm_ctr_fn) (struct dm_target *target, + unsigned int argc, char **argv); /* * The destructor doesn't need to free the dm_target, just @@ -32,8 +33,12 @@ * > 0: simple remap complete */ typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio); + +typedef void (*dm_suspend_fn) (struct dm_target *ti); +typedef void (*dm_resume_fn) (struct dm_target *ti); + typedef int (*dm_status_fn) (struct dm_target *ti, status_type_t status_type, - char *result, int maxlen); + char *result, unsigned int maxlen); void dm_error(const char *message); @@ -55,6 +60,8 @@ dm_ctr_fn ctr; dm_dtr_fn dtr; dm_map_fn map; + dm_suspend_fn suspend; + dm_resume_fn resume; dm_status_fn status; }; diff -urN linux-2.5.70-bk14/include/linux/if_arp.h linux-2.5.70-bk15/include/linux/if_arp.h --- linux-2.5.70-bk14/include/linux/if_arp.h 2003-05-26 18:00:46.000000000 -0700 +++ linux-2.5.70-bk15/include/linux/if_arp.h 2003-06-10 04:43:12.000000000 -0700 @@ -60,7 +60,7 @@ #define ARPHRD_RAWHDLC 518 /* Raw HDLC */ #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ -#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ +#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ #define ARPHRD_FRAD 770 /* Frame Relay Access Device */ #define ARPHRD_SKIP 771 /* SKIP vif */ #define ARPHRD_LOOPBACK 772 /* Loopback device */ diff -urN linux-2.5.70-bk14/include/linux/ip6_tunnel.h linux-2.5.70-bk15/include/linux/ip6_tunnel.h --- linux-2.5.70-bk14/include/linux/ip6_tunnel.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.70-bk15/include/linux/ip6_tunnel.h 2003-06-10 04:43:12.000000000 -0700 @@ -0,0 +1,32 @@ +/* + * $Id$ + */ + +#ifndef _IP6_TUNNEL_H +#define _IP6_TUNNEL_H + +#define IPV6_TLV_TNL_ENCAP_LIMIT 4 +#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4 + +/* don't add encapsulation limit if one isn't present in inner packet */ +#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1 +/* copy the traffic class field from the inner packet */ +#define IP6_TNL_F_USE_ORIG_TCLASS 0x2 +/* copy the flowlabel from the inner packet */ +#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4 +/* being used for Mobile IPv6 */ +#define IP6_TNL_F_MIP6_DEV 0x8 + +struct ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ + __u32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ +}; + +#endif diff -urN linux-2.5.70-bk14/include/net/flow.h linux-2.5.70-bk15/include/net/flow.h --- linux-2.5.70-bk14/include/net/flow.h 2003-05-26 18:00:56.000000000 -0700 +++ linux-2.5.70-bk15/include/net/flow.h 2003-06-10 04:43:12.000000000 -0700 @@ -87,6 +87,7 @@ extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver); +extern void flow_cache_flush(void *object); extern atomic_t flow_cache_genid; #endif diff -urN linux-2.5.70-bk14/include/net/ip6_tunnel.h linux-2.5.70-bk15/include/net/ip6_tunnel.h --- linux-2.5.70-bk14/include/net/ip6_tunnel.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.70-bk15/include/net/ip6_tunnel.h 2003-06-10 04:43:12.000000000 -0700 @@ -0,0 +1,44 @@ +/* + * $Id$ + */ + +#ifndef _NET_IP6_TUNNEL_H +#define _NET_IP6_TUNNEL_H + +#include +#include +#include + +/* capable of sending packets */ +#define IP6_TNL_F_CAP_XMIT 0x10000 +/* capable of receiving packets */ +#define IP6_TNL_F_CAP_RCV 0x20000 + +#define IP6_TNL_MAX 128 + +/* IPv6 tunnel */ + +struct ip6_tnl { + struct ip6_tnl *next; /* next tunnel in list */ + struct net_device *dev; /* virtual device associated with tunnel */ + struct net_device_stats stat; /* statistics for tunnel device */ + int recursion; /* depth of hard_start_xmit recursion */ + struct ip6_tnl_parm parms; /* tunnel configuration paramters */ + struct flowi fl; /* flowi template for xmit */ +}; + +/* Tunnel encapsulation limit destination sub-option */ + +struct ipv6_tlv_tnl_enc_lim { + __u8 type; /* type-code for option */ + __u8 length; /* option length */ + __u8 encap_limit; /* tunnel encapsulation limit */ +} __attribute__ ((packed)); + +#ifdef __KERNEL__ +#ifdef CONFIG_IPV6_TUNNEL +extern int __init ip6_tunnel_init(void); +extern void ip6_tunnel_cleanup(void); +#endif +#endif +#endif diff -urN linux-2.5.70-bk14/include/net/ipv6.h linux-2.5.70-bk15/include/net/ipv6.h --- linux-2.5.70-bk14/include/net/ipv6.h 2003-06-10 04:43:01.000000000 -0700 +++ linux-2.5.70-bk15/include/net/ipv6.h 2003-06-10 04:43:12.000000000 -0700 @@ -263,6 +263,21 @@ memcpy((void *) a1, (const void *) a2, sizeof(struct in6_addr)); } +static inline void ipv6_addr_prefix(struct in6_addr *pfx, + const struct in6_addr *addr, + int plen) +{ + /* caller must guarantee 0 <= plen <= 128 */ + int o = plen >> 3, + b = plen & 0x7; + + memcpy(pfx->s6_addr, addr, o); + if (b != 0) + pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); + if (o < 16) + memset(pfx->s6_addr + o, 0, 16 - o); +} + #ifndef __HAVE_ARCH_ADDR_SET static inline void ipv6_addr_set(struct in6_addr *addr, __u32 w1, __u32 w2, diff -urN linux-2.5.70-bk14/include/net/xfrm.h linux-2.5.70-bk15/include/net/xfrm.h --- linux-2.5.70-bk14/include/net/xfrm.h 2003-06-10 04:43:01.000000000 -0700 +++ linux-2.5.70-bk15/include/net/xfrm.h 2003-06-10 04:43:12.000000000 -0700 @@ -266,6 +266,7 @@ struct xfrm_policy { struct xfrm_policy *next; + struct list_head list; /* This lock only affects elements except for entry. */ rwlock_t lock; diff -urN linux-2.5.70-bk14/lib/zlib_deflate/deflate.c linux-2.5.70-bk15/lib/zlib_deflate/deflate.c --- linux-2.5.70-bk14/lib/zlib_deflate/deflate.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/lib/zlib_deflate/deflate.c 2003-06-10 04:43:12.000000000 -0700 @@ -872,8 +872,9 @@ * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ -static void fill_window(s) - deflate_state *s; +static void fill_window( + deflate_state *s +) { register unsigned n, m; register Pos *p; @@ -1261,7 +1262,7 @@ return flush == Z_FINISH ? finish_done : block_done; } -extern int zlib_deflate_workspacesize () +extern int zlib_deflate_workspacesize(void) { return sizeof(deflate_workspace); } diff -urN linux-2.5.70-bk14/net/core/flow.c linux-2.5.70-bk15/net/core/flow.c --- linux-2.5.70-bk14/net/core/flow.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/core/flow.c 2003-06-10 04:43:12.000000000 -0700 @@ -12,8 +12,12 @@ #include #include #include +#include +#include +#include #include #include +#include struct flow_cache_entry { struct flow_cache_entry *next; @@ -49,6 +53,14 @@ #define FLOW_HASH_RND_PERIOD (10 * 60 * HZ) +struct flow_flush_info { + void *object; + atomic_t cpuleft; + struct completion completion; +}; +static struct tasklet_struct flow_flush_tasklets[NR_CPUS]; +static DECLARE_MUTEX(flow_flush_sem); + static void flow_cache_new_hashrnd(unsigned long arg) { int i; @@ -170,6 +182,22 @@ } } + if (!fle) { + if (flow_count(cpu) > flow_hwm) + flow_cache_shrink(cpu); + + fle = kmem_cache_alloc(flow_cachep, SLAB_ATOMIC); + if (fle) { + fle->next = *head; + *head = fle; + fle->family = family; + fle->dir = dir; + memcpy(&fle->key, key, sizeof(*key)); + fle->object = NULL; + flow_count(cpu)++; + } + } + { void *obj; atomic_t *obj_ref; @@ -186,25 +214,6 @@ fle->object_ref = obj_ref; if (obj) atomic_inc(fle->object_ref); - } else { - if (flow_count(cpu) > flow_hwm) - flow_cache_shrink(cpu); - - fle = kmem_cache_alloc(flow_cachep, SLAB_ATOMIC); - if (fle) { - fle->next = *head; - *head = fle; - fle->family = family; - fle->dir = dir; - memcpy(&fle->key, key, sizeof(*key)); - fle->genid = atomic_read(&flow_cache_genid); - fle->object = obj; - fle->object_ref = obj_ref; - if (obj) - atomic_inc(fle->object_ref); - - flow_count(cpu)++; - } } local_bh_enable(); @@ -212,6 +221,62 @@ } } +static void flow_cache_flush_tasklet(unsigned long data) +{ + struct flow_flush_info *info = (void *)data; + void *object = info->object; + int i; + int cpu; + + cpu = smp_processor_id(); + for (i = 0; i < flow_hash_size; i++) { + struct flow_cache_entry *fle, **flp; + + flp = &flow_table[(cpu << flow_hash_shift) + i]; + for (; (fle = *flp) != NULL; flp = &fle->next) { + if (fle->object != object) + continue; + fle->object = NULL; + atomic_dec(fle->object_ref); + } + } + + if (atomic_dec_and_test(&info->cpuleft)) + complete(&info->completion); +} + +static void flow_cache_flush_per_cpu(void *data) +{ + struct flow_flush_info *info = data; + int cpu; + struct tasklet_struct *tasklet; + + cpu = smp_processor_id(); + tasklet = &flow_flush_tasklets[cpu]; + tasklet_init(tasklet, flow_cache_flush_tasklet, (unsigned long)info); + tasklet_schedule(tasklet); +} + +void flow_cache_flush(void *object) +{ + struct flow_flush_info info; + + info.object = object; + atomic_set(&info.cpuleft, num_online_cpus()); + init_completion(&info.completion); + + down(&flow_flush_sem); + + smp_call_function(flow_cache_flush_per_cpu, &info, 1, 0); + local_bh_disable(); + flow_cache_flush_per_cpu(&info); + local_bh_enable(); + + wait_for_completion(&info.completion); + + up(&flow_flush_sem); +} + static int __init flow_cache_init(void) { unsigned long order; diff -urN linux-2.5.70-bk14/net/ipv6/Kconfig linux-2.5.70-bk15/net/ipv6/Kconfig --- linux-2.5.70-bk14/net/ipv6/Kconfig 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/ipv6/Kconfig 2003-06-10 04:43:12.000000000 -0700 @@ -55,4 +55,12 @@ If unsure, say Y. +config IPV6_TUNNEL + tristate "IPv6: IPv6-in-IPv6 tunnel" + depends on IPV6 + ---help--- + Support for IPv6-in-IPv6 tunnels described in RFC 2473. + + If unsure, say N. + source "net/ipv6/netfilter/Kconfig" diff -urN linux-2.5.70-bk14/net/ipv6/Makefile linux-2.5.70-bk15/net/ipv6/Makefile --- linux-2.5.70-bk14/net/ipv6/Makefile 2003-05-26 18:00:45.000000000 -0700 +++ linux-2.5.70-bk15/net/ipv6/Makefile 2003-06-10 04:43:12.000000000 -0700 @@ -15,3 +15,5 @@ obj-$(CONFIG_INET6_ESP) += esp6.o obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o obj-$(CONFIG_NETFILTER) += netfilter/ + +obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o diff -urN linux-2.5.70-bk14/net/ipv6/addrconf.c linux-2.5.70-bk15/net/ipv6/addrconf.c --- linux-2.5.70-bk14/net/ipv6/addrconf.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/ipv6/addrconf.c 2003-06-10 04:43:12.000000000 -0700 @@ -400,38 +400,6 @@ return idev; } -void ipv6_addr_prefix(struct in6_addr *prefix, - struct in6_addr *addr, int prefix_len) -{ - unsigned long mask; - int ncopy, nbits; - - memset(prefix, 0, sizeof(*prefix)); - - if (prefix_len <= 0) - return; - if (prefix_len > 128) - prefix_len = 128; - - ncopy = prefix_len / 32; - switch (ncopy) { - case 4: prefix->s6_addr32[3] = addr->s6_addr32[3]; - case 3: prefix->s6_addr32[2] = addr->s6_addr32[2]; - case 2: prefix->s6_addr32[1] = addr->s6_addr32[1]; - case 1: prefix->s6_addr32[0] = addr->s6_addr32[0]; - case 0: break; - } - nbits = prefix_len % 32; - if (nbits == 0) - return; - - mask = ~((1 << (32 - nbits)) - 1); - mask = htonl(mask); - - prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask; -} - - static void dev_forward_change(struct inet6_dev *idev) { struct net_device *dev; diff -urN linux-2.5.70-bk14/net/ipv6/af_inet6.c linux-2.5.70-bk15/net/ipv6/af_inet6.c --- linux-2.5.70-bk14/net/ipv6/af_inet6.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/ipv6/af_inet6.c 2003-06-10 04:43:12.000000000 -0700 @@ -57,6 +57,9 @@ #include #include #include +#if CONFIG_IPV6_TUNNEL +#include +#endif #include #include @@ -776,6 +779,11 @@ err = ndisc_init(&inet6_family_ops); if (err) goto ndisc_fail; +#ifdef CONFIG_IPV6_TUNNEL + err = ip6_tunnel_init(); + if (err) + goto ip6_tunnel_fail; +#endif err = igmp6_init(&inet6_family_ops); if (err) goto igmp_fail; @@ -830,6 +838,10 @@ igmp6_cleanup(); #endif igmp_fail: +#ifdef CONFIG_IPV6_TUNNEL + ip6_tunnel_cleanup(); +ip6_tunnel_fail: +#endif ndisc_cleanup(); ndisc_fail: icmpv6_cleanup(); @@ -865,6 +877,9 @@ ip6_route_cleanup(); ipv6_packet_cleanup(); igmp6_cleanup(); +#ifdef CONFIG_IPV6_TUNNEL + ip6_tunnel_cleanup(); +#endif ndisc_cleanup(); icmpv6_cleanup(); #ifdef CONFIG_SYSCTL diff -urN linux-2.5.70-bk14/net/ipv6/ip6_tunnel.c linux-2.5.70-bk15/net/ipv6/ip6_tunnel.c --- linux-2.5.70-bk14/net/ipv6/ip6_tunnel.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.5.70-bk15/net/ipv6/ip6_tunnel.c 2003-06-10 04:43:12.000000000 -0700 @@ -0,0 +1,1261 @@ +/* + * IPv6 over IPv6 tunnel device + * Linux INET6 implementation + * + * Authors: + * Ville Nuorvala + * + * $Id$ + * + * Based on: + * linux/net/ipv6/sit.c + * + * RFC 2473 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Ville Nuorvala"); +MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); +MODULE_LICENSE("GPL"); + +#define IPV6_TLV_TEL_DST_SIZE 8 + +#ifdef IP6_TNL_DEBUG +#define IP6_TNL_TRACE(x...) printk(KERN_DEBUG "%s:" x "\n", __FUNCTION__) +#else +#define IP6_TNL_TRACE(x...) do {;} while(0) +#endif + +#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) + +/* socket(s) used by ip6ip6_tnl_xmit() for resending packets */ +static struct socket *__ip6_socket[NR_CPUS]; +#define ip6_socket __ip6_socket[smp_processor_id()] + +static void ip6_xmit_lock(void) +{ + local_bh_disable(); + if (unlikely(!spin_trylock(&ip6_socket->sk->sk_lock.slock))) + BUG(); +} + +static void ip6_xmit_unlock(void) +{ + spin_unlock_bh(&ip6_socket->sk->sk_lock.slock); +} + +#define HASH_SIZE 32 + +#define HASH(addr) (((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \ + (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ + (HASH_SIZE - 1)) + +static int ip6ip6_fb_tnl_dev_init(struct net_device *dev); +static int ip6ip6_tnl_dev_init(struct net_device *dev); + +/* the IPv6 tunnel fallback device */ +static struct net_device ip6ip6_fb_tnl_dev = { + .name = "ip6tnl0", + .init = ip6ip6_fb_tnl_dev_init +}; + +/* the IPv6 fallback tunnel */ +static struct ip6_tnl ip6ip6_fb_tnl = { + .dev = &ip6ip6_fb_tnl_dev, + .parms ={.name = "ip6tnl0", .proto = IPPROTO_IPV6} +}; + +/* lists for storing tunnels in use */ +static struct ip6_tnl *tnls_r_l[HASH_SIZE]; +static struct ip6_tnl *tnls_wc[1]; +static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l }; + +/* lock for the tunnel lists */ +static rwlock_t ip6ip6_lock = RW_LOCK_UNLOCKED; + +/** + * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses + * @remote: the address of the tunnel exit-point + * @local: the address of the tunnel entry-point + * + * Return: + * tunnel matching given end-points if found, + * else fallback tunnel if its device is up, + * else %NULL + **/ + +struct ip6_tnl * +ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local) +{ + unsigned h0 = HASH(remote); + unsigned h1 = HASH(local); + struct ip6_tnl *t; + + for (t = tnls_r_l[h0 ^ h1]; t; t = t->next) { + if (!ipv6_addr_cmp(local, &t->parms.laddr) && + !ipv6_addr_cmp(remote, &t->parms.raddr) && + (t->dev->flags & IFF_UP)) + return t; + } + if ((t = tnls_wc[0]) != NULL && (t->dev->flags & IFF_UP)) + return t; + + return NULL; +} + +/** + * ip6ip6_bucket - get head of list matching given tunnel parameters + * @p: parameters containing tunnel end-points + * + * Description: + * ip6ip6_bucket() returns the head of the list matching the + * &struct in6_addr entries laddr and raddr in @p. + * + * Return: head of IPv6 tunnel list + **/ + +static struct ip6_tnl ** +ip6ip6_bucket(struct ip6_tnl_parm *p) +{ + struct in6_addr *remote = &p->raddr; + struct in6_addr *local = &p->laddr; + unsigned h = 0; + int prio = 0; + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; + h = HASH(remote) ^ HASH(local); + } + return &tnls[prio][h]; +} + +/** + * ip6ip6_tnl_link - add tunnel to hash table + * @t: tunnel to be added + **/ + +static void +ip6ip6_tnl_link(struct ip6_tnl *t) +{ + struct ip6_tnl **tp = ip6ip6_bucket(&t->parms); + + write_lock_bh(&ip6ip6_lock); + t->next = *tp; + write_unlock_bh(&ip6ip6_lock); + *tp = t; +} + +/** + * ip6ip6_tnl_unlink - remove tunnel from hash table + * @t: tunnel to be removed + **/ + +static void +ip6ip6_tnl_unlink(struct ip6_tnl *t) +{ + struct ip6_tnl **tp; + + for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) { + if (t == *tp) { + write_lock_bh(&ip6ip6_lock); + *tp = t->next; + write_unlock_bh(&ip6ip6_lock); + break; + } + } +} + +/** + * ip6_tnl_create() - create a new tunnel + * @p: tunnel parameters + * @pt: pointer to new tunnel + * + * Description: + * Create tunnel matching given parameters. + * + * Return: + * 0 on success + **/ + +static int +ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) +{ + struct net_device *dev; + int err = -ENOBUFS; + struct ip6_tnl *t; + + dev = kmalloc(sizeof (*dev) + sizeof (*t), GFP_KERNEL); + if (!dev) + return err; + + memset(dev, 0, sizeof (*dev) + sizeof (*t)); + dev->priv = (void *) (dev + 1); + t = (struct ip6_tnl *) dev->priv; + t->dev = dev; + dev->init = ip6ip6_tnl_dev_init; + memcpy(&t->parms, p, sizeof (*p)); + t->parms.name[IFNAMSIZ - 1] = '\0'; + if (t->parms.hop_limit > 255) + t->parms.hop_limit = -1; + strcpy(dev->name, t->parms.name); + if (!dev->name[0]) { + int i = 0; + int exists = 0; + + do { + sprintf(dev->name, "ip6tnl%d", ++i); + exists = (__dev_get_by_name(dev->name) != NULL); + } while (i < IP6_TNL_MAX && exists); + + if (i == IP6_TNL_MAX) { + goto failed; + } + memcpy(t->parms.name, dev->name, IFNAMSIZ); + } + SET_MODULE_OWNER(dev); + if ((err = register_netdevice(dev)) < 0) { + goto failed; + } + ip6ip6_tnl_link(t); + *pt = t; + return 0; +failed: + kfree(dev); + return err; +} + +/** + * ip6_tnl_destroy() - destroy old tunnel + * @t: tunnel to be destroyed + * + * Return: + * whatever unregister_netdevice() returns + **/ + +static inline int +ip6_tnl_destroy(struct ip6_tnl *t) +{ + return unregister_netdevice(t->dev); +} + +/** + * ip6ip6_tnl_locate - find or create tunnel matching given parameters + * @p: tunnel parameters + * @create: != 0 if allowed to create new tunnel if no match found + * + * Description: + * ip6ip6_tnl_locate() first tries to locate an existing tunnel + * based on @parms. If this is unsuccessful, but @create is set a new + * tunnel device is created and registered for use. + * + * Return: + * 0 if tunnel located or created, + * -EINVAL if parameters incorrect, + * -ENODEV if no matching tunnel available + **/ + +static int +ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create) +{ + struct in6_addr *remote = &p->raddr; + struct in6_addr *local = &p->laddr; + struct ip6_tnl *t; + + if (p->proto != IPPROTO_IPV6) + return -EINVAL; + + for (t = *ip6ip6_bucket(p); t; t = t->next) { + if (!ipv6_addr_cmp(local, &t->parms.laddr) && + !ipv6_addr_cmp(remote, &t->parms.raddr)) { + *pt = t; + return (create ? -EEXIST : 0); + } + } + if (!create) { + return -ENODEV; + } + return ip6_tnl_create(p, pt); +} + +/** + * ip6ip6_tnl_dev_destructor - tunnel device destructor + * @dev: the device to be destroyed + **/ + +static void +ip6ip6_tnl_dev_destructor(struct net_device *dev) +{ + kfree(dev); +} + +/** + * ip6ip6_tnl_dev_uninit - tunnel device uninitializer + * @dev: the device to be destroyed + * + * Description: + * ip6ip6_tnl_dev_uninit() removes tunnel from its list + **/ + +static void +ip6ip6_tnl_dev_uninit(struct net_device *dev) +{ + if (dev == &ip6ip6_fb_tnl_dev) { + write_lock_bh(&ip6ip6_lock); + tnls_wc[0] = NULL; + write_unlock_bh(&ip6ip6_lock); + } else { + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + ip6ip6_tnl_unlink(t); + } +} + +/** + * parse_tvl_tnl_enc_lim - handle encapsulation limit option + * @skb: received socket buffer + * + * Return: + * 0 if none was found, + * else index to encapsulation limit + **/ + +static __u16 +parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) +{ + struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw; + __u8 nexthdr = ipv6h->nexthdr; + __u16 off = sizeof (*ipv6h); + + while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { + __u16 optlen = 0; + struct ipv6_opt_hdr *hdr; + if (raw + off + sizeof (*hdr) > skb->data && + !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) + break; + + hdr = (struct ipv6_opt_hdr *) (raw + off); + if (nexthdr == NEXTHDR_FRAGMENT) { + struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; + if (frag_hdr->frag_off) + break; + optlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) { + optlen = (hdr->hdrlen + 2) << 2; + } else { + optlen = ipv6_optlen(hdr); + } + if (nexthdr == NEXTHDR_DEST) { + __u16 i = off + 2; + while (1) { + struct ipv6_tlv_tnl_enc_lim *tel; + + /* No more room for encapsulation limit */ + if (i + sizeof (*tel) > off + optlen) + break; + + tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; + /* return index of option if found and valid */ + if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && + tel->length == 1) + return i; + /* else jump to next option */ + if (tel->type) + i += tel->length + 2; + else + i++; + } + } + nexthdr = hdr->nexthdr; + off += optlen; + } + return 0; +} + +/** + * ip6ip6_err - tunnel error handler + * + * Description: + * ip6ip6_err() should handle errors in the tunnel according + * to the specifications in RFC 2473. + **/ + +void ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; + struct ip6_tnl *t; + int rel_msg = 0; + int rel_type = ICMPV6_DEST_UNREACH; + int rel_code = ICMPV6_ADDR_UNREACH; + __u32 rel_info = 0; + __u16 len; + + /* If the packet doesn't contain the original IPv6 header we are + in trouble since we might need the source address for furter + processing of the error. */ + + read_lock(&ip6ip6_lock); + if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL) + goto out; + + switch (type) { + __u32 teli; + struct ipv6_tlv_tnl_enc_lim *tel; + __u32 mtu; + case ICMPV6_DEST_UNREACH: + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Path to destination invalid " + "or inactive!\n", t->parms.name); + rel_msg = 1; + break; + case ICMPV6_TIME_EXCEED: + if (code == ICMPV6_EXC_HOPLIMIT) { + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Too small hop limit or " + "routing loop in tunnel!\n", + t->parms.name); + rel_msg = 1; + } + break; + case ICMPV6_PARAMPROB: + /* ignore if parameter problem not caused by a tunnel + encapsulation limit sub-option */ + if (code != ICMPV6_HDR_FIELD) { + break; + } + teli = parse_tlv_tnl_enc_lim(skb, skb->data); + + if (teli && teli == info - 2) { + tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; + if (tel->encap_limit <= 1) { + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Too small encapsulation " + "limit or routing loop in " + "tunnel!\n", t->parms.name); + rel_msg = 1; + } + } + break; + case ICMPV6_PKT_TOOBIG: + mtu = info - offset; + if (mtu <= IPV6_MIN_MTU) { + mtu = IPV6_MIN_MTU; + } + t->dev->mtu = mtu; + + if ((len = sizeof (*ipv6h) + ipv6h->payload_len) > mtu) { + rel_type = ICMPV6_PKT_TOOBIG; + rel_code = 0; + rel_info = mtu; + rel_msg = 1; + } + break; + } + if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { + struct rt6_info *rt; + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + if (!skb2) + goto out; + + dst_release(skb2->dst); + skb2->dst = NULL; + skb_pull(skb2, offset); + skb2->nh.raw = skb2->data; + + /* Try to guess incoming interface */ + rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0); + + if (rt && rt->rt6i_dev) + skb2->dev = rt->rt6i_dev; + + icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev); + + if (rt) + dst_free(&rt->u.dst); + + kfree_skb(skb2); + } +out: + read_unlock(&ip6ip6_lock); +} + +/** + * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally + * @skb: received socket buffer + * + * Return: 0 + **/ + +int ip6ip6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) +{ + struct sk_buff *skb = *pskb; + struct ipv6hdr *ipv6h; + struct ip6_tnl *t; + + if (!pskb_may_pull(skb, sizeof (*ipv6h))) + goto discard; + + ipv6h = skb->nh.ipv6h; + + read_lock(&ip6ip6_lock); + + if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { + if (!(t->parms.flags & IP6_TNL_F_CAP_RCV)) { + t->stat.rx_dropped++; + read_unlock(&ip6ip6_lock); + goto discard; + } + skb->mac.raw = skb->nh.raw; + skb->nh.raw = skb->data; + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + skb->dev = t->dev; + dst_release(skb->dst); + skb->dst = NULL; + t->stat.rx_packets++; + t->stat.rx_bytes += skb->len; + netif_rx(skb); + read_unlock(&ip6ip6_lock); + return 0; + } + read_unlock(&ip6ip6_lock); + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); +discard: + kfree_skb(skb); + return 0; +} + +/** + * txopt_len - get necessary size for new &struct ipv6_txoptions + * @orig_opt: old options + * + * Return: + * Size of old one plus size of tunnel encapsulation limit option + **/ + +static inline int +txopt_len(struct ipv6_txoptions *orig_opt) +{ + int len = sizeof (*orig_opt) + 8; + + if (orig_opt && orig_opt->dst0opt) + len += ipv6_optlen(orig_opt->dst0opt); + return len; +} + +/** + * merge_options - add encapsulation limit to original options + * @encap_limit: number of allowed encapsulation limits + * @orig_opt: original options + * + * Return: + * Pointer to new &struct ipv6_txoptions containing the tunnel + * encapsulation limit + **/ + +static struct ipv6_txoptions * +merge_options(struct sock *sk, __u8 encap_limit, + struct ipv6_txoptions *orig_opt) +{ + struct ipv6_tlv_tnl_enc_lim *tel; + struct ipv6_txoptions *opt; + __u8 *raw; + __u8 pad_to = 8; + int opt_len = txopt_len(orig_opt); + + if (!(opt = sock_kmalloc(sk, opt_len, GFP_ATOMIC))) { + return NULL; + } + + memset(opt, 0, opt_len); + opt->tot_len = opt_len; + opt->dst0opt = (struct ipv6_opt_hdr *) (opt + 1); + opt->opt_nflen = 8; + + raw = (__u8 *) opt->dst0opt; + + tel = (struct ipv6_tlv_tnl_enc_lim *) (opt->dst0opt + 1); + tel->type = IPV6_TLV_TNL_ENCAP_LIMIT; + tel->length = 1; + tel->encap_limit = encap_limit; + + if (orig_opt) { + __u8 *orig_raw; + + opt->hopopt = orig_opt->hopopt; + + /* Keep the original destination options properly + aligned and merge possible old paddings to the + new padding option */ + if ((orig_raw = (__u8 *) orig_opt->dst0opt) != NULL) { + __u8 type; + int i = sizeof (struct ipv6_opt_hdr); + pad_to += sizeof (struct ipv6_opt_hdr); + while (i < ipv6_optlen(orig_opt->dst0opt)) { + type = orig_raw[i++]; + if (type == IPV6_TLV_PAD0) + pad_to++; + else if (type == IPV6_TLV_PADN) { + int len = orig_raw[i++]; + i += len; + pad_to += len + 2; + } else { + break; + } + } + opt->dst0opt->hdrlen = orig_opt->dst0opt->hdrlen + 1; + memcpy(raw + pad_to, orig_raw + pad_to - 8, + opt_len - sizeof (*opt) - pad_to); + } + opt->srcrt = orig_opt->srcrt; + opt->opt_nflen += orig_opt->opt_nflen; + + opt->dst1opt = orig_opt->dst1opt; + opt->auth = orig_opt->auth; + opt->opt_flen = orig_opt->opt_flen; + } + raw[5] = IPV6_TLV_PADN; + + /* subtract lengths of destination suboption header, + tunnel encapsulation limit and pad N header */ + raw[6] = pad_to - 7; + + return opt; +} + +/** + * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device + * @hdr: IPv6 header from the incoming packet + * + * Description: + * Avoid trivial tunneling loop by checking that tunnel exit-point + * doesn't match source of incoming packet. + * + * Return: + * 1 if conflict, + * 0 else + **/ + +static inline int +ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) +{ + return !ipv6_addr_cmp(&t->parms.raddr, &hdr->saddr); +} + +/** + * ip6ip6_tnl_xmit - encapsulate packet and send + * @skb: the outgoing socket buffer + * @dev: the outgoing tunnel device + * + * Description: + * Build new header and do some sanity checks on the packet before sending + * it to ip6_build_xmit(). + * + * Return: + * 0 + **/ + +int ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + struct net_device_stats *stats = &t->stat; + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + struct ipv6_txoptions *orig_opt = NULL; + struct ipv6_txoptions *opt = NULL; + __u8 encap_limit = 0; + __u16 offset; + struct flowi fl; + struct ip6_flowlabel *fl_lbl = NULL; + int err = 0; + struct dst_entry *dst; + int link_failure = 0; + struct sock *sk = ip6_socket->sk; + struct ipv6_pinfo *np = inet6_sk(sk); + int mtu; + + if (t->recursion++) { + stats->collisions++; + goto tx_err; + } + if (skb->protocol != htons(ETH_P_IPV6) || + !(t->parms.flags & IP6_TNL_F_CAP_XMIT) || + ip6ip6_tnl_addr_conflict(t, ipv6h)) { + goto tx_err; + } + if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { + struct ipv6_tlv_tnl_enc_lim *tel; + tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; + if (tel->encap_limit <= 1) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, offset + 2, skb->dev); + goto tx_err; + } + encap_limit = tel->encap_limit - 1; + } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { + encap_limit = t->parms.encap_limit; + } + ip6_xmit_lock(); + + memcpy(&fl, &t->fl, sizeof (fl)); + + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) + fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_TCLASS_MASK); + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) + fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_FLOWLABEL_MASK); + + if (fl.fl6_flowlabel) { + fl_lbl = fl6_sock_lookup(sk, fl.fl6_flowlabel); + if (fl_lbl) + orig_opt = fl_lbl->opt; + } + if (encap_limit > 0) { + if (!(opt = merge_options(sk, encap_limit, orig_opt))) { + goto tx_err_free_fl_lbl; + } + } else { + opt = orig_opt; + } + dst = __sk_dst_check(sk, np->dst_cookie); + + if (dst) { + if (np->daddr_cache == NULL || + ipv6_addr_cmp(&fl.fl6_dst, np->daddr_cache) || + (fl.oif && fl.oif != dst->dev->ifindex)) { + dst = NULL; + } + } + if (dst == NULL) { + dst = ip6_route_output(sk, &fl); + if (dst->error) { + stats->tx_carrier_errors++; + link_failure = 1; + goto tx_err_dst_release; + } + /* local routing loop */ + if (dst->dev == dev) { + stats->collisions++; + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Local routing loop detected!\n", + t->parms.name); + goto tx_err_dst_release; + } + ipv6_addr_copy(&np->daddr, &fl.fl6_dst); + ipv6_addr_copy(&np->saddr, &fl.fl6_src); + } + mtu = dst_pmtu(dst) - sizeof (*ipv6h); + if (opt) { + mtu -= (opt->opt_nflen + opt->opt_flen); + } + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + if (skb->dst && mtu < dst_pmtu(skb->dst)) { + struct rt6_info *rt = (struct rt6_info *) skb->dst; + rt->rt6i_flags |= RTF_MODIFIED; + rt->u.dst.metrics[RTAX_MTU-1] = mtu; + } + if (skb->len > mtu) { + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); + goto tx_err_opt_release; + } + err = ip6_append_data(sk, ip_generic_getfrag, skb->nh.raw, skb->len, 0, + t->parms.hop_limit, opt, &fl, + (struct rt6_info *)dst, MSG_DONTWAIT); + + if (err) { + ip6_flush_pending_frames(sk); + } else { + err = ip6_push_pending_frames(sk); + err = (err < 0 ? err : 0); + } + if (!err) { + stats->tx_bytes += skb->len; + stats->tx_packets++; + } else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } + if (opt && opt != orig_opt) + sock_kfree_s(sk, opt, opt->tot_len); + + fl6_sock_release(fl_lbl); + ip6_dst_store(sk, dst, &np->daddr); + ip6_xmit_unlock(); + kfree_skb(skb); + t->recursion--; + return 0; +tx_err_dst_release: + dst_release(dst); +tx_err_opt_release: + if (opt && opt != orig_opt) + sock_kfree_s(sk, opt, opt->tot_len); +tx_err_free_fl_lbl: + fl6_sock_release(fl_lbl); + ip6_xmit_unlock(); + if (link_failure) + dst_link_failure(skb); +tx_err: + stats->tx_errors++; + stats->tx_dropped++; + kfree_skb(skb); + t->recursion--; + return 0; +} + +static void ip6_tnl_set_cap(struct ip6_tnl *t) +{ + struct ip6_tnl_parm *p = &t->parms; + struct in6_addr *laddr = &p->laddr; + struct in6_addr *raddr = &p->raddr; + int ltype = ipv6_addr_type(laddr); + int rtype = ipv6_addr_type(raddr); + + p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); + + if (ltype != IPV6_ADDR_ANY && rtype != IPV6_ADDR_ANY && + ((ltype|rtype) & + (IPV6_ADDR_UNICAST| + IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL| + IPV6_ADDR_MAPPED|IPV6_ADDR_RESERVED)) == IPV6_ADDR_UNICAST) { + struct net_device *ldev = NULL; + int l_ok = 1; + int r_ok = 1; + + if (p->link) + ldev = dev_get_by_index(p->link); + + if ((ltype&IPV6_ADDR_UNICAST) && !ipv6_chk_addr(laddr, ldev)) + l_ok = 0; + + if ((rtype&IPV6_ADDR_UNICAST) && ipv6_chk_addr(raddr, NULL)) + r_ok = 0; + + if (l_ok && r_ok) { + if (ltype&IPV6_ADDR_UNICAST) + p->flags |= IP6_TNL_F_CAP_XMIT; + if (rtype&IPV6_ADDR_UNICAST) + p->flags |= IP6_TNL_F_CAP_RCV; + } + if (ldev) + dev_put(ldev); + } +} + + +static void ip6ip6_tnl_link_config(struct ip6_tnl *t) +{ + struct net_device *dev = t->dev; + struct ip6_tnl_parm *p = &t->parms; + struct flowi *fl; + /* Set up flowi template */ + fl = &t->fl; + ipv6_addr_copy(&fl->fl6_src, &p->laddr); + ipv6_addr_copy(&fl->fl6_dst, &p->raddr); + fl->oif = p->link; + fl->fl6_flowlabel = 0; + + if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) + fl->fl6_flowlabel |= IPV6_TCLASS_MASK & htonl(p->flowinfo); + if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) + fl->fl6_flowlabel |= IPV6_FLOWLABEL_MASK & htonl(p->flowinfo); + + ip6_tnl_set_cap(t); + + if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV) + dev->flags |= IFF_POINTOPOINT; + else + dev->flags &= ~IFF_POINTOPOINT; + + if (p->flags & IP6_TNL_F_CAP_XMIT) { + struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, + p->link, 0); + if (rt) { + struct net_device *rtdev; + if (!(rtdev = rt->rt6i_dev) || + rtdev->type == ARPHRD_TUNNEL6) { + /* as long as tunnels use the same socket + for transmission, locally nested tunnels + won't work */ + dst_release(&rt->u.dst); + goto no_link; + } else { + dev->iflink = rtdev->ifindex; + dev->hard_header_len = rtdev->hard_header_len + + sizeof (struct ipv6hdr); + dev->mtu = rtdev->mtu - sizeof (struct ipv6hdr); + if (dev->mtu < IPV6_MIN_MTU) + dev->mtu = IPV6_MIN_MTU; + + dst_release(&rt->u.dst); + } + } + } else { + no_link: + dev->iflink = 0; + dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); + dev->mtu = ETH_DATA_LEN - sizeof (struct ipv6hdr); + } +} + +/** + * ip6ip6_tnl_change - update the tunnel parameters + * @t: tunnel to be changed + * @p: tunnel configuration parameters + * @active: != 0 if tunnel is ready for use + * + * Description: + * ip6ip6_tnl_change() updates the tunnel parameters + **/ + +static int +ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) +{ + ipv6_addr_copy(&t->parms.laddr, &p->laddr); + ipv6_addr_copy(&t->parms.raddr, &p->raddr); + t->parms.flags = p->flags; + t->parms.hop_limit = (p->hop_limit <= 255 ? p->hop_limit : -1); + t->parms.encap_limit = p->encap_limit; + t->parms.flowinfo = p->flowinfo; + ip6ip6_tnl_link_config(t); + return 0; +} + +/** + * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace + * @dev: virtual device associated with tunnel + * @ifr: parameters passed from userspace + * @cmd: command to be performed + * + * Description: + * ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels + * from userspace. + * + * The possible commands are the following: + * %SIOCGETTUNNEL: get tunnel parameters for device + * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters + * %SIOCCHGTUNNEL: change tunnel parameters to those given + * %SIOCDELTUNNEL: delete tunnel + * + * The fallback device "ip6tnl0", created during module + * initialization, can be used for creating other tunnel devices. + * + * Return: + * 0 on success, + * %-EFAULT if unable to copy data to or from userspace, + * %-EPERM if current process hasn't %CAP_NET_ADMIN set + * %-EINVAL if passed tunnel parameters are invalid, + * %-EEXIST if changing a tunnel's parameters would cause a conflict + * %-ENODEV if attempting to change or delete a nonexisting device + **/ + +static int +ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int err = 0; + int create; + struct ip6_tnl_parm p; + struct ip6_tnl *t = NULL; + + switch (cmd) { + case SIOCGETTUNNEL: + if (dev == &ip6ip6_fb_tnl_dev) { + if (copy_from_user(&p, + ifr->ifr_ifru.ifru_data, + sizeof (p))) { + err = -EFAULT; + break; + } + if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) + t = (struct ip6_tnl *) dev->priv; + else if (err) + break; + } else + t = (struct ip6_tnl *) dev->priv; + + memcpy(&p, &t->parms, sizeof (p)); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { + err = -EFAULT; + } + break; + case SIOCADDTUNNEL: + case SIOCCHGTUNNEL: + err = -EPERM; + create = (cmd == SIOCADDTUNNEL); + if (!capable(CAP_NET_ADMIN)) + break; + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { + err = -EFAULT; + break; + } + if (!create && dev != &ip6ip6_fb_tnl_dev) { + t = (struct ip6_tnl *) dev->priv; + } + if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) { + break; + } + if (cmd == SIOCCHGTUNNEL) { + if (t->dev != dev) { + err = -EEXIST; + break; + } + ip6ip6_tnl_unlink(t); + err = ip6ip6_tnl_change(t, &p); + ip6ip6_tnl_link(t); + netdev_state_change(dev); + } + if (copy_to_user(ifr->ifr_ifru.ifru_data, + &t->parms, sizeof (p))) { + err = -EFAULT; + } else { + err = 0; + } + break; + case SIOCDELTUNNEL: + err = -EPERM; + if (!capable(CAP_NET_ADMIN)) + break; + + if (dev == &ip6ip6_fb_tnl_dev) { + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, + sizeof (p))) { + err = -EFAULT; + break; + } + err = ip6ip6_tnl_locate(&p, &t, 0); + if (err) + break; + if (t == &ip6ip6_fb_tnl) { + err = -EPERM; + break; + } + } else { + t = (struct ip6_tnl *) dev->priv; + } + err = ip6_tnl_destroy(t); + break; + default: + err = -EINVAL; + } + return err; +} + +/** + * ip6ip6_tnl_get_stats - return the stats for tunnel device + * @dev: virtual device associated with tunnel + * + * Return: stats for device + **/ + +static struct net_device_stats * +ip6ip6_tnl_get_stats(struct net_device *dev) +{ + return &(((struct ip6_tnl *) dev->priv)->stat); +} + +/** + * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device + * @dev: virtual device associated with tunnel + * @new_mtu: the new mtu + * + * Return: + * 0 on success, + * %-EINVAL if mtu too small + **/ + +static int +ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < IPV6_MIN_MTU) { + return -EINVAL; + } + dev->mtu = new_mtu; + return 0; +} + +/** + * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices + * @dev: virtual device associated with tunnel + * + * Description: + * Set function pointers and initialize the &struct flowi template used + * by the tunnel. + **/ + +static void +ip6ip6_tnl_dev_init_gen(struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + struct flowi *fl = &t->fl; + + memset(fl, 0, sizeof (*fl)); + fl->proto = IPPROTO_IPV6; + + dev->destructor = ip6ip6_tnl_dev_destructor; + dev->uninit = ip6ip6_tnl_dev_uninit; + dev->hard_start_xmit = ip6ip6_tnl_xmit; + dev->get_stats = ip6ip6_tnl_get_stats; + dev->do_ioctl = ip6ip6_tnl_ioctl; + dev->change_mtu = ip6ip6_tnl_change_mtu; + dev->type = ARPHRD_TUNNEL6; + dev->flags |= IFF_NOARP; + if (ipv6_addr_type(&t->parms.raddr) & IPV6_ADDR_UNICAST && + ipv6_addr_type(&t->parms.laddr) & IPV6_ADDR_UNICAST) + dev->flags |= IFF_POINTOPOINT; + /* Hmm... MAX_ADDR_LEN is 8, so the ipv6 addresses can't be + copied to dev->dev_addr and dev->broadcast, like the ipv4 + addresses were in ipip.c, ip_gre.c and sit.c. */ + dev->addr_len = 0; +} + +/** + * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices + * @dev: virtual device associated with tunnel + **/ + +static int +ip6ip6_tnl_dev_init(struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + ip6ip6_tnl_dev_init_gen(dev); + ip6ip6_tnl_link_config(t); + return 0; +} + +/** + * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device + * @dev: fallback device + * + * Return: 0 + **/ + +int ip6ip6_fb_tnl_dev_init(struct net_device *dev) +{ + ip6ip6_tnl_dev_init_gen(dev); + tnls_wc[0] = &ip6ip6_fb_tnl; + return 0; +} + +static struct inet6_protocol ip6ip6_protocol = { + .handler = ip6ip6_rcv, + .err_handler = ip6ip6_err, + .flags = INET6_PROTO_FINAL +}; + +/** + * ip6_tunnel_init - register protocol and reserve needed resources + * + * Return: 0 on success + **/ + +int __init ip6_tunnel_init(void) +{ + int i, j, err; + struct sock *sk; + struct ipv6_pinfo *np; + + ip6ip6_fb_tnl_dev.priv = (void *) &ip6ip6_fb_tnl; + + for (i = 0; i < NR_CPUS; i++) { + if (!cpu_possible(i)) + continue; + + err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_IPV6, + &__ip6_socket[i]); + if (err < 0) { + printk(KERN_ERR + "Failed to create the IPv6 tunnel socket " + "(err %d).\n", + err); + goto fail; + } + sk = __ip6_socket[i]->sk; + sk->sk_allocation = GFP_ATOMIC; + + np = inet6_sk(sk); + np->hop_limit = 255; + np->mc_loop = 0; + + sk->sk_prot->unhash(sk); + } + if ((err = inet6_add_protocol(&ip6ip6_protocol, IPPROTO_IPV6)) < 0) { + printk(KERN_ERR "Failed to register IPv6 protocol\n"); + goto fail; + } + + SET_MODULE_OWNER(&ip6ip6_fb_tnl_dev); + register_netdev(&ip6ip6_fb_tnl_dev); + + return 0; +fail: + for (j = 0; j < i; j++) { + if (!cpu_possible(j)) + continue; + sock_release(__ip6_socket[j]); + __ip6_socket[j] = NULL; + } + return err; +} + +/** + * ip6_tunnel_cleanup - free resources and unregister protocol + **/ + +void ip6_tunnel_cleanup(void) +{ + int i; + + unregister_netdev(&ip6ip6_fb_tnl_dev); + + inet6_del_protocol(&ip6ip6_protocol, IPPROTO_IPV6); + + for (i = 0; i < NR_CPUS; i++) { + if (!cpu_possible(i)) + continue; + sock_release(__ip6_socket[i]); + __ip6_socket[i] = NULL; + } +} + +#ifdef MODULE +module_init(ip6_tunnel_init); +module_exit(ip6_tunnel_cleanup); +#endif diff -urN linux-2.5.70-bk14/net/ipv6/ipv6_syms.c linux-2.5.70-bk15/net/ipv6/ipv6_syms.c --- linux-2.5.70-bk14/net/ipv6/ipv6_syms.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/ipv6/ipv6_syms.c 2003-06-10 04:43:12.000000000 -0700 @@ -38,3 +38,9 @@ EXPORT_SYMBOL(ip6_find_1stfragopt); EXPORT_SYMBOL(xfrm6_rcv); EXPORT_SYMBOL(xfrm6_clear_mutable_options); +EXPORT_SYMBOL(rt6_lookup); +EXPORT_SYMBOL(fl6_sock_lookup); +EXPORT_SYMBOL(ipv6_ext_hdr); +EXPORT_SYMBOL(ip6_append_data); +EXPORT_SYMBOL(ip6_flush_pending_frames); +EXPORT_SYMBOL(ip6_push_pending_frames); diff -urN linux-2.5.70-bk14/net/ipv6/route.c linux-2.5.70-bk15/net/ipv6/route.c --- linux-2.5.70-bk14/net/ipv6/route.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/ipv6/route.c 2003-06-10 04:43:12.000000000 -0700 @@ -587,19 +587,6 @@ Remove it only when all the things will work! */ -static void ipv6_addr_prefix(struct in6_addr *pfx, - const struct in6_addr *addr, int plen) -{ - int b = plen&0x7; - int o = plen>>3; - - memcpy(pfx->s6_addr, addr, o); - if (o < 16) - memset(pfx->s6_addr + o, 0, 16 - o); - if (b != 0) - pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b); -} - static int ipv6_get_mtu(struct net_device *dev) { int mtu = IPV6_MIN_MTU; @@ -1220,10 +1207,12 @@ if (rt == NULL) return -ENOMEM; + dev_hold(&loopback_dev); + rt->u.dst.flags = DST_HOST; rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; - rt->rt6i_dev = dev_get_by_name("lo"); + rt->rt6i_dev = &loopback_dev; rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev); rt->u.dst.metrics[RTAX_ADVMSS-1] = max_t(unsigned int, dst_pmtu(&rt->u.dst) - 60, ip6_rt_min_advmss); if (rt->u.dst.metrics[RTAX_ADVMSS-1] > 65535-20) diff -urN linux-2.5.70-bk14/net/key/af_key.c linux-2.5.70-bk15/net/key/af_key.c --- linux-2.5.70-bk14/net/key/af_key.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/key/af_key.c 2003-06-10 04:43:12.000000000 -0700 @@ -2016,7 +2016,6 @@ out: if (xp) { xfrm_policy_kill(xp); - xfrm_pol_put(xp); } return err; } @@ -2060,7 +2059,8 @@ if (xp) { if (hdr->sadb_msg_type == SADB_X_SPDDELETE2) xfrm_policy_kill(xp); - xfrm_pol_put(xp); + else + xfrm_pol_put(xp); } return err; } diff -urN linux-2.5.70-bk14/net/netsyms.c linux-2.5.70-bk15/net/netsyms.c --- linux-2.5.70-bk14/net/netsyms.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/netsyms.c 2003-06-10 04:43:12.000000000 -0700 @@ -477,8 +477,10 @@ EXPORT_SYMBOL(sysctl_max_syn_backlog); #endif -EXPORT_SYMBOL(ip_generic_getfrag); +#endif +#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE) || defined (CONFIG_IPV6_TUNNEL_MODULE) +EXPORT_SYMBOL(ip_generic_getfrag); #endif EXPORT_SYMBOL(tcp_read_sock); diff -urN linux-2.5.70-bk14/net/sched/sch_ingress.c linux-2.5.70-bk15/net/sched/sch_ingress.c --- linux-2.5.70-bk14/net/sched/sch_ingress.c 2003-05-26 18:00:46.000000000 -0700 +++ linux-2.5.70-bk15/net/sched/sch_ingress.c 2003-06-10 04:43:12.000000000 -0700 @@ -15,10 +15,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff -urN linux-2.5.70-bk14/net/xfrm/xfrm_policy.c linux-2.5.70-bk15/net/xfrm/xfrm_policy.c --- linux-2.5.70-bk14/net/xfrm/xfrm_policy.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/xfrm/xfrm_policy.c 2003-06-10 04:43:12.000000000 -0700 @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include @@ -30,6 +33,11 @@ kmem_cache_t *xfrm_dst_cache; +static struct work_struct xfrm_policy_gc_work; +static struct list_head xfrm_policy_gc_list = + LIST_HEAD_INIT(xfrm_policy_gc_list); +static spinlock_t xfrm_policy_gc_lock = SPIN_LOCK_UNLOCKED; + int xfrm_register_type(struct xfrm_type *type, unsigned short family) { struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); @@ -162,7 +170,6 @@ xp = xfrm_policy_byid(0, index, 1); if (xp) { xfrm_policy_kill(xp); - xfrm_pol_put(xp); } } @@ -204,27 +211,56 @@ kfree(policy); } +static void xfrm_policy_gc_kill(struct xfrm_policy *policy) +{ + struct dst_entry *dst; + + while ((dst = policy->bundles) != NULL) { + policy->bundles = dst->next; + dst_free(dst); + } + + if (del_timer(&policy->timer)) + atomic_dec(&policy->refcnt); + + if (atomic_read(&policy->refcnt) > 1) + flow_cache_flush(policy); + + xfrm_pol_put(policy); +} + +static void xfrm_policy_gc_task(void *data) +{ + struct xfrm_policy *policy; + struct list_head *entry, *tmp; + struct list_head gc_list = LIST_HEAD_INIT(gc_list); + + spin_lock_bh(&xfrm_policy_gc_lock); + list_splice_init(&xfrm_policy_gc_list, &gc_list); + spin_unlock_bh(&xfrm_policy_gc_lock); + + list_for_each_safe(entry, tmp, &gc_list) { + policy = list_entry(entry, struct xfrm_policy, list); + xfrm_policy_gc_kill(policy); + } +} + /* Rule must be locked. Release descentant resources, announce * entry dead. The rule must be unlinked from lists to the moment. */ void xfrm_policy_kill(struct xfrm_policy *policy) { - struct dst_entry *dst; - write_lock_bh(&policy->lock); if (policy->dead) goto out; policy->dead = 1; - while ((dst = policy->bundles) != NULL) { - policy->bundles = dst->next; - dst_free(dst); - } - - if (del_timer(&policy->timer)) - atomic_dec(&policy->refcnt); + spin_lock(&xfrm_policy_gc_lock); + list_add(&policy->list, &xfrm_policy_gc_list); + spin_unlock(&xfrm_policy_gc_lock); + schedule_work(&xfrm_policy_gc_work); out: write_unlock_bh(&policy->lock); @@ -292,7 +328,6 @@ if (delpol) { xfrm_policy_kill(delpol); - xfrm_pol_put(delpol); } return 0; } @@ -354,7 +389,6 @@ write_unlock_bh(&xfrm_policy_lock); xfrm_policy_kill(xp); - xfrm_pol_put(xp); write_lock_bh(&xfrm_policy_lock); } @@ -398,8 +432,8 @@ /* Find policy to apply to this flow. */ -void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, - void **objp, atomic_t **obj_refp) +static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, + void **objp, atomic_t **obj_refp) { struct xfrm_policy *pol; @@ -478,7 +512,6 @@ if (old_pol) { xfrm_policy_kill(old_pol); - xfrm_pol_put(old_pol); } return 0; } @@ -524,7 +557,6 @@ write_unlock_bh(&xfrm_policy_lock); xfrm_policy_kill(pol); - xfrm_pol_put(pol); } /* Resolve list of templates for the flow, given policy. */ @@ -1143,6 +1175,8 @@ NULL, NULL); if (!xfrm_dst_cache) panic("XFRM: failed to allocate xfrm_dst_cache\n"); + + INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task, NULL); } void __init xfrm_init(void) diff -urN linux-2.5.70-bk14/net/xfrm/xfrm_user.c linux-2.5.70-bk15/net/xfrm/xfrm_user.c --- linux-2.5.70-bk14/net/xfrm/xfrm_user.c 2003-06-10 04:43:02.000000000 -0700 +++ linux-2.5.70-bk15/net/xfrm/xfrm_user.c 2003-06-10 04:43:12.000000000 -0700 @@ -784,10 +784,9 @@ NETLINK_CB(skb).pid, MSG_DONTWAIT); } + xfrm_pol_put(xp); } - xfrm_pol_put(xp); - return err; }