diff options
Diffstat (limited to 'fs/tftp.c')
-rw-r--r-- | fs/tftp.c | 374 |
1 files changed, 55 insertions, 319 deletions
@@ -78,9 +78,6 @@ /* allocate this number of blocks more than needed in the fifo */ #define TFTP_EXTRA_BLOCKS 2 -/* size of cache which deals with udp reordering */ -#define TFTP_WINDOW_CACHE_NUM CONFIG_FS_TFTP_REORDER_CACHE_SIZE - /* marker for an emtpy 'tftp_cache' */ #define TFTP_CACHE_NO_ID (-1) @@ -101,24 +98,12 @@ struct tftp_block { uint16_t id; uint16_t len; - struct list_head head; + struct list_head list; uint8_t data[]; }; struct tftp_cache { - /* The id located at @pos or TFTP_CACHE_NO_ID when cache is empty. It - is possible that the corresponding bit in @used is NOT set. */ - unsigned int id; - unsigned int pos; - - /* bitmask */ - unsigned long used[BITS_TO_LONGS(TFTP_WINDOW_CACHE_NUM)]; - - /* size of the cache; is limited by TFTP_WINDOW_CACHE_NUM and the - actual window size */ - unsigned int size; - unsigned int block_len; - struct tftp_block *blocks[TFTP_WINDOW_CACHE_NUM]; + struct list_head blocks; }; struct file_priv { @@ -150,13 +135,6 @@ static inline bool is_block_before(uint16_t a, uint16_t b) return (int16_t)(b - a) > 0; } -static inline uint16_t get_block_delta(uint16_t a, uint16_t b) -{ - debug_assert(!is_block_before(b, a)); - - return b - a; -} - static bool in_window(uint16_t block, uint16_t start, uint16_t end) { /* handle the three cases: @@ -169,192 +147,38 @@ static bool in_window(uint16_t block, uint16_t start, uint16_t end) (end <= start && start <= block)); } -static inline size_t tftp_window_cache_size(struct tftp_cache const *cache) -{ - /* allows to optimize away the cache code when TFTP_WINDOW_CACHE_SIZE - is 0 */ - return TFTP_WINDOW_CACHE_NUM == 0 ? 0 : cache->size; -} - -static void tftp_window_cache_init(struct tftp_cache *cache, - uint16_t block_len, uint16_t window_size) -{ - debug_assert(window_size > 0); - - *cache = (struct tftp_cache) { - .id = TFTP_CACHE_NO_ID, - .block_len = block_len, - .size = min_t(uint16_t, window_size - 1, - ARRAY_SIZE(cache->blocks)), - }; -} - static void tftp_window_cache_free(struct tftp_cache *cache) { - size_t cache_size = tftp_window_cache_size(cache); - size_t i; + struct tftp_block *block, *tmp; - for (i = 0; i < cache_size; ++i) { - free(cache->blocks[i]); - cache->blocks[i] = NULL; - } -} - -static void tftp_window_cache_reset(struct tftp_cache *cache) -{ - cache->id = TFTP_CACHE_NO_ID; - memset(cache->used, 0, sizeof cache->used); -} - -static int tftp_window_cache_get_pos(struct tftp_cache const *cache, uint16_t id) -{ - size_t cache_size = tftp_window_cache_size(cache); - unsigned int pos; - - if (cache_size == 0) - return -1; - - if (cache->id == TFTP_CACHE_NO_ID) - return -1; - - if (!in_window(id, cache->id, cache->id + cache_size - 1)) - return -1; - - pos = cache->pos + get_block_delta(cache->id, id); - pos %= cache_size; - - return pos; -} - -/* returns whether the first cached element has the given @id */ -static bool tftp_window_cache_starts_with(struct tftp_cache const *cache, - uint16_t id) -{ - return (TFTP_WINDOW_CACHE_NUM > 0 && - cache->id != TFTP_CACHE_NO_ID && - cache->id == id && - test_bit(cache->pos, cache->used)); -} - -static bool tftp_window_cache_is_empty(struct tftp_cache const *cache) -{ - /* use this indirection to avoid warnings about a '0 < 0' comparison - in the loop condition when TFTP_WINDOW_CACHE_NUM is zero */ - size_t cache_size = ARRAY_SIZE(cache->used); - size_t i; - - for (i = 0; i < cache_size; ++i) { - if (cache->used[i] != 0) - return false; - } - - return true; + list_for_each_entry_safe(block, tmp, &cache->blocks, list) + free(block); } static int tftp_window_cache_insert(struct tftp_cache *cache, uint16_t id, void const *data, size_t len) { - size_t const cache_size = tftp_window_cache_size(cache); - int pos; - struct tftp_block *block; + struct tftp_block *block, *new; - if (cache_size == 0) - return -ENOSPC; - - if (cache->id == TFTP_CACHE_NO_ID) { - /* initialize cache when it does not contain elements yet */ - cache->id = id; - cache->pos = 0; - - /* sanity check; cache is expected to be empty */ - debug_assert(tftp_window_cache_is_empty(cache)); - } + list_for_each_entry(block, &cache->blocks, list) { + if (block->id == id) + return 0; + if (is_block_before(block->id, id)) + continue; - pos = tftp_window_cache_get_pos(cache, id); - if (pos < 0) - return -ENOSPC; - - debug_assert(pos < cache_size); - - if (test_bit(pos, cache->used)) - /* block already cached */ - return 0; - - block = cache->blocks[pos]; - if (!block) { - /* allocate space for the block; after being released, this - memory can be reused for other blocks during the same tftp - transfer */ - block = malloc(sizeof *block + cache->block_len); - if (!block) - return -ENOMEM; - - cache->blocks[pos] = block; + break; } - __set_bit(pos, cache->used); - memcpy(block->data, data, len); - block->id = id; - block->len = len; + new = xzalloc(sizeof(*new) + len); + memcpy(new->data, data, len); + new->id = id; + new->len = len; + list_add_tail(&new->list, &block->list); return 0; } -/* Marks the element at 'pos' as unused and updates internal cache information. - Returns true iff element at pos was valid. */ -static bool tftp_window_cache_remove(struct tftp_cache *cache, unsigned int pos) -{ - size_t const cache_size = tftp_window_cache_size(cache); - bool res; - - if (cache_size == 0) - return 0; - - res = __test_and_clear_bit(pos, cache->used); - - if (tftp_window_cache_is_empty(cache)) { - /* cache has been cleared; reset pointers */ - cache->id = TFTP_CACHE_NO_ID; - } else if (pos != cache->pos) { - /* noop; elements has been removed from the middle of cache */ - } else { - /* first element of cache has been removed; proceed to the - next one */ - cache->id += 1; - cache->pos += 1; - cache->pos %= cache_size; - } - - return res; -} - -/* Releases the first element from the cache and returns its content. - * - * Function can return NULL when the element is not cached - */ -static struct tftp_block *tftp_window_cache_pop(struct tftp_cache *cache) -{ - unsigned int pos = cache->pos; - - debug_assert(cache->id != TFTP_CACHE_NO_ID); - - if (!tftp_window_cache_remove(cache, pos)) - return NULL; - - return cache->blocks[pos]; -} - -static bool tftp_window_cache_remove_id(struct tftp_cache *cache, uint16_t id) -{ - int pos = tftp_window_cache_get_pos(cache, id); - - if (pos < 0) - return false; - - return tftp_window_cache_remove(cache, pos); -} - -static int tftp_truncate(struct device_d *dev, FILE *f, loff_t size) +static int tftp_truncate(struct device *dev, FILE *f, loff_t size) { return 0; } @@ -444,7 +268,6 @@ static int tftp_send(struct file_priv *priv) priv->ack_block += priv->windowsize; pkt = (unsigned char *)s; len = pkt - xp; - tftp_window_cache_reset(&priv->cache); break; } @@ -561,11 +384,10 @@ static int tftp_allocate_transfer(struct file_priv *priv) priv->fifo = NULL; goto err; } - } else { - tftp_window_cache_init(&priv->cache, - priv->blocksize, priv->windowsize); } + INIT_LIST_HEAD(&priv->cache.blocks); + return 0; err: @@ -606,7 +428,7 @@ static void tftp_apply_window_cache(struct file_priv *priv) { struct tftp_cache *cache = &priv->cache; - while (tftp_window_cache_starts_with(cache, priv->last_block + 1)) { + while (1) { struct tftp_block *block; /* can be changed by tftp_put_data() below and must be @@ -614,12 +436,26 @@ static void tftp_apply_window_cache(struct file_priv *priv) if (priv->state != STATE_RDATA) return; - block = tftp_window_cache_pop(cache); + if (list_empty(&cache->blocks)) + return; - debug_assert(block); - debug_assert(block->id == (uint16_t)(priv->last_block + 1)); + block = list_first_entry(&cache->blocks, struct tftp_block, list); + + if (is_block_before(block->id, priv->last_block + 1)) { + /* shouldn't happen, but be sure */ + list_del(&block->list); + free(block); + continue; + } + + if (block->id != (uint16_t)(priv->last_block + 1)) + return; tftp_put_data(priv, block->id, block->data, block->len); + + list_del(&block->list); + + free(block); } } @@ -636,12 +472,13 @@ static void tftp_handle_data(struct file_priv *priv, uint16_t block, fifo directly and try to apply cached items then */ tftp_timer_reset(priv); tftp_put_data(priv, block, data, len); - tftp_window_cache_remove_id(&priv->cache, block); tftp_apply_window_cache(priv); } else if (!in_window(block, exp_block, priv->ack_block)) { /* completely unexpected and unrelated to actual window; ignore the packet. */ printf("B"); + if (g_tftp_window_size > 1) + pr_warn_once("Unexpected packet. global.tftp.windowsize set too high?\n"); } else { /* The 'rc < 0' below happens e.g. when datagrams in the first part of the transfer window are dropped. @@ -812,10 +649,11 @@ static int tftp_start_transfer(struct file_priv *priv) return 0; } -static struct file_priv *tftp_do_open(struct device_d *dev, - int accmode, struct dentry *dentry, bool is_getattr) +static struct file_priv *tftp_do_open(struct device *dev, + int accmode, struct dentry *dentry, + bool is_getattr) { - struct fs_device_d *fsdev = dev_to_fs_device(dev); + struct fs_device *fsdev = dev_to_fs_device(dev); struct file_priv *priv; struct tftp_priv *tpriv = dev->priv; int ret; @@ -917,7 +755,7 @@ out: return ERR_PTR(ret); } -static int tftp_open(struct device_d *dev, FILE *file, const char *filename) +static int tftp_open(struct device *dev, FILE *file, const char *filename) { struct file_priv *priv; @@ -970,15 +808,15 @@ static int tftp_do_close(struct file_priv *priv) return 0; } -static int tftp_close(struct device_d *dev, FILE *f) +static int tftp_close(struct device *dev, FILE *f) { struct file_priv *priv = f->priv; return tftp_do_close(priv); } -static int tftp_write(struct device_d *_dev, FILE *f, const void *inbuf, - size_t insize) +static int tftp_write(struct device *_dev, FILE *f, const void *inbuf, + size_t insize) { struct file_priv *priv = f->priv; size_t size, now; @@ -1013,7 +851,7 @@ static int tftp_write(struct device_d *_dev, FILE *f, const void *inbuf, return insize; } -static int tftp_read(struct device_d *dev, FILE *f, void *buf, size_t insize) +static int tftp_read(struct device *dev, FILE *f, void *buf, size_t insize) { struct file_priv *priv = f->priv; size_t outsize = 0, now; @@ -1053,7 +891,7 @@ static int tftp_read(struct device_d *dev, FILE *f, void *buf, size_t insize) return outsize; } -static int tftp_lseek(struct device_d *dev, FILE *f, loff_t pos) +static int tftp_lseek(struct device *dev, FILE *f, loff_t pos) { /* We cannot seek backwards without reloading or caching the file */ loff_t f_pos = f->pos; @@ -1144,7 +982,7 @@ static struct dentry *tftp_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct super_block *sb = dir->i_sb; - struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb); + struct fs_device *fsdev = container_of(sb, struct fs_device, sb); struct inode *inode; struct file_priv *priv; loff_t filesize; @@ -1179,9 +1017,9 @@ static const struct inode_operations tftp_dir_inode_operations = static const struct super_operations tftp_ops; -static int tftp_probe(struct device_d *dev) +static int tftp_probe(struct device *dev) { - struct fs_device_d *fsdev = dev_to_fs_device(dev); + struct fs_device *fsdev = dev_to_fs_device(dev); struct tftp_priv *priv = xzalloc(sizeof(struct tftp_priv)); struct super_block *sb = &fsdev->sb; struct inode *inode; @@ -1208,14 +1046,14 @@ err: return ret; } -static void tftp_remove(struct device_d *dev) +static void tftp_remove(struct device *dev) { struct tftp_priv *priv = dev->priv; free(priv); } -static struct fs_driver_d tftp_driver = { +static struct fs_driver tftp_driver = { .open = tftp_open, .close = tftp_close, .read = tftp_read, @@ -1237,105 +1075,3 @@ static int tftp_init(void) return register_fs_driver(&tftp_driver); } coredevice_initcall(tftp_init); - - -BSELFTEST_GLOBALS(); - -static int __maybe_unused tftp_window_cache_selftest(void) -{ - struct tftp_cache *cache = malloc(sizeof *cache); - - if (!cache) - return -ENOMEM; - - (void)skipped_tests; - - expect_it( is_block_before(0, 1)); - expect_it(!is_block_before(1, 0)); - expect_it( is_block_before(65535, 0)); - expect_it(!is_block_before(0, 65535)); - - expect_eq(get_block_delta(0, 1), 1); - expect_eq(get_block_delta(65535, 0), 1); - expect_eq(get_block_delta(65535, 1), 2); - - expect_it(!in_window(0, 1, 3)); - expect_it( in_window(1, 1, 3)); - expect_it( in_window(2, 1, 3)); - expect_it( in_window(3, 1, 3)); - expect_it(!in_window(4, 1, 3)); - - expect_it(!in_window(65534, 65535, 1)); - expect_it( in_window(65535, 65535, 1)); - expect_it( in_window( 0, 65535, 1)); - expect_it( in_window( 1, 65535, 1)); - expect_it(!in_window( 2, 65535, 1)); - - - tftp_window_cache_init(cache, 512, 5); - - if (tftp_window_cache_size(cache) < 4) - goto out; - - expect_eq(tftp_window_cache_size(cache), 4); - - /* sequence 1 */ - expect_ok (tftp_window_cache_insert(cache, 20, "20", 2)); - expect_ok (tftp_window_cache_insert(cache, 22, "22", 2)); - expect_ok (tftp_window_cache_insert(cache, 21, "21", 2)); - expect_ok (tftp_window_cache_insert(cache, 23, "23", 2)); - expect_err(tftp_window_cache_insert(cache, 24, "24", 2)); - expect_err(tftp_window_cache_insert(cache, 19, "19", 2)); - expect_ok (tftp_window_cache_insert(cache, 22, "22", 2)); - expect_ok (tftp_window_cache_insert(cache, 20, "20", 2)); - - expect_eq(tftp_window_cache_pop(cache)->id, 20); - expect_eq(tftp_window_cache_pop(cache)->id, 21); - expect_eq(tftp_window_cache_pop(cache)->id, 22); - expect_eq(tftp_window_cache_pop(cache)->id, 23); - expect_eq(cache->id, TFTP_CACHE_NO_ID); - - /* sequence 2 */ - expect_ok (tftp_window_cache_insert(cache, 30, "30", 2)); - expect_ok (tftp_window_cache_insert(cache, 32, "32", 2)); - expect_err(tftp_window_cache_insert(cache, 34, "34", 2)); - - expect_it(tftp_window_cache_starts_with(cache, 30)); - expect_eq(tftp_window_cache_pop(cache)->id, 30); - - expect_ok (tftp_window_cache_insert(cache, 34, "34", 2)); - expect_err(tftp_window_cache_insert(cache, 35, "35", 2)); - - expect_it(!tftp_window_cache_starts_with(cache, 30)); - expect_it(!tftp_window_cache_starts_with(cache, 31)); - expect_it(!tftp_window_cache_starts_with(cache, 32)); - expect_NULL(tftp_window_cache_pop(cache)); - - expect_it(tftp_window_cache_starts_with(cache, 32)); - expect_eq(tftp_window_cache_pop(cache)->id, 32); - - expect_NULL(tftp_window_cache_pop(cache)); - expect_eq(tftp_window_cache_pop(cache)->id, 34); - - expect_eq(cache->id, TFTP_CACHE_NO_ID); - - /* sequence 3 */ - expect_ok(tftp_window_cache_insert(cache, 40, "40", 2)); - expect_ok(tftp_window_cache_insert(cache, 42, "42", 2)); - expect_ok(tftp_window_cache_insert(cache, 43, "43", 2)); - - expect_it(!tftp_window_cache_remove_id(cache, 30)); - expect_it(!tftp_window_cache_remove_id(cache, 41)); - expect_it(!tftp_window_cache_remove_id(cache, 44)); - - expect_it( tftp_window_cache_remove_id(cache, 42)); - expect_it(!tftp_window_cache_remove_id(cache, 42)); - -out: - tftp_window_cache_free(cache); - - return 0; -} -#ifdef CONFIG_SELFTEST_TFTP -bselftest(core, tftp_window_cache_selftest); -#endif |