Slab destructors were no longer supported after Christoph's
c59def9f22
change. They've been
BUGs for both slab and slub, and slob never supported them
either.
This rips out support for the dtor pointer from kmem_cache_create()
completely and fixes up every single callsite in the kernel (there were
about 224, not including the slab allocator definitions themselves,
or the documentation references).
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
296 lines
7.2 KiB
C
296 lines
7.2 KiB
C
/*
|
|
* net/dccp/ccids/lib/loss_interval.c
|
|
*
|
|
* Copyright (c) 2005-7 The University of Waikato, Hamilton, New Zealand.
|
|
* Copyright (c) 2005-7 Ian McDonald <ian.mcdonald@jandi.co.nz>
|
|
* Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
|
|
*
|
|
* 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 <linux/module.h>
|
|
#include <net/sock.h>
|
|
#include "../../dccp.h"
|
|
#include "loss_interval.h"
|
|
#include "packet_history.h"
|
|
#include "tfrc.h"
|
|
|
|
#define DCCP_LI_HIST_IVAL_F_LENGTH 8
|
|
|
|
struct dccp_li_hist_entry {
|
|
struct list_head dccplih_node;
|
|
u64 dccplih_seqno:48,
|
|
dccplih_win_count:4;
|
|
u32 dccplih_interval;
|
|
};
|
|
|
|
static struct kmem_cache *dccp_li_cachep __read_mostly;
|
|
|
|
static inline struct dccp_li_hist_entry *dccp_li_hist_entry_new(const gfp_t prio)
|
|
{
|
|
return kmem_cache_alloc(dccp_li_cachep, prio);
|
|
}
|
|
|
|
static inline void dccp_li_hist_entry_delete(struct dccp_li_hist_entry *entry)
|
|
{
|
|
if (entry != NULL)
|
|
kmem_cache_free(dccp_li_cachep, entry);
|
|
}
|
|
|
|
void dccp_li_hist_purge(struct list_head *list)
|
|
{
|
|
struct dccp_li_hist_entry *entry, *next;
|
|
|
|
list_for_each_entry_safe(entry, next, list, dccplih_node) {
|
|
list_del_init(&entry->dccplih_node);
|
|
kmem_cache_free(dccp_li_cachep, entry);
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_li_hist_purge);
|
|
|
|
/* Weights used to calculate loss event rate */
|
|
/*
|
|
* These are integers as per section 8 of RFC3448. We can then divide by 4 *
|
|
* when we use it.
|
|
*/
|
|
static const int dccp_li_hist_w[DCCP_LI_HIST_IVAL_F_LENGTH] = {
|
|
4, 4, 4, 4, 3, 2, 1, 1,
|
|
};
|
|
|
|
u32 dccp_li_hist_calc_i_mean(struct list_head *list)
|
|
{
|
|
struct dccp_li_hist_entry *li_entry, *li_next;
|
|
int i = 0;
|
|
u32 i_tot;
|
|
u32 i_tot0 = 0;
|
|
u32 i_tot1 = 0;
|
|
u32 w_tot = 0;
|
|
|
|
list_for_each_entry_safe(li_entry, li_next, list, dccplih_node) {
|
|
if (li_entry->dccplih_interval != ~0U) {
|
|
i_tot0 += li_entry->dccplih_interval * dccp_li_hist_w[i];
|
|
w_tot += dccp_li_hist_w[i];
|
|
if (i != 0)
|
|
i_tot1 += li_entry->dccplih_interval * dccp_li_hist_w[i - 1];
|
|
}
|
|
|
|
|
|
if (++i > DCCP_LI_HIST_IVAL_F_LENGTH)
|
|
break;
|
|
}
|
|
|
|
if (i != DCCP_LI_HIST_IVAL_F_LENGTH)
|
|
return 0;
|
|
|
|
i_tot = max(i_tot0, i_tot1);
|
|
|
|
if (!w_tot) {
|
|
DCCP_WARN("w_tot = 0\n");
|
|
return 1;
|
|
}
|
|
|
|
return i_tot / w_tot;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_li_hist_calc_i_mean);
|
|
|
|
static int dccp_li_hist_interval_new(struct list_head *list,
|
|
const u64 seq_loss, const u8 win_loss)
|
|
{
|
|
struct dccp_li_hist_entry *entry;
|
|
int i;
|
|
|
|
for (i = 0; i < DCCP_LI_HIST_IVAL_F_LENGTH; i++) {
|
|
entry = dccp_li_hist_entry_new(GFP_ATOMIC);
|
|
if (entry == NULL) {
|
|
dccp_li_hist_purge(list);
|
|
DCCP_BUG("loss interval list entry is NULL");
|
|
return 0;
|
|
}
|
|
entry->dccplih_interval = ~0;
|
|
list_add(&entry->dccplih_node, list);
|
|
}
|
|
|
|
entry->dccplih_seqno = seq_loss;
|
|
entry->dccplih_win_count = win_loss;
|
|
return 1;
|
|
}
|
|
|
|
/* calculate first loss interval
|
|
*
|
|
* returns estimated loss interval in usecs */
|
|
static u32 dccp_li_calc_first_li(struct sock *sk,
|
|
struct list_head *hist_list,
|
|
struct timeval *last_feedback,
|
|
u16 s, u32 bytes_recv,
|
|
u32 previous_x_recv)
|
|
{
|
|
struct dccp_rx_hist_entry *entry, *next, *tail = NULL;
|
|
u32 x_recv, p;
|
|
suseconds_t rtt, delta;
|
|
struct timeval tstamp = { 0, 0 };
|
|
int interval = 0;
|
|
int win_count = 0;
|
|
int step = 0;
|
|
u64 fval;
|
|
|
|
list_for_each_entry_safe(entry, next, hist_list, dccphrx_node) {
|
|
if (dccp_rx_hist_entry_data_packet(entry)) {
|
|
tail = entry;
|
|
|
|
switch (step) {
|
|
case 0:
|
|
tstamp = entry->dccphrx_tstamp;
|
|
win_count = entry->dccphrx_ccval;
|
|
step = 1;
|
|
break;
|
|
case 1:
|
|
interval = win_count - entry->dccphrx_ccval;
|
|
if (interval < 0)
|
|
interval += TFRC_WIN_COUNT_LIMIT;
|
|
if (interval > 4)
|
|
goto found;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unlikely(step == 0)) {
|
|
DCCP_WARN("%s(%p), packet history has no data packets!\n",
|
|
dccp_role(sk), sk);
|
|
return ~0;
|
|
}
|
|
|
|
if (unlikely(interval == 0)) {
|
|
DCCP_WARN("%s(%p), Could not find a win_count interval > 0."
|
|
"Defaulting to 1\n", dccp_role(sk), sk);
|
|
interval = 1;
|
|
}
|
|
found:
|
|
if (!tail) {
|
|
DCCP_CRIT("tail is null\n");
|
|
return ~0;
|
|
}
|
|
|
|
delta = timeval_delta(&tstamp, &tail->dccphrx_tstamp);
|
|
DCCP_BUG_ON(delta < 0);
|
|
|
|
rtt = delta * 4 / interval;
|
|
dccp_pr_debug("%s(%p), approximated RTT to %dus\n",
|
|
dccp_role(sk), sk, (int)rtt);
|
|
|
|
/*
|
|
* Determine the length of the first loss interval via inverse lookup.
|
|
* Assume that X_recv can be computed by the throughput equation
|
|
* s
|
|
* X_recv = --------
|
|
* R * fval
|
|
* Find some p such that f(p) = fval; return 1/p [RFC 3448, 6.3.1].
|
|
*/
|
|
if (rtt == 0) { /* would result in divide-by-zero */
|
|
DCCP_WARN("RTT==0\n");
|
|
return ~0;
|
|
}
|
|
|
|
dccp_timestamp(sk, &tstamp);
|
|
delta = timeval_delta(&tstamp, last_feedback);
|
|
DCCP_BUG_ON(delta <= 0);
|
|
|
|
x_recv = scaled_div32(bytes_recv, delta);
|
|
if (x_recv == 0) { /* would also trigger divide-by-zero */
|
|
DCCP_WARN("X_recv==0\n");
|
|
if (previous_x_recv == 0) {
|
|
DCCP_BUG("stored value of X_recv is zero");
|
|
return ~0;
|
|
}
|
|
x_recv = previous_x_recv;
|
|
}
|
|
|
|
fval = scaled_div(s, rtt);
|
|
fval = scaled_div32(fval, x_recv);
|
|
p = tfrc_calc_x_reverse_lookup(fval);
|
|
|
|
dccp_pr_debug("%s(%p), receive rate=%u bytes/s, implied "
|
|
"loss rate=%u\n", dccp_role(sk), sk, x_recv, p);
|
|
|
|
if (p == 0)
|
|
return ~0;
|
|
else
|
|
return 1000000 / p;
|
|
}
|
|
|
|
void dccp_li_update_li(struct sock *sk,
|
|
struct list_head *li_hist_list,
|
|
struct list_head *hist_list,
|
|
struct timeval *last_feedback, u16 s, u32 bytes_recv,
|
|
u32 previous_x_recv, u64 seq_loss, u8 win_loss)
|
|
{
|
|
struct dccp_li_hist_entry *head;
|
|
u64 seq_temp;
|
|
|
|
if (list_empty(li_hist_list)) {
|
|
if (!dccp_li_hist_interval_new(li_hist_list, seq_loss,
|
|
win_loss))
|
|
return;
|
|
|
|
head = list_entry(li_hist_list->next, struct dccp_li_hist_entry,
|
|
dccplih_node);
|
|
head->dccplih_interval = dccp_li_calc_first_li(sk, hist_list,
|
|
last_feedback,
|
|
s, bytes_recv,
|
|
previous_x_recv);
|
|
} else {
|
|
struct dccp_li_hist_entry *entry;
|
|
struct list_head *tail;
|
|
|
|
head = list_entry(li_hist_list->next, struct dccp_li_hist_entry,
|
|
dccplih_node);
|
|
/* FIXME win count check removed as was wrong */
|
|
/* should make this check with receive history */
|
|
/* and compare there as per section 10.2 of RFC4342 */
|
|
|
|
/* new loss event detected */
|
|
/* calculate last interval length */
|
|
seq_temp = dccp_delta_seqno(head->dccplih_seqno, seq_loss);
|
|
entry = dccp_li_hist_entry_new(GFP_ATOMIC);
|
|
|
|
if (entry == NULL) {
|
|
DCCP_BUG("out of memory - can not allocate entry");
|
|
return;
|
|
}
|
|
|
|
list_add(&entry->dccplih_node, li_hist_list);
|
|
|
|
tail = li_hist_list->prev;
|
|
list_del(tail);
|
|
kmem_cache_free(dccp_li_cachep, tail);
|
|
|
|
/* Create the newest interval */
|
|
entry->dccplih_seqno = seq_loss;
|
|
entry->dccplih_interval = seq_temp;
|
|
entry->dccplih_win_count = win_loss;
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dccp_li_update_li);
|
|
|
|
static __init int dccp_li_init(void)
|
|
{
|
|
dccp_li_cachep = kmem_cache_create("dccp_li_hist",
|
|
sizeof(struct dccp_li_hist_entry),
|
|
0, SLAB_HWCACHE_ALIGN, NULL);
|
|
return dccp_li_cachep == NULL ? -ENOBUFS : 0;
|
|
}
|
|
|
|
static __exit void dccp_li_exit(void)
|
|
{
|
|
kmem_cache_destroy(dccp_li_cachep);
|
|
}
|
|
|
|
module_init(dccp_li_init);
|
|
module_exit(dccp_li_exit);
|