* Re: possible deadlock in JFFS2
From: David Woodhouse @ 2004-01-26 12:44 UTC (permalink / raw)
To: Pierre Vandwalle; +Cc: linux-fsdevel
In-Reply-To: <3FFBC907DD03A34CA4410C5C745DEB120339A735@wnimail.WoodsideNet.Com>
On Sat, 2004-01-24 at 03:46 -0800, Pierre Vandwalle wrote:
> The deadlock is between:
> 1- a first process in jffs2_setattr doing an open/create which require truncating the inode pages
> 2- a second process in do_generic_file_read on the same file
Thanks for the report. At first glance, I suspect that a quick fix would
be to move the vmtruncate() down to the end of jffs2_setattr(), after
we've dropped the jffs2-private inode semaphore.
This increases the likelihood that we'll get a simultaneous call to
readpage() which ends up attempting to read a page from an offset
_greater_ than the new end-of-file, but as far as I can tell, nothing in
generic_file_read() actually prevents that anyway, and we handle it OK
by returning a page full of zeroes.
Equivalent fix for CVS HEAD and 2.6 version, if you're using that, is at
http://lists.infradead.org/pipermail/linux-mtd-cvs/2004-January/003460.html
Index: fs/jffs2/file.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/file.c,v
retrieving revision 1.58.2.7
retrieving revision 1.58.2.8
diff -u -p -r1.58.2.7 -r1.58.2.8
--- fs/jffs2/file.c 2 Nov 2003 13:51:17 -0000 1.58.2.7
+++ fs/jffs2/file.c 26 Jan 2004 12:40:32 -0000 1.58.2.8
@@ -1,7 +1,7 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2004 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
@@ -31,7 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
- * $Id: file.c,v 1.58.2.7 2003/11/02 13:51:17 dwmw2 Exp $
+ * $Id: file.c,v 1.58.2.8 2004/01/26 12:40:32 dwmw2 Exp $
*
*/
@@ -197,10 +197,8 @@ int jffs2_setattr (struct dentry *dentry
old_metadata = f->metadata;
- if (inode->i_size > ri->isize) {
- vmtruncate(inode, ri->isize);
+ if (inode->i_size > ri->isize)
jffs2_truncate_fraglist (c, &f->fraglist, ri->isize);
- }
if (inode->i_size < ri->isize) {
jffs2_add_full_dnode_to_inode(c, f, new_metadata);
@@ -215,6 +213,15 @@ int jffs2_setattr (struct dentry *dentry
}
jffs2_free_raw_inode(ri);
up(&f->sem);
+
+ /* We have to do the vmtruncate() without f->sem held, since
+ some pages may be locked and waiting for it in readpage().
+ We are protected from a simultaneous write() extending i_size
+ back past iattr->ia_size, because do_truncate() holds the
+ generic inode semaphore. */
+ if (inode->i_size > ri->isize)
+ vmtruncate(inode, ri->isize);
+
return 0;
}
--
dwmw2
^ permalink raw reply
* Re: Fried the onboard Broadcom 4401 network...
From: Jamie Lokier @ 2004-01-26 12:30 UTC (permalink / raw)
To: Tomas Ogren, linux-kernel
In-Reply-To: <20040126094815.GA2060@ing.umu.se>
Tomas Ogren wrote:
> On 26 January, 2004 - Pavel Machek sent me these 0,4K bytes:
> > > After that, I have not been able to get link (neither see it through
> > > Linux/WinXP or the physical LED). I have tried multiple cables and my
> > > laptop is perfectly happy with all of them, but the broadcom thingie
> > > seems not. The switch doesn't see link either.
> >
> > Try to physically unplug machine from AC for a while.
>
> Ah, thank you! Just turning the power switch off didn't help.. I suppose
> it's kept alive (for some values of alive ;) for WOL and such..
I was startled when I bought a power meter to find that my computers
and even some monitors consume power when switched off. I don't mean
"soft" off - even with the mechanical switch in the off position they
still consume significant power.
My AMD box consumes about 15W of power when the mechanical switch on
its power supply is off. About 35W when the mechanical switch is on
but the computer is in the "soft off" state (i.e. what you get when
you ask it to turn itself off).
-- Jamie
^ permalink raw reply
* IBM750GX support
From: Karoliya, Abhishek @ 2004-01-26 12:30 UTC (permalink / raw)
To: 'linuxppc-embedded@lists.linuxppc.org'
Hi All,
Is there anywhere support for the new IBM750GX processor?
Any pointer to it will greatly help.
For the time being I have used entries {in arch/ppc/kernel/cputables.c}
for IBM750FX {one that applies to all except for Rev 2.0 of the Processor}
as provided in MontaVista PE3.0 based on 2.4.18. Is this enough to get the
max out of GX?
Thanks
Abhi
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply
* Wrong Synaptics Touchpad detection when USB mouse present
From: Mattia Dongili @ 2004-01-26 12:17 UTC (permalink / raw)
To: Linux Kernel Mailing List
Hi,
[Please Cc me as I'm not subscribed to the list]
I'm experiencing problems with a dual configuration of mice on my
laptop. The sympthoms are:
- if I boot with my Logitech USB mouse plugged in, the Synaptics
Touchpad is not recognized as such but as "PS/2 Generic Mouse"
- if I boot without USB mouse plugged in or if I simply reload psmouse
after the boot process, the Synaptics Touchpad is recognized correctly
So it has something to do with the order modules are loaded.
I'm available to test patches and/or send more info.
I have some evidences of those facts:
---> /proc/bus/input/devices
usb mouse present during boot
I: Bus=0011 Vendor=0002 Product=0001 Version=0000
N: Name="PS/2 Generic Mouse"
P: Phys=isa0060/serio1/input0
H: Handlers=mouse0 event1
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=3
usb mouse absent during boot
I: Bus=0011 Vendor=0002 Product=0007 Version=0000
N: Name="SynPS/2 Synaptics TouchPad"
P: Phys=isa0060/serio1/input0
H: Handlers=mouse0 event1
B: EV=b
B: KEY=6420 0 670000 0 0 0 0 0 0 0 0
B: ABS=11000003
---> dmesg
usb mouse present during boot
input: PS/2 Generic Mouse on isa0060/serio1
usb mouse absent during boot or reloading psmouse
Synaptics Touchpad, model: 1
Firmware: 5.9
Sensor: 37
new absolute packet format
Touchpad has extended capability bits
-> multifinger detection
-> palm detection
input: SynPS/2 Synaptics TouchPad on isa0060/serio1
Linux inferi 2.6.1-2 #2 Mon Jan 26 11:09:55 CET 2004 i686 GNU/Linux
# lsmod
Module Size Used by
[...]
psmouse 18700 0
usb_storage 25600 0
scsi_mod 66860 1 usb_storage
hid 23680 0
uhci_hcd 30224 0
usbcore 97884 5 usb_storage,hid,uhci_hcd
radeon 116908 0
intel_agp 16412 1
agpgart 26920 2 intel_agp
sonypi 21052 0
speedstep_ich 3724 0
speedstep_lib 2944 1 speedstep_ich
evdev 7808 1
pcspkr 3428 0
rtc 10680 0
bye
--
mattia
:wq!
^ permalink raw reply
* Re: IMQ Replacement
From: Andre Correa @ 2004-01-26 12:16 UTC (permalink / raw)
To: Netfilter Mailing List
In-Reply-To: <400E9246.3020601@pobox.com>
Hi guys, Andy F. pointed out that Alexander Clouter and other guys
ported IMQ to 2.6. The URL to their port is:
http://www.digriz.org.uk/jdg-qos-script/#qos-2.6
I had no time or resources to test it so far...
I've got no answer from the last IMQ maintainer so I'll start a list for
us to discuss what we'll gonna do about it. I'll let you know later today.
I'm sorry if this thread is disturbing somebody in this list. I'll just
post 1 or 2 e-mails about it and we'll be out...
Meanwhile, anybody interrested in joining us to work with IMQ please
contact me privately.
tks...
Andre
^ permalink raw reply
* Re: [BENCHMARKS] Namesys VM patches improve kbuild
From: Nikita Danilov @ 2004-01-26 12:14 UTC (permalink / raw)
To: Nick Piggin; +Cc: Andrew Morton, linux-mm
In-Reply-To: <4014F915.7060300@cyberone.com.au>
Nick Piggin writes:
>
>
> Nikita Danilov wrote:
>
> >Nick Piggin writes:
> > >
> >
> >[...]
> >
> > >
> > > But by clearing the referenced bit when below the reclaim_mapped
> > > threshold, you're throwing this information away.
> > >
> > > Say you have 16 mapped pages on the active list, 8 referenced, 8 not.
> > > You do a !reclaim_mapped scan. Your 16 pages are now in the same
> > > order and none are referenced. You now do a reclaim_mapped scan and
> > > reclaim 8 pages. 4 of them were the referenced ones, 4 were not.
> > >
> > > With my change, you would reclaim all 8 non referenced pages.
> >
> >Which is wrong, because none of them was referenced _recently_. These
> >pages are cold, according to the VM's notion of hotness. (Long time
> >probably has passed between !reclaim_mapped and reclaim_mapped scans in
> >your example.)
> >
>
> Well you'd have to admit the referenced pages are hotter, but
> I guess I can't argue with the numbers: it must not be very
> significant.
>
> I just wonder why your patch makes such an improvement. You're
> basically putting mapped pages to one side until reclaim_mapped,
> which is similar to what my patch does, right?
Difference is that dont-rotate-active-list leaves mapped pages behind
the scanning point, in stead of moving them to the head of the active
list. Moving these pages to the head of the active list destroys LRU
approximation for the file system cache (see mark_page_accessed()): in
LRU, a page is moved to the head of the queue when accessed and later
migrates through the queue because other _hotter_ pages are added to the
head of the queue. But in the un-patched VM a page migrates through the
queue, because:
(1) other hotter pages are added to the head of the queue.
(2) other possibly _colder_ mapped pages are added to the head of the
queue.
(2) is obviously bad for the LRU approximation, and
dont-rotate-active-list patch gets rid of it.
>
>
Nikita.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"aart@kvack.org"> aart@kvack.org </a>
^ permalink raw reply
* Re: GCC-3.4 reorders asm() with -O2
From: Ralf Baechle @ 2004-01-26 8:36 UTC (permalink / raw)
To: Andi Kleen; +Cc: jh, echristo, hubicka, eager, gcc, linux-mips
In-Reply-To: <20040125202807.2d786115.ak@suse.de>
On Sun, Jan 25, 2004 at 08:28:07PM +0100, Andi Kleen wrote:
> > > It is. Ralf already knows about the problem, I think - we leave
> > > markers outside of functions which define an entry point, save some
> > > additional registers to the stack, and try to fall through to the
> > > following function. If the function gets emitted elsewhere, obviously,
> > > we've lost :)
> > >
> > > [This is save_static_function...]
> >
> > I only recently fixed the problem with the save_static() inline function
> > which of course was fragile, speculating on the compiler doing the
> > right thing ... I'll cook up a fix ...
>
> You can always use __attribute__((noinline))
Not in this particular case. save_static's purpose was saving all
caller saved registers into the stack so they can be accessed via the
usual struct pt_regs pointer and to make that work it to be inline before
any change of these registers. That was a small optimization but it also
was fragile so I removed that. save_static_function was meant to be
used immediately preceeding a syscall's C function and served the same
purpose. As the implementation ``knew'' gcc wasn't going to move around
the code just falling though worked fine but again that was fragile.
Ralf
^ permalink raw reply
* Re: [Qemu-devel] QEMU 0.5.2 testing
From: Renzo Davoli @ 2004-01-26 11:48 UTC (permalink / raw)
To: qemu-devel
In-Reply-To: <1075108796.5267.1.camel@axp.nolabel.net>
I am testing 0.5.2 (or the latest cvs).
Win98:
- install quite okay: it complains about a shortage of memory when
testing plug&pray devices.
- after install CD-ROM, Network interface are not automatically recognized.
- with in CONFIG.SYS:
device=c:\windows\oakcdrom.sys /d:mscd001
and in AUTOEXEC.BAT
mscdex /d:mscd001
(standard msdos methods)
the cdrom works (oakcdrom has been copied from the rescue disk).
- no way now to let the network interface work.
- I am going to reinstall it. Now at boot time ot complaints about the
display adapter ("your display adapter is not configured properly).
- floppy are okay at boot (I can boot from the rescue floopy) but
when win98 has boot all the floppies appear as unformatted, and
fails whem trying to format a new image.
Debian Linux:
- I have started a net installation and it worked quite well.
- I had to configure the ne.o (ne2000) module by hand (irq=9
iobase=0x300) but then the installation continued okay.
- CDROM is recognized at boot.
- I have not finished the installation just because it was too late
yesterday night.
- more info follows, I am planning to complete the installation
as soon as possible.
In both tests I am using the vdeq tool that interfaces qemu with my
"virtual distributed ethernet" vde network (see vde.sf.net).
ciao
renzo
^ permalink raw reply
* Re: latest 2.6 patch for dpt_i20 driver
From: Jasper Slits @ 2004-01-26 12:09 UTC (permalink / raw)
To: shaeffer; +Cc: linux-scsi
I managed to get dpt_i2o working with kernel 2.6.1-rc2.
First of it all, it's still risky to do it. But here is how:
get Mark's patch ->
http://marc.theaimsgroup.com/?l=linux-scsi&m=107210932819564&w=2
Hack the ./drivers/scsi/dpt_i2o.c and remove all references to the
access_count member.
The access_count member doesnt exist anymore but is it still included in
the shipped dpt_i2o.c file. I googled some changelogs and it appears to
be removed in a 2.5.x kernel, but correct me if i am wrong.
it's q&d but it works so far ;)
Compile & boot :)
I enabled bigmem (4GB) and didnt encounter any problems yet. I have to
do some benchmarks bonnie/postal to get a high load for a couple of
hours. Just to make sure it doesnt die ;)
^ permalink raw reply
* Side effects of a <defunct> kswapd ?
From: James Pearson @ 2004-01-26 12:07 UTC (permalink / raw)
To: linux-kernel
I have number of machines that have <defunct> kswapd processes - I'm
fairly sure what is causing this - it's an issue with autofs v4 and a
'VFS: Busy inodes after unmount. Self-destruct in 5 seconds Have a nice
day...' problem.
There are a few threads about this on the linux-nfs list - e.g.
http://marc.theaimsgroup.com/?t=106332683300004&r=1&w=2
Using autofs v4.1.0 appears to fix this problem.
However, I was wondering what would be the side effects of having a
<defunct> kswapd? - the box _appears_ to keep running fine (running
heavy CPU/memory applications).
Thanks
James Pearson
^ permalink raw reply
* Re: iptables routing help
From: John A. Sullivan III @ 2004-01-26 12:06 UTC (permalink / raw)
To: William Knop; +Cc: netfilter
In-Reply-To: <C6E1084C-4F67-11D8-A7CD-000A9577164C@hotmail.com>
On Sun, 2004-01-25 at 13:53, William Knop wrote:
> Okay, the problem is that we don't want to do nat (as I said in my
> original plee for help). We need external ips on all of the machines.
> Additionally, The ISP's DHCP server specifies it's own gateway, so I
> can't do normal routing, without spoofing the gateway's address and
> doing all sorts of ugly stuff (please correct me if I'm wrong).
>
>
> I was under the impression one could have iptables drop a packet from
> the prerouting or brouting table and it would go through the machine's
> routing table, without being specified on all the lan machines as the
> gateway.
>
>
> The physical layout we have are a bunch of boxes connected to a
> switch, and the dsl modem connected to the switch's uplink port. I
> could have the modem jack into a firewall box, or something, however
> the linux ethernet bridge seems to do very odd things to arps, and
> also iptables. Would bridging be necessary?
>
>
>
<snip>
This may not be as bad as it sounds and it my be a netfilter issue.
Looking at the topology, I would assume that there are several devices
on the same public subnet connect through the switch to the DSL modem in
which case they should talk to each other directly on that subnet
without sending the data across the DSL modem. But am I correct to
understand that even though these devices share the same switch and the
same DSL modem that they are allocated public addresses out of different
IP subnets?
If that is the case, the best solution is to install a second NIC into
each device and create a separate private network as already suggested.
Barring that, you can create a second, logical network on the same
media. Use iproute2 to bind a second address to each of the public
interfaces. These will all come from the same subnet and should be able
to communicate with each other. Just be sure to use the secondary
address when sending data between those devices.
ip address add dev0 192.168.1.4/24
ip address add dev0 192.168.1.5/24
ip address add dev0 192.168.1.6/24 . . . etc.
This is a bit dangerous as these devices are still publicly exposed and
the ISP may allow traffic on RFC1918 addresses on their internal
networks so you may want to tightly secure the devices even for traffic
from these "private" addresses using iptables.
This is the sort of set up that we use on our internal routers to
participate in the worldwide VPN project (http://www.worldwidevpn.com).
Good luck - John
--
John A. Sullivan III
Chief Technology Officer
Nexus Management
+1 207-985-7880
john.sullivan@nexusmgmt.com
---
If you are interested in helping to develop a GPL enterprise class
VPN/Firewall/Security device management console, please visit
http://iscs.sourceforge.net
^ permalink raw reply
* Re: [PATCH] sg driver against lk 2.6.1-bk6
From: Douglas Gilbert @ 2004-01-26 12:01 UTC (permalink / raw)
To: linux-scsi
In-Reply-To: <400E706A.1050008@torque.net>
[-- Attachment #1: Type: text/plain, Size: 430 bytes --]
Douglas Gilbert wrote:
> This patch is against the sg driver found in lk 2.6.1-bk6 .
> It is an update on the sg patch sent in the "devices with more
> than one node" thread:
> http://marc.theaimsgroup.com/?l=linux-scsi&m=107415435300783&w=2
Attached is an updated patch adding the cdev_unmap() call
which Kai Makisara reported as required. This should apply
against lk 2.6.1-bk6 up to the current lk 2.6.2-rc2 .
Doug Gilbert
[-- Attachment #2: sg_261rc1_simp.diff --]
[-- Type: text/plain, Size: 4372 bytes --]
--- linux/drivers/scsi/sg.c 2004-01-10 15:53:59.000000000 +1000
+++ linux/drivers/scsi/sg.c261rc1_simp 2004-01-24 22:17:15.000000000 +1000
@@ -58,10 +58,6 @@
#include <linux/cdev.h>
#include <linux/seq_file.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
#include <linux/blkdev.h>
#include "scsi.h"
#include "hosts.h"
@@ -73,7 +69,7 @@
#ifdef CONFIG_SCSI_PROC_FS
#include <linux/proc_fs.h>
-static char *sg_version_str = "3.5.30 [20031010]";
+static char *sg_version_str = "3.5.30 [20040124]";
static int sg_proc_init(void);
static void sg_proc_cleanup(void);
@@ -1333,6 +1329,10 @@
.fasync = sg_fasync,
};
+static struct class_simple * sg_sysfs_class;
+
+static int sg_sysfs_valid = 0;
+
static int
sg_add(struct class_device *cl_dev)
{
@@ -1360,7 +1360,7 @@
tmp_dev_max * sizeof(Sg_device *));
if (NULL == tmp_da) {
printk(KERN_ERR
- "sg_attach: device array cannot be resized\n");
+ "sg_add: device array cannot be resized\n");
error = -ENOMEM;
goto out;
}
@@ -1401,12 +1401,12 @@
sdp = NULL;
if (NULL == sdp) {
write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
- printk(KERN_ERR "sg_attach: Sg_device cannot be allocated\n");
+ printk(KERN_ERR "sg_add: Sg_device cannot be allocated\n");
error = -ENOMEM;
goto out;
}
- SCSI_LOG_TIMEOUT(3, printk("sg_attach: dev=%d \n", k));
+ SCSI_LOG_TIMEOUT(3, printk("sg_add: dev=%d \n", k));
memset(sdp, 0, sizeof(*sdp));
sprintf(disk->disk_name, "sg%d", k);
strncpy(cdev->kobj.name, disk->disk_name, KOBJ_NAME_LEN);
@@ -1432,16 +1432,24 @@
goto out;
}
sdp->cdev = cdev;
- error = sysfs_create_link(&cdev->kobj, &scsidp->sdev_gendev.kobj,
- "device");
- if (error)
- printk(KERN_ERR "sg_attach: unable to make symlink 'device'"
- " for sg%d\n", k);
- error = sysfs_create_link(&scsidp->sdev_gendev.kobj, &cdev->kobj,
- "generic");
- if (error)
- printk(KERN_ERR "sg_attach: unable to make symlink 'generic'"
- " back to sg%d\n", k);
+ if (sg_sysfs_valid) {
+ struct class_device * sg_class_member;
+
+ sg_class_member = class_simple_device_add(sg_sysfs_class,
+ MKDEV(SCSI_GENERIC_MAJOR, k),
+ cl_dev->dev, "%s",
+ disk->disk_name);
+ if (NULL == sg_class_member)
+ printk(KERN_WARNING "sg_add: "
+ "class_simple_device_add failed\n");
+ class_set_devdata(sg_class_member, sdp);
+ error = sysfs_create_link(&scsidp->sdev_gendev.kobj,
+ &sg_class_member->kobj, "generic");
+ if (error)
+ printk(KERN_ERR "sg_add: unable to make symlink "
+ "'generic' back to sg%d\n", k);
+ } else
+ printk(KERN_WARNING "sg_add: sg_sys INvalid\n");
printk(KERN_NOTICE
"Attached scsi generic sg%d at scsi%d, channel"
@@ -1512,7 +1520,8 @@
if (sdp) {
sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
- sysfs_remove_link(&sdp->cdev->kobj, "device");
+ class_simple_device_remove(MKDEV(SCSI_GENERIC_MAJOR, k));
+ cdev_unmap(MKDEV(SCSI_GENERIC_MAJOR, k), 1);
cdev_del(sdp->cdev);
sdp->cdev = NULL;
devfs_remove("%s/generic", scsidp->devfs_name);
@@ -1538,6 +1547,7 @@
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
+MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))");
static int __init
init_sg(void)
@@ -1551,16 +1561,23 @@
SG_MAX_DEVS, "sg");
if (rc)
return rc;
+ sg_sysfs_class = class_simple_create(THIS_MODULE, "scsi_generic");
+ if ( IS_ERR(sg_sysfs_class) ) {
+ rc = PTR_ERR(sg_sysfs_class);
+ goto err_out;
+ }
+ sg_sysfs_valid = 1;
rc = scsi_register_interface(&sg_interface);
- if (rc) {
- unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
- SG_MAX_DEVS);
- return rc;
- }
+ if (0 == rc) {
#ifdef CONFIG_SCSI_PROC_FS
- sg_proc_init();
+ sg_proc_init();
#endif /* CONFIG_SCSI_PROC_FS */
- return 0;
+ return 0;
+ }
+ class_simple_destroy(sg_sysfs_class);
+err_out:
+ unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS);
+ return rc;
}
static void __exit
@@ -1570,6 +1587,8 @@
sg_proc_cleanup();
#endif /* CONFIG_SCSI_PROC_FS */
scsi_unregister_interface(&sg_interface);
+ class_simple_destroy(sg_sysfs_class);
+ sg_sysfs_valid = 0;
unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
SG_MAX_DEVS);
if (sg_dev_arr != NULL) {
^ permalink raw reply
* [PATCH]: altq HFSC port
From: Patrick McHardy @ 2004-01-26 12:02 UTC (permalink / raw)
To: netdev; +Cc: linux-net, davem
[-- Attachment #1: Type: TEXT/PLAIN, Size: 1467 bytes --]
This patch is a port of HFSC from altq to Linux 2.6. HFSC is a
hierarchical packet scheduler which allows flexible resource
allocation by decoupling of bandwidth and delay. The original
version and a paper describing HFSC can be found here:
http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html .
iproute paches, 2.4 patches, tcsim patches and related stuff
are available at http://trash.net/~kaber/hfsc .
I would like to get this mergable, so comments/testing are highly
appreciated. It's running stable on 2.6 for a couple of month,
testing in tcsim and personal experience showed excellent results.
Known issues are:
- requeuing is broken. Currently, packets are requeued to the class
they came from without adjusting (internal) statistics. The easiest
solution is probably a fifo requeue-queue which is always dequeued
first.
- intolerant to user errors: when used with non-work-conserving inner
qdiscs it will crash when the inner qdisc decides not to give out
any packets and q.qlen != 0.
- layout of struct hfsc_class is all but optimal
The last issue is the License: The altq version is released under a
BSD-style License without advertising clause (the original authors
kindly agreed to remove it). It is my understanding that this is
compatible with the GPL, and because the code includes some minor
amounts of GPL'ed code the correct License is GPL and not
Dual BSD/GPL. I would be glad if someone can confirm that this is
correct.
Best regards,
Patrick
[-- Attachment #2: Type: TEXT/PLAIN, Size: 51405 bytes --]
diff -urN linux-2.5/include/linux/pkt_sched.h linux-2.5-hfsc/include/linux/pkt_sched.h
--- linux-2.5/include/linux/pkt_sched.h 2004-01-26 10:41:18.000000000 +0100
+++ linux-2.5-hfsc/include/linux/pkt_sched.h 2004-01-26 10:45:56.000000000 +0100
@@ -290,6 +290,37 @@
__u32 ctokens;
};
+/* HFSC section */
+
+struct tc_hfsc_qopt
+{
+ __u16 defcls; /* default class */
+};
+
+struct tc_service_curve
+{
+ __u32 m1; /* slope of the first segment in bps */
+ __u32 d; /* x-projection of the first segment in us */
+ __u32 m2; /* slope of the second segment in bps */
+};
+
+struct tc_hfsc_stats
+{
+ __u64 work; /* total work done */
+ __u64 rtwork; /* work done by real-time criteria */
+ __u32 period; /* current period */
+ __u32 level; /* class level in hierarchy */
+};
+
+enum
+{
+ TCA_HFSC_UNSPEC,
+ TCA_HFSC_RSC,
+ TCA_HFSC_FSC,
+ TCA_HFSC_USC,
+ TCA_HFSC_MAX = TCA_HFSC_USC
+};
+
/* CBQ section */
#define TC_CBQ_MAXPRIO 8
diff -urN linux-2.5/include/net/pkt_sched.h linux-2.5-hfsc/include/net/pkt_sched.h
--- linux-2.5/include/net/pkt_sched.h 2004-01-26 10:40:02.000000000 +0100
+++ linux-2.5-hfsc/include/net/pkt_sched.h 2004-01-26 10:44:27.000000000 +0100
@@ -203,6 +203,7 @@
#define PSCHED_GET_TIME(stamp) do_gettimeofday(&(stamp))
#define PSCHED_US2JIFFIE(usecs) (((usecs)+(1000000/HZ-1))/(1000000/HZ))
+#define PSCHED_JIFFIE2US(delay) ((delay)*(1000000/HZ))
#define PSCHED_EXPORTLIST EXPORT_SYMBOL(psched_tod_diff);
@@ -251,6 +252,7 @@
#endif
#define PSCHED_US2JIFFIE(delay) (((delay)+(1<<PSCHED_JSCALE)-1)>>PSCHED_JSCALE)
+#define PSCHED_JIFFIE2US(delay) ((delay)<<PSCHED_JSCALE)
#elif PSCHED_CLOCK_SOURCE == PSCHED_CPU
@@ -261,6 +263,7 @@
EXPORT_SYMBOL(psched_clock_scale);
#define PSCHED_US2JIFFIE(delay) (((delay)+psched_clock_per_hz-1)/psched_clock_per_hz)
+#define PSCHED_JIFFIE2US(delay) ((delay)*psched_clock_per_hz)
#ifdef CONFIG_X86_TSC
diff -urN linux-2.5/net/sched/Kconfig linux-2.5-hfsc/net/sched/Kconfig
--- linux-2.5/net/sched/Kconfig 2004-01-26 10:41:36.000000000 +0100
+++ linux-2.5-hfsc/net/sched/Kconfig 2004-01-26 10:46:41.000000000 +0100
@@ -39,6 +39,16 @@
To compile this code as a module, choose M here: the
module will be called sch_htb.
+config NET_SCH_HFSC
+ tristate "HFSC packet scheduler"
+ depends on NET_SCHED
+ ---help---
+ Say Y here if you want to use the Hierarchical Fair Service Curve
+ (HFSC) packet scheduling algorithm for some of your network devices.
+
+ To compile this code as a module, choose M here: the
+ module will be called sch_hfsc.
+
config NET_SCH_CSZ
tristate "CSZ packet scheduler"
depends on NET_SCHED
diff -urN linux-2.5/net/sched/sch_hfsc.c linux-2.5-hfsc/net/sched/sch_hfsc.c
--- linux-2.5/net/sched/sch_hfsc.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5-hfsc/net/sched/sch_hfsc.c 2004-01-26 10:45:25.000000000 +0100
@@ -0,0 +1,1860 @@
+/*
+ * Copyright (c) 2003 Patrick McHardy, <kaber@trash.net>
+ *
+ * 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.
+ *
+ * 2003-10-17 - Ported from altq
+ */
+/*
+ * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation is hereby granted (including for commercial or
+ * for-profit use), provided that both the copyright notice and this
+ * permission notice appear in all copies of the software, derivative
+ * works, or modified versions, and any portions thereof.
+ *
+ * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF
+ * WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS
+ * SOFTWARE IN ITS ``AS IS'' CONDITION, AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Carnegie Mellon encourages (but does not require) users of this
+ * software to return any improvements or extensions that they make,
+ * and to grant Carnegie Mellon the rights to redistribute these
+ * changes without encumbrance.
+ */
+/*
+ * H-FSC is described in Proceedings of SIGCOMM'97,
+ * "A Hierarchical Fair Service Curve Algorithm for Link-Sharing,
+ * Real-Time and Priority Service"
+ * by Ion Stoica, Hui Zhang, and T. S. Eugene Ng.
+ *
+ * Oleg Cherevko <olwi@aq.ml.com.ua> added the upperlimit for link-sharing.
+ * when a class has an upperlimit, the fit-time is computed from the
+ * upperlimit service curve. the link-sharing scheduler does not schedule
+ * a class whose fit-time exceeds the current time.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/jiffies.h>
+#include <linux/compiler.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/pkt_sched.h>
+#include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
+#include <asm/system.h>
+#include <asm/div64.h>
+
+#define HFSC_DEBUG 1
+
+/*
+ * kernel internal service curve representation:
+ * coordinates are given by 64 bit unsigned integers.
+ * x-axis: unit is clock count.
+ * y-axis: unit is byte.
+ *
+ * The service curve parameters are converted to the internal
+ * representation. The slope values are scaled to avoid overflow.
+ * the inverse slope values as well as the y-projection of the 1st
+ * segment are kept in order to to avoid 64-bit divide operations
+ * that are expensive on 32-bit architectures.
+ */
+
+struct internal_sc
+{
+ u_int64_t sm1; /* scaled slope of the 1st segment */
+ u_int64_t ism1; /* scaled inverse-slope of the 1st segment */
+ u_int64_t dx; /* the x-projection of the 1st segment */
+ u_int64_t dy; /* the y-projection of the 1st segment */
+ u_int64_t sm2; /* scaled slope of the 2nd segment */
+ u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */
+};
+
+/* runtime service curve */
+struct runtime_sc
+{
+ u_int64_t x; /* current starting position on x-axis */
+ u_int64_t y; /* current starting position on y-axis */
+ u_int64_t sm1; /* scaled slope of the 1st segment */
+ u_int64_t ism1; /* scaled inverse-slope of the 1st segment */
+ u_int64_t dx; /* the x-projection of the 1st segment */
+ u_int64_t dy; /* the y-projection of the 1st segment */
+ u_int64_t sm2; /* scaled slope of the 2nd segment */
+ u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */
+};
+
+enum hfsc_class_flags
+{
+ HFSC_RSC = 0x1,
+ HFSC_FSC = 0x2,
+ HFSC_USC = 0x4
+};
+
+struct hfsc_class
+{
+ u_int32_t classid; /* class id */
+ unsigned int refcnt; /* usage count */
+
+ struct tc_stats stats; /* generic statistics */
+ unsigned int level; /* class level in hierarchy */
+ struct tcf_proto *filter_list; /* filter list */
+ unsigned int filter_cnt; /* filter count */
+
+ struct hfsc_sched *sched; /* scheduler data */
+ struct hfsc_class *cl_parent; /* parent class */
+ struct list_head siblings; /* sibling classes */
+ struct list_head children; /* child classes */
+ struct Qdisc *qdisc; /* leaf qdisc */
+
+ struct list_head actlist; /* active children list */
+ struct list_head alist; /* active children list member */
+ struct list_head ellist; /* eligible list member */
+ struct list_head hlist; /* hash list member */
+ struct list_head dlist; /* drop list member */
+
+ u_int64_t cl_total; /* total work in bytes */
+ u_int64_t cl_cumul; /* cumulative work in bytes done by
+ real-time criteria */
+
+ u_int64_t cl_d; /* deadline*/
+ u_int64_t cl_e; /* eligible time */
+ u_int64_t cl_vt; /* virtual time */
+ u_int64_t cl_f; /* time when this class will fit for
+ link-sharing, max(myf, cfmin) */
+ u_int64_t cl_myf; /* my fit-time (calculated from this
+ class's own upperlimit curve) */
+ u_int64_t cl_myfadj; /* my fit-time adjustment (to cancel
+ history dependence) */
+ u_int64_t cl_cfmin; /* earliest children's fit-time (used
+ with cl_myf to obtain cl_f) */
+ u_int64_t cl_cvtmin; /* minimal virtual time among the
+ children fit for link-sharing
+ (monotonic within a period) */
+ u_int64_t cl_vtadj; /* intra-period cumulative vt
+ adjustment */
+ u_int64_t cl_vtoff; /* inter-period cumulative vt offset */
+ u_int64_t cl_cvtmax; /* max child's vt in the last period */
+
+ struct internal_sc cl_rsc; /* internal real-time service curve */
+ struct internal_sc cl_fsc; /* internal fair service curve */
+ struct internal_sc cl_usc; /* internal upperlimit service curve */
+ struct runtime_sc cl_deadline; /* deadline curve */
+ struct runtime_sc cl_eligible; /* eligible curve */
+ struct runtime_sc cl_virtual; /* virtual curve */
+ struct runtime_sc cl_ulimit; /* upperlimit curve */
+
+ unsigned long cl_flags; /* which curves are valid */
+ unsigned long cl_vtperiod; /* vt period sequence number */
+ unsigned long cl_parentperiod;/* parent's vt period sequence number*/
+ unsigned long cl_nactive; /* number of active children */
+};
+
+#define HFSC_HSIZE 16
+
+struct hfsc_sched
+{
+ u_int16_t defcls; /* default class id */
+
+ struct hfsc_class root; /* root class */
+ struct hfsc_class *last_xmit; /* class that transmitted last
+ packet (for requeueing) */
+ struct list_head clhash[HFSC_HSIZE]; /* class hash */
+ struct list_head eligible; /* eligible list */
+ struct list_head droplist; /* active leaf class list (for
+ dropping) */
+ struct timer_list wd_timer; /* watchdog timer */
+};
+
+/*
+ * macros
+ */
+#if PSCHED_CLOCK_SOURCE == PSCHED_GETTIMEOFDAY
+#include <linux/time.h>
+#undef PSCHED_GET_TIME
+#define PSCHED_GET_TIME(stamp) \
+do { \
+ struct timeval tv; \
+ do_gettimeofday(&tv); \
+ (stamp) = 1000000ULL * tv.tv_sec + tv.tv_usec; \
+} while (0)
+#endif
+
+#if HFSC_DEBUG
+#define ASSERT(cond) \
+do { \
+ if (unlikely(!(cond))) \
+ printk("assertion %s failed at %s:%i (%s)\n", \
+ #cond, __FILE__, __LINE__, __FUNCTION__); \
+} while (0)
+#else
+#define ASSERT(cond)
+#endif /* HFSC_DEBUG */
+
+#define HT_INFINITY 0xffffffffffffffffULL /* infinite time value */
+
+
+/*
+ * eligible list holds backlogged classes being sorted by their eligible times.
+ * there is one eligible list per hfsc instance.
+ */
+
+static void
+ellist_insert(struct hfsc_class *cl)
+{
+ struct list_head *head = &cl->sched->eligible;
+ struct hfsc_class *p;
+
+ /* check the last entry first */
+ if (list_empty(head) ||
+ ((p = list_entry(head->prev, struct hfsc_class, ellist)) &&
+ p->cl_e <= cl->cl_e)) {
+ list_add_tail(&cl->ellist, head);
+ return;
+ }
+
+ list_for_each_entry(p, head, ellist) {
+ if (cl->cl_e < p->cl_e) {
+ /* insert cl before p */
+ list_add_tail(&cl->ellist, &p->ellist);
+ return;
+ }
+ }
+ ASSERT(0); /* should not reach here */
+}
+
+static inline void
+ellist_remove(struct hfsc_class *cl)
+{
+ list_del(&cl->ellist);
+}
+
+static void
+ellist_update(struct hfsc_class *cl)
+{
+ struct list_head *head = &cl->sched->eligible;
+ struct hfsc_class *p, *last;
+
+ /*
+ * the eligible time of a class increases monotonically.
+ * if the next entry has a larger eligible time, nothing to do.
+ */
+ if (cl->ellist.next == head ||
+ ((p = list_entry(cl->ellist.next, struct hfsc_class, ellist)) &&
+ cl->cl_e <= p->cl_e))
+ return;
+
+ /* check the last entry */
+ last = list_entry(head->prev, struct hfsc_class, ellist);
+ if (last->cl_e <= cl->cl_e) {
+ list_move_tail(&cl->ellist, head);
+ return;
+ }
+
+ /*
+ * the new position must be between the next entry
+ * and the last entry
+ */
+ list_for_each_entry_continue(p, head, ellist) {
+ if (cl->cl_e < p->cl_e) {
+ list_move_tail(&cl->ellist, &p->ellist);
+ return;
+ }
+ }
+ ASSERT(0); /* should not reach here */
+}
+
+/* find the class with the minimum deadline among the eligible classes */
+static inline struct hfsc_class *
+ellist_get_mindl(struct list_head *head, u_int64_t cur_time)
+{
+ struct hfsc_class *p, *cl = NULL;
+
+ list_for_each_entry(p, head, ellist) {
+ if (p->cl_e > cur_time)
+ break;
+ if (cl == NULL || p->cl_d < cl->cl_d)
+ cl = p;
+ }
+ return cl;
+}
+
+/* find the class with minimum eligible time among the eligible classes */
+static inline struct hfsc_class *
+ellist_get_minel(struct list_head *head)
+{
+ if (list_empty(head))
+ return NULL;
+ return list_entry(head->next, struct hfsc_class, ellist);
+}
+
+/*
+ * active children list holds backlogged child classes being sorted
+ * by their virtual time. each intermediate class has one active
+ * children list.
+ */
+static void
+actlist_insert(struct hfsc_class *cl)
+{
+ struct list_head *head = &cl->cl_parent->actlist;
+ struct hfsc_class *p;
+
+ /* check the last entry first */
+ if (list_empty(head) ||
+ ((p = list_entry(head->prev, struct hfsc_class, alist)) &&
+ p->cl_vt <= cl->cl_vt)) {
+ list_add_tail(&cl->alist, head);
+ return;
+ }
+
+ list_for_each_entry(p, head, alist) {
+ if (cl->cl_vt < p->cl_vt) {
+ /* insert cl before p */
+ list_add_tail(&cl->alist, &p->alist);
+ return;
+ }
+ }
+ ASSERT(0); /* should not reach here */
+}
+
+static inline void
+actlist_remove(struct hfsc_class *cl)
+{
+ list_del(&cl->alist);
+}
+
+static void
+actlist_update(struct hfsc_class *cl)
+{
+ struct list_head *head = &cl->cl_parent->actlist;
+ struct hfsc_class *p, *last;
+
+ /*
+ * the virtual time of a class increases monotonically.
+ * if the next entry has a larger virtual time, nothing to do.
+ */
+ if (cl->alist.next == head ||
+ ((p = list_entry(cl->alist.next, struct hfsc_class, alist)) &&
+ cl->cl_vt <= p->cl_vt))
+ return;
+
+ /* check the last entry */
+ last = list_entry(head->prev, struct hfsc_class, alist);
+ if (last->cl_vt <= cl->cl_vt) {
+ list_move_tail(&cl->alist, head);
+ return;
+ }
+
+ /*
+ * the new position must be between the next entry
+ * and the last entry
+ */
+ list_for_each_entry_continue(p, head, alist) {
+ if (cl->cl_vt < p->cl_vt) {
+ list_move_tail(&cl->alist, &p->alist);
+ return;
+ }
+ }
+ ASSERT(0); /* should not reach here */
+}
+
+static inline struct hfsc_class *
+actlist_firstfit(struct hfsc_class *cl, u_int64_t cur_time)
+{
+ struct hfsc_class *p;
+
+ list_for_each_entry(p, &cl->actlist, alist) {
+ if (p->cl_f <= cur_time) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * get the leaf class with the minimum vt in the hierarchy
+ */
+static struct hfsc_class *
+actlist_get_minvt(struct hfsc_class *cl, u_int64_t cur_time)
+{
+ /* if root-class's cfmin is bigger than cur_time nothing to do */
+ if (cl->cl_cfmin > cur_time)
+ return NULL;
+
+ while (cl->level > 0) {
+ cl = actlist_firstfit(cl, cur_time);
+ if (cl == NULL)
+ return NULL;
+ /*
+ * update parent's cl_cvtmin.
+ */
+ if (cl->cl_parent->cl_cvtmin < cl->cl_vt)
+ cl->cl_parent->cl_cvtmin = cl->cl_vt;
+ }
+ return cl;
+}
+
+/*
+ * service curve support functions
+ *
+ * external service curve parameters
+ * m: bps
+ * d: us
+ * internal service curve parameters
+ * sm: (bytes/psched_us) << SM_SHIFT
+ * ism: (psched_us/byte) << ISM_SHIFT
+ * dx: psched_us
+ *
+ * Time source resolution
+ * PSCHED_JIFFIES: for 48<=HZ<=1534 resolution is between 0.63us and 1.27us.
+ * PSCHED_CPU: resolution is between 0.5us and 1us.
+ * PSCHED_GETTIMEOFDAY: resolution is exactly 1us.
+ *
+ * sm and ism are scaled in order to keep effective digits.
+ * SM_SHIFT and ISM_SHIFT are selected to keep at least 4 effective
+ * digits in decimal using the following table.
+ *
+ * Note: We can afford the additional accuracy (altq hfsc keeps at most
+ * 3 effective digits) thanks to the fact that linux clock is bounded
+ * much more tightly.
+ *
+ * bits/sec 100Kbps 1Mbps 10Mbps 100Mbps 1Gbps
+ * ------------+-------------------------------------------------------
+ * bytes/0.5us 6.25e-3 62.5e-3 625e-3 6250e-e 62500e-3
+ * bytes/us 12.5e-3 125e-3 1250e-3 12500e-3 125000e-3
+ * bytes/1.27us 15.875e-3 158.75e-3 1587.5e-3 15875e-3 158750e-3
+ *
+ * 0.5us/byte 160 16 1.6 0.16 0.016
+ * us/byte 80 8 0.8 0.08 0.008
+ * 1.27us/byte 63 6.3 0.63 0.063 0.0063
+ */
+#define SM_SHIFT 20
+#define ISM_SHIFT 18
+
+#define SM_MASK ((1ULL << SM_SHIFT) - 1)
+#define ISM_MASK ((1ULL << ISM_SHIFT) - 1)
+
+static inline u_int64_t
+seg_x2y(u_int64_t x, u_int64_t sm)
+{
+ u_int64_t y;
+
+ /*
+ * compute
+ * y = x * sm >> SM_SHIFT
+ * but divide it for the upper and lower bits to avoid overflow
+ */
+ y = (x >> SM_SHIFT) * sm + (((x & SM_MASK) * sm) >> SM_SHIFT);
+ return y;
+}
+
+static inline u_int64_t
+seg_y2x(u_int64_t y, u_int64_t ism)
+{
+ u_int64_t x;
+
+ if (y == 0)
+ x = 0;
+ else if (ism == HT_INFINITY)
+ x = HT_INFINITY;
+ else {
+ x = (y >> ISM_SHIFT) * ism
+ + (((y & ISM_MASK) * ism) >> ISM_SHIFT);
+ }
+ return x;
+}
+
+/* Convert m (bps) into sm (bytes/psched us) */
+static u_int64_t
+m2sm(u_int32_t m)
+{
+ u_int64_t sm;
+
+ sm = ((u_int64_t)m << SM_SHIFT);
+ sm += PSCHED_JIFFIE2US(HZ) - 1;
+ do_div(sm, PSCHED_JIFFIE2US(HZ));
+ return sm;
+}
+
+/* convert m (bps) into ism (psched us/byte) */
+static u_int64_t
+m2ism(u_int32_t m)
+{
+ u_int64_t ism;
+
+ if (m == 0)
+ ism = HT_INFINITY;
+ else {
+ ism = ((u_int64_t)PSCHED_JIFFIE2US(HZ) << ISM_SHIFT);
+ ism += m - 1;
+ do_div(ism, m);
+ }
+ return ism;
+}
+
+/* convert d (us) into dx (psched us) */
+static u_int64_t
+d2dx(u_int32_t d)
+{
+ u_int64_t dx;
+
+ dx = ((u_int64_t)d * PSCHED_JIFFIE2US(HZ));
+ dx += 1000000 - 1;
+ do_div(dx, 1000000);
+ return dx;
+}
+
+/* convert sm (bytes/psched us) into m (bps) */
+static u_int32_t
+sm2m(u_int64_t sm)
+{
+ u_int64_t m;
+
+ m = (sm * PSCHED_JIFFIE2US(HZ)) >> SM_SHIFT;
+ return (u_int32_t)m;
+}
+
+/* convert dx (psched us) into d (us) */
+static u_int32_t
+dx2d(u_int64_t dx)
+{
+ u_int64_t d;
+
+ d = dx * 1000000;
+ do_div(d, PSCHED_JIFFIE2US(HZ));
+ return (u_int32_t)d;
+}
+
+static void
+sc2isc(struct tc_service_curve *sc, struct internal_sc *isc)
+{
+ isc->sm1 = m2sm(sc->m1);
+ isc->ism1 = m2ism(sc->m1);
+ isc->dx = d2dx(sc->d);
+ isc->dy = seg_x2y(isc->dx, isc->sm1);
+ isc->sm2 = m2sm(sc->m2);
+ isc->ism2 = m2ism(sc->m2);
+}
+
+/*
+ * initialize the runtime service curve with the given internal
+ * service curve starting at (x, y).
+ */
+static void
+rtsc_init(struct runtime_sc *rtsc, struct internal_sc *isc, u_int64_t x,
+ u_int64_t y)
+{
+ rtsc->x = x;
+ rtsc->y = y;
+ rtsc->sm1 = isc->sm1;
+ rtsc->ism1 = isc->ism1;
+ rtsc->dx = isc->dx;
+ rtsc->dy = isc->dy;
+ rtsc->sm2 = isc->sm2;
+ rtsc->ism2 = isc->ism2;
+}
+
+/*
+ * calculate the y-projection of the runtime service curve by the
+ * given x-projection value
+ */
+static u_int64_t
+rtsc_y2x(struct runtime_sc *rtsc, u_int64_t y)
+{
+ u_int64_t x;
+
+ if (y < rtsc->y)
+ x = rtsc->x;
+ else if (y <= rtsc->y + rtsc->dy) {
+ /* x belongs to the 1st segment */
+ if (rtsc->dy == 0)
+ x = rtsc->x + rtsc->dx;
+ else
+ x = rtsc->x + seg_y2x(y - rtsc->y, rtsc->ism1);
+ } else {
+ /* x belongs to the 2nd segment */
+ x = rtsc->x + rtsc->dx
+ + seg_y2x(y - rtsc->y - rtsc->dy, rtsc->ism2);
+ }
+ return x;
+}
+
+static u_int64_t
+rtsc_x2y(struct runtime_sc *rtsc, u_int64_t x)
+{
+ u_int64_t y;
+
+ if (x <= rtsc->x)
+ y = rtsc->y;
+ else if (x <= rtsc->x + rtsc->dx)
+ /* y belongs to the 1st segment */
+ y = rtsc->y + seg_x2y(x - rtsc->x, rtsc->sm1);
+ else
+ /* y belongs to the 2nd segment */
+ y = rtsc->y + rtsc->dy
+ + seg_x2y(x - rtsc->x - rtsc->dx, rtsc->sm2);
+ return y;
+}
+
+/*
+ * update the runtime service curve by taking the minimum of the current
+ * runtime service curve and the service curve starting at (x, y).
+ */
+static void
+rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u_int64_t x,
+ u_int64_t y)
+{
+ u_int64_t y1, y2, dx, dy;
+ u_int32_t dsm;
+
+ if (isc->sm1 <= isc->sm2) {
+ /* service curve is convex */
+ y1 = rtsc_x2y(rtsc, x);
+ if (y1 < y)
+ /* the current rtsc is smaller */
+ return;
+ rtsc->x = x;
+ rtsc->y = y;
+ return;
+ }
+
+ /*
+ * service curve is concave
+ * compute the two y values of the current rtsc
+ * y1: at x
+ * y2: at (x + dx)
+ */
+ y1 = rtsc_x2y(rtsc, x);
+ if (y1 <= y) {
+ /* rtsc is below isc, no change to rtsc */
+ return;
+ }
+
+ y2 = rtsc_x2y(rtsc, x + isc->dx);
+ if (y2 >= y + isc->dy) {
+ /* rtsc is above isc, replace rtsc by isc */
+ rtsc->x = x;
+ rtsc->y = y;
+ rtsc->dx = isc->dx;
+ rtsc->dy = isc->dy;
+ return;
+ }
+
+ /*
+ * the two curves intersect
+ * compute the offsets (dx, dy) using the reverse
+ * function of seg_x2y()
+ * seg_x2y(dx, sm1) == seg_x2y(dx, sm2) + (y1 - y)
+ */
+ dx = (y1 - y) << SM_SHIFT;
+ dsm = isc->sm1 - isc->sm2;
+ do_div(dx, dsm);
+ /*
+ * check if (x, y1) belongs to the 1st segment of rtsc.
+ * if so, add the offset.
+ */
+ if (rtsc->x + rtsc->dx > x)
+ dx += rtsc->x + rtsc->dx - x;
+ dy = seg_x2y(dx, isc->sm1);
+
+ rtsc->x = x;
+ rtsc->y = y;
+ rtsc->dx = dx;
+ rtsc->dy = dy;
+ return;
+}
+
+static void
+init_ed(struct hfsc_class *cl, unsigned int next_len)
+{
+ u_int64_t cur_time;
+
+ PSCHED_GET_TIME(cur_time);
+
+ /* update the deadline curve */
+ rtsc_min(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
+
+ /*
+ * update the eligible curve.
+ * for concave, it is equal to the deadline curve.
+ * for convex, it is a linear curve with slope m2.
+ */
+ cl->cl_eligible = cl->cl_deadline;
+ if (cl->cl_rsc.sm1 <= cl->cl_rsc.sm2) {
+ cl->cl_eligible.dx = 0;
+ cl->cl_eligible.dy = 0;
+ }
+
+ /* compute e and d */
+ cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
+ cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
+
+ ellist_insert(cl);
+}
+
+static void
+update_ed(struct hfsc_class *cl, unsigned int next_len)
+{
+ cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
+ cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
+
+ ellist_update(cl);
+}
+
+static inline void
+update_d(struct hfsc_class *cl, unsigned int next_len)
+{
+ cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
+}
+
+static void
+update_cfmin(struct hfsc_class *cl)
+{
+ struct hfsc_class *p;
+ u_int64_t cfmin;
+
+ if (list_empty(&cl->actlist)) {
+ cl->cl_cfmin = 0;
+ return;
+ }
+ cfmin = HT_INFINITY;
+ list_for_each_entry(p, &cl->actlist, alist) {
+ if (p->cl_f == 0) {
+ cl->cl_cfmin = 0;
+ return;
+ }
+ if (p->cl_f < cfmin)
+ cfmin = p->cl_f;
+ }
+ cl->cl_cfmin = cfmin;
+}
+
+static void
+init_vf(struct hfsc_class *cl, unsigned int len)
+{
+ struct hfsc_class *max_cl, *p;
+ u_int64_t vt, f, cur_time;
+ int go_active;
+
+ cur_time = 0;
+ go_active = 1;
+ for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
+ if (go_active && cl->cl_nactive++ == 0)
+ go_active = 1;
+ else
+ go_active = 0;
+
+ if (go_active) {
+ if (!list_empty(&cl->cl_parent->actlist)) {
+ max_cl = list_entry(cl->cl_parent->actlist.prev,
+ struct hfsc_class, alist);
+ /*
+ * set vt to the average of the min and max
+ * classes. if the parent's period didn't
+ * change, don't decrease vt of the class.
+ */
+ vt = max_cl->cl_vt;
+ if (cl->cl_parent->cl_cvtmin != 0)
+ vt = (cl->cl_parent->cl_cvtmin + vt)/2;
+
+ if (cl->cl_parent->cl_vtperiod !=
+ cl->cl_parentperiod || vt > cl->cl_vt)
+ cl->cl_vt = vt;
+ } else {
+ /*
+ * first child for a new parent backlog period.
+ * add parent's cvtmax to vtoff of children
+ * to make a new vt (vtoff + vt) larger than
+ * the vt in the last period for all children.
+ */
+ vt = cl->cl_parent->cl_cvtmax;
+ list_for_each_entry(p, &cl->cl_parent->children,
+ siblings)
+ p->cl_vtoff += vt;
+ cl->cl_vt = 0;
+ cl->cl_parent->cl_cvtmax = 0;
+ cl->cl_parent->cl_cvtmin = 0;
+ }
+
+ /* update the virtual curve */
+ vt = cl->cl_vt + cl->cl_vtoff;
+ rtsc_min(&cl->cl_virtual, &cl->cl_fsc, vt,
+ cl->cl_total);
+ if (cl->cl_virtual.x == vt) {
+ cl->cl_virtual.x -= cl->cl_vtoff;
+ cl->cl_vtoff = 0;
+ }
+ cl->cl_vtadj = 0;
+
+ cl->cl_vtperiod++; /* increment vt period */
+ cl->cl_parentperiod = cl->cl_parent->cl_vtperiod;
+ if (cl->cl_parent->cl_nactive == 0)
+ cl->cl_parentperiod++;
+ cl->cl_f = 0;
+
+ actlist_insert(cl);
+
+ if (cl->cl_flags & HFSC_USC) {
+ /* class has upper limit curve */
+ if (cur_time == 0)
+ PSCHED_GET_TIME(cur_time);
+
+ /* update the ulimit curve */
+ rtsc_min(&cl->cl_ulimit, &cl->cl_usc, cur_time,
+ cl->cl_total);
+ /* compute myf */
+ cl->cl_myf = rtsc_y2x(&cl->cl_ulimit,
+ cl->cl_total);
+ cl->cl_myfadj = 0;
+ }
+ }
+
+ f = max(cl->cl_myf, cl->cl_cfmin);
+ if (f != cl->cl_f) {
+ cl->cl_f = f;
+ update_cfmin(cl->cl_parent);
+ }
+ }
+}
+
+static void
+update_vf(struct hfsc_class *cl, unsigned int len, u_int64_t cur_time)
+{
+ u_int64_t f; /* , myf_bound, delta; */
+ int go_passive = 0;
+
+ if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC)
+ go_passive = 1;
+
+ for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
+ cl->cl_total += len;
+
+ if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0)
+ continue;
+
+ if (go_passive && --cl->cl_nactive == 0)
+ go_passive = 1;
+ else
+ go_passive = 0;
+
+ if (go_passive) {
+ /* no more active child, going passive */
+
+ /* update cvtmax of the parent class */
+ if (cl->cl_vt > cl->cl_parent->cl_cvtmax)
+ cl->cl_parent->cl_cvtmax = cl->cl_vt;
+
+ /* remove this class from the vt list */
+ actlist_remove(cl);
+
+ update_cfmin(cl->cl_parent);
+
+ continue;
+ }
+
+ /*
+ * update vt and f
+ */
+ cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total)
+ - cl->cl_vtoff + cl->cl_vtadj;
+
+ /*
+ * if vt of the class is smaller than cvtmin,
+ * the class was skipped in the past due to non-fit.
+ * if so, we need to adjust vtadj.
+ */
+ if (cl->cl_vt < cl->cl_parent->cl_cvtmin) {
+ cl->cl_vtadj += cl->cl_parent->cl_cvtmin - cl->cl_vt;
+ cl->cl_vt = cl->cl_parent->cl_cvtmin;
+ }
+
+ /* update the vt list */
+ actlist_update(cl);
+
+ if (cl->cl_flags & HFSC_USC) {
+ cl->cl_myf = cl->cl_myfadj + rtsc_y2x(&cl->cl_ulimit,
+ cl->cl_total);
+#if 0
+ /*
+ * This code causes classes to stay way under their
+ * limit when multiple classes are used at gigabit
+ * speed. needs investigation. -kaber
+ */
+ /*
+ * if myf lags behind by more than one clock tick
+ * from the current time, adjust myfadj to prevent
+ * a rate-limited class from going greedy.
+ * in a steady state under rate-limiting, myf
+ * fluctuates within one clock tick.
+ */
+ myf_bound = cur_time - PSCHED_JIFFIE2US(1);
+ if (cl->cl_myf < myf_bound) {
+ delta = cur_time - cl->cl_myf;
+ cl->cl_myfadj += delta;
+ cl->cl_myf += delta;
+ }
+#endif
+ }
+
+ f = max(cl->cl_myf, cl->cl_cfmin);
+ if (f != cl->cl_f) {
+ cl->cl_f = f;
+ update_cfmin(cl->cl_parent);
+ }
+ }
+}
+
+static void
+set_active(struct hfsc_class *cl, unsigned int len)
+{
+ if (cl->cl_flags & HFSC_RSC)
+ init_ed(cl, len);
+ if (cl->cl_flags & HFSC_FSC)
+ init_vf(cl, len);
+
+ list_add_tail(&cl->dlist, &cl->sched->droplist);
+}
+
+static void
+set_passive(struct hfsc_class *cl)
+{
+ if (cl->cl_flags & HFSC_RSC)
+ ellist_remove(cl);
+
+ list_del(&cl->dlist);
+
+ /*
+ * actlist is now handled in update_vf() so that update_vf(cl, 0, 0)
+ * needs to be called explicitly to remove a class from actlist
+ */
+}
+
+/*
+ * hack to get length of first packet in queue.
+ */
+static unsigned int
+qdisc_peek_len(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+ unsigned int len;
+
+ skb = sch->dequeue(sch);
+ if (skb == NULL) {
+ if (net_ratelimit())
+ printk("qdisc_peek_len: non work-conserving qdisc ?\n");
+ return 0;
+ }
+ len = skb->len;
+ if (unlikely(sch->ops->requeue(skb, sch) != NET_XMIT_SUCCESS)) {
+ if (net_ratelimit())
+ printk("qdisc_peek_len: failed to requeue\n");
+ return 0;
+ }
+ return len;
+}
+
+static void
+hfsc_purge_queue(struct Qdisc *sch, struct hfsc_class *cl)
+{
+ unsigned int len = cl->qdisc->q.qlen;
+
+ qdisc_reset(cl->qdisc);
+ if (len > 0) {
+ update_vf(cl, 0, 0);
+ set_passive(cl);
+ sch->q.qlen -= len;
+ }
+}
+
+static void
+hfsc_adjust_levels(struct hfsc_class *cl)
+{
+ struct hfsc_class *p;
+ unsigned int level;
+
+ do {
+ level = 0;
+ list_for_each_entry(p, &cl->children, siblings) {
+ if (p->level > level)
+ level = p->level;
+ }
+ cl->level = level + 1;
+ } while ((cl = cl->cl_parent) != NULL);
+}
+
+static inline unsigned int
+hfsc_hash(u_int32_t h)
+{
+ h ^= h >> 8;
+ h ^= h >> 4;
+
+ return h & (HFSC_HSIZE - 1);
+}
+
+static inline struct hfsc_class *
+hfsc_find_class(u_int32_t classid, struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+
+ list_for_each_entry(cl, &q->clhash[hfsc_hash(classid)], hlist) {
+ if (cl->classid == classid)
+ return cl;
+ }
+ return NULL;
+}
+
+static void
+hfsc_change_rsc(struct hfsc_class *cl, struct tc_service_curve *rsc,
+ u_int64_t cur_time)
+{
+ sc2isc(rsc, &cl->cl_rsc);
+ rtsc_init(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
+ cl->cl_eligible = cl->cl_deadline;
+ if (cl->cl_rsc.sm1 <= cl->cl_rsc.sm2) {
+ cl->cl_eligible.dx = 0;
+ cl->cl_eligible.dy = 0;
+ }
+ cl->cl_flags |= HFSC_RSC;
+}
+
+static void
+hfsc_change_fsc(struct hfsc_class *cl, struct tc_service_curve *fsc)
+{
+ sc2isc(fsc, &cl->cl_fsc);
+ rtsc_init(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total);
+ cl->cl_flags |= HFSC_FSC;
+}
+
+static void
+hfsc_change_usc(struct hfsc_class *cl, struct tc_service_curve *usc,
+ u_int64_t cur_time)
+{
+ sc2isc(usc, &cl->cl_usc);
+ rtsc_init(&cl->cl_ulimit, &cl->cl_usc, cur_time, cl->cl_total);
+ cl->cl_flags |= HFSC_USC;
+}
+
+static int
+hfsc_change_class(struct Qdisc *sch, u_int32_t classid, u_int32_t parentid,
+ struct rtattr **tca, unsigned long *arg)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl = (struct hfsc_class *)*arg;
+ struct hfsc_class *parent = NULL;
+ struct rtattr *opt = tca[TCA_OPTIONS-1];
+ struct rtattr *tb[TCA_HFSC_MAX];
+ struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
+ u_int64_t cur_time;
+
+ if (opt == NULL ||
+ rtattr_parse(tb, TCA_HFSC_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)))
+ return -EINVAL;
+
+ if (tb[TCA_HFSC_RSC-1]) {
+ if (RTA_PAYLOAD(tb[TCA_HFSC_RSC-1]) < sizeof(*rsc))
+ return -EINVAL;
+ rsc = RTA_DATA(tb[TCA_HFSC_RSC-1]);
+ if (rsc->m1 == 0 && rsc->m2 == 0)
+ rsc = NULL;
+ }
+
+ if (tb[TCA_HFSC_FSC-1]) {
+ if (RTA_PAYLOAD(tb[TCA_HFSC_FSC-1]) < sizeof(*fsc))
+ return -EINVAL;
+ fsc = RTA_DATA(tb[TCA_HFSC_FSC-1]);
+ if (fsc->m1 == 0 && fsc->m2 == 0)
+ fsc = NULL;
+ }
+
+ if (tb[TCA_HFSC_USC-1]) {
+ if (RTA_PAYLOAD(tb[TCA_HFSC_USC-1]) < sizeof(*usc))
+ return -EINVAL;
+ usc = RTA_DATA(tb[TCA_HFSC_USC-1]);
+ if (usc->m1 == 0 && usc->m2 == 0)
+ usc = NULL;
+ }
+
+ if (cl != NULL) {
+ if (parentid) {
+ if (cl->cl_parent && cl->cl_parent->classid != parentid)
+ return -EINVAL;
+ if (cl->cl_parent == NULL && parentid != TC_H_ROOT)
+ return -EINVAL;
+ }
+ PSCHED_GET_TIME(cur_time);
+
+ sch_tree_lock(sch);
+ if (rsc != NULL)
+ hfsc_change_rsc(cl, rsc, cur_time);
+ if (fsc != NULL)
+ hfsc_change_fsc(cl, fsc);
+ if (usc != NULL)
+ hfsc_change_usc(cl, usc, cur_time);
+
+ if (cl->qdisc->q.qlen != 0) {
+ if (cl->cl_flags & HFSC_RSC)
+ update_ed(cl, qdisc_peek_len(cl->qdisc));
+ if (cl->cl_flags & HFSC_FSC)
+ update_vf(cl, 0, cur_time);
+ }
+ sch_tree_unlock(sch);
+
+#ifdef CONFIG_NET_ESTIMATOR
+ if (tca[TCA_RATE-1]) {
+ qdisc_kill_estimator(&cl->stats);
+ qdisc_new_estimator(&cl->stats, tca[TCA_RATE-1]);
+ }
+#endif
+ return 0;
+ }
+
+ if (parentid == TC_H_ROOT)
+ return -EEXIST;
+
+ parent = &q->root;
+ if (parentid) {
+ parent = hfsc_find_class(parentid, sch);
+ if (parent == NULL)
+ return -ENOENT;
+ }
+
+ if (classid == 0 || TC_H_MAJ(classid ^ sch->handle) != 0)
+ return -EINVAL;
+ if (hfsc_find_class(classid, sch))
+ return -EEXIST;
+
+ if (rsc == NULL && fsc == NULL)
+ return -EINVAL;
+
+ cl = kmalloc(sizeof(struct hfsc_class), GFP_KERNEL);
+ if (cl == NULL)
+ return -ENOBUFS;
+ memset(cl, 0, sizeof(struct hfsc_class));
+
+ if (rsc != NULL)
+ hfsc_change_rsc(cl, rsc, 0);
+ if (fsc != NULL)
+ hfsc_change_fsc(cl, fsc);
+ if (usc != NULL)
+ hfsc_change_usc(cl, usc, 0);
+
+ cl->refcnt = 1;
+ cl->classid = classid;
+ cl->sched = q;
+ cl->cl_parent = parent;
+ cl->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+ if (cl->qdisc == NULL)
+ cl->qdisc = &noop_qdisc;
+ cl->stats.lock = &sch->dev->queue_lock;
+ INIT_LIST_HEAD(&cl->children);
+ INIT_LIST_HEAD(&cl->actlist);
+
+ sch_tree_lock(sch);
+ list_add_tail(&cl->hlist, &q->clhash[hfsc_hash(classid)]);
+ list_add_tail(&cl->siblings, &parent->children);
+ if (parent->level == 0)
+ hfsc_purge_queue(sch, parent);
+ hfsc_adjust_levels(parent);
+ sch_tree_unlock(sch);
+
+#ifdef CONFIG_NET_ESTIMATOR
+ if (tca[TCA_RATE-1])
+ qdisc_new_estimator(&cl->stats, tca[TCA_RATE-1]);
+#endif
+ *arg = (unsigned long)cl;
+ return 0;
+}
+
+static void
+hfsc_destroy_filters(struct tcf_proto **fl)
+{
+ struct tcf_proto *tp;
+
+ while ((tp = *fl) != NULL) {
+ *fl = tp->next;
+ tcf_destroy(tp);
+ }
+}
+
+static void
+hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+
+ hfsc_destroy_filters(&cl->filter_list);
+ qdisc_destroy(cl->qdisc);
+#ifdef CONFIG_NET_ESTIMATOR
+ qdisc_kill_estimator(&cl->stats);
+#endif
+ if (cl != &q->root)
+ kfree(cl);
+}
+
+static int
+hfsc_delete_class(struct Qdisc *sch, unsigned long arg)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+ if (cl->level > 0 || cl->filter_cnt > 0 || cl == &q->root)
+ return -EBUSY;
+
+ sch_tree_lock(sch);
+
+ list_del(&cl->hlist);
+ list_del(&cl->siblings);
+ hfsc_adjust_levels(cl->cl_parent);
+ hfsc_purge_queue(sch, cl);
+ if (q->last_xmit == cl)
+ q->last_xmit = NULL;
+
+ if (--cl->refcnt == 0)
+ hfsc_destroy_class(sch, cl);
+
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static struct hfsc_class *
+hfsc_classify(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+ struct tcf_result res;
+ struct tcf_proto *tcf;
+ int result;
+
+ if (TC_H_MAJ(skb->priority ^ sch->handle) == 0 &&
+ (cl = hfsc_find_class(skb->priority, sch)) != NULL)
+ if (cl->level == 0)
+ return cl;
+
+ tcf = q->root.filter_list;
+ while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) {
+#ifdef CONFIG_NET_CLS_POLICE
+ if (result == TC_POLICE_SHOT)
+ return NULL;
+#endif
+ if ((cl = (struct hfsc_class *)res.class) == NULL) {
+ if ((cl = hfsc_find_class(res.classid, sch)) == NULL)
+ break; /* filter selected invalid classid */
+ }
+
+ if (cl->level == 0)
+ return cl; /* hit leaf class */
+
+ /* apply inner filter chain */
+ tcf = cl->filter_list;
+ }
+
+ /* classification failed, try default class */
+ cl = hfsc_find_class(TC_H_MAKE(TC_H_MAJ(sch->handle), q->defcls), sch);
+ if (cl == NULL || cl->level > 0)
+ return NULL;
+
+ return cl;
+}
+
+static int
+hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
+ struct Qdisc **old)
+{
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+ if (cl == NULL)
+ return -ENOENT;
+ if (cl->level > 0)
+ return -EINVAL;
+ if (new == NULL) {
+ new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+ if (new == NULL)
+ new = &noop_qdisc;
+ }
+
+ sch_tree_lock(sch);
+ hfsc_purge_queue(sch, cl);
+ *old = xchg(&cl->qdisc, new);
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static struct Qdisc *
+hfsc_class_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+ if (cl != NULL && cl->level == 0)
+ return cl->qdisc;
+
+ return NULL;
+}
+
+static unsigned long
+hfsc_get_class(struct Qdisc *sch, u_int32_t classid)
+{
+ struct hfsc_class *cl = hfsc_find_class(classid, sch);
+
+ if (cl != NULL)
+ cl->refcnt++;
+
+ return (unsigned long)cl;
+}
+
+static void
+hfsc_put_class(struct Qdisc *sch, unsigned long arg)
+{
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+ if (--cl->refcnt == 0)
+ hfsc_destroy_class(sch, cl);
+}
+
+static unsigned long
+hfsc_bind_tcf(struct Qdisc *sch, unsigned long parent, u_int32_t classid)
+{
+ struct hfsc_class *p = (struct hfsc_class *)parent;
+ struct hfsc_class *cl = hfsc_find_class(classid, sch);
+
+ if (cl != NULL) {
+ if (p != NULL && p->level <= cl->level)
+ return 0;
+ cl->filter_cnt++;
+ }
+
+ return (unsigned long)cl;
+}
+
+static void
+hfsc_unbind_tcf(struct Qdisc *sch, unsigned long arg)
+{
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+ cl->filter_cnt--;
+}
+
+static struct tcf_proto **
+hfsc_tcf_chain(struct Qdisc *sch, unsigned long arg)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+ if (cl == NULL)
+ cl = &q->root;
+
+ return &cl->filter_list;
+}
+
+static int
+hfsc_dump_sc(struct sk_buff *skb, int attr, struct internal_sc *sc)
+{
+ struct tc_service_curve tsc;
+
+ tsc.m1 = sm2m(sc->sm1);
+ tsc.d = dx2d(sc->dx);
+ tsc.m2 = sm2m(sc->sm2);
+ RTA_PUT(skb, attr, sizeof(tsc), &tsc);
+
+ return skb->len;
+
+ rtattr_failure:
+ return -1;
+}
+
+static inline int
+hfsc_dump_curves(struct sk_buff *skb, struct hfsc_class *cl)
+{
+ if ((cl->cl_flags & HFSC_RSC) &&
+ (hfsc_dump_sc(skb, TCA_HFSC_RSC, &cl->cl_rsc) < 0))
+ goto rtattr_failure;
+
+ if ((cl->cl_flags & HFSC_FSC) &&
+ (hfsc_dump_sc(skb, TCA_HFSC_FSC, &cl->cl_fsc) < 0))
+ goto rtattr_failure;
+
+ if ((cl->cl_flags & HFSC_USC) &&
+ (hfsc_dump_sc(skb, TCA_HFSC_USC, &cl->cl_usc) < 0))
+ goto rtattr_failure;
+
+ return skb->len;
+
+ rtattr_failure:
+ return -1;
+}
+
+static inline int
+hfsc_dump_stats(struct sk_buff *skb, struct hfsc_class *cl)
+{
+ cl->stats.qlen = cl->qdisc->q.qlen;
+ if (qdisc_copy_stats(skb, &cl->stats) < 0)
+ goto rtattr_failure;
+
+ return skb->len;
+
+ rtattr_failure:
+ return -1;
+}
+
+static inline int
+hfsc_dump_xstats(struct sk_buff *skb, struct hfsc_class *cl)
+{
+ struct tc_hfsc_stats xstats;
+
+ xstats.level = cl->level;
+ xstats.period = cl->cl_vtperiod;
+ xstats.work = cl->cl_total;
+ xstats.rtwork = cl->cl_cumul;
+ RTA_PUT(skb, TCA_XSTATS, sizeof(xstats), &xstats);
+
+ return skb->len;
+
+ rtattr_failure:
+ return -1;
+}
+
+static int
+hfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
+ struct tcmsg *tcm)
+{
+ struct hfsc_class *cl = (struct hfsc_class *)arg;
+ unsigned char *b = skb->tail;
+ struct rtattr *rta = (struct rtattr *)b;
+
+ tcm->tcm_parent = cl->cl_parent ? cl->cl_parent->classid : TC_H_ROOT;
+ tcm->tcm_handle = cl->classid;
+ if (cl->level == 0)
+ tcm->tcm_info = cl->qdisc->handle;
+
+ RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+ if (hfsc_dump_curves(skb, cl) < 0)
+ goto rtattr_failure;
+ rta->rta_len = skb->tail - b;
+
+ if ((hfsc_dump_stats(skb, cl) < 0) ||
+ (hfsc_dump_xstats(skb, cl) < 0))
+ goto rtattr_failure;
+
+ return skb->len;
+
+ rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static void
+hfsc_walk(struct Qdisc *sch, struct qdisc_walker *arg)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+ unsigned int i;
+
+ if (arg->stop)
+ return;
+
+ for (i = 0; i < HFSC_HSIZE; i++) {
+ list_for_each_entry(cl, &q->clhash[i], hlist) {
+ if (arg->count < arg->skip) {
+ arg->count++;
+ continue;
+ }
+ if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
+ arg->stop = 1;
+ return;
+ }
+ arg->count++;
+ }
+ }
+}
+
+static void
+hfsc_watchdog(unsigned long arg)
+{
+ struct Qdisc *sch = (struct Qdisc *)arg;
+
+ sch->flags &= ~TCQ_F_THROTTLED;
+ netif_schedule(sch->dev);
+}
+
+static void
+hfsc_schedule_watchdog(struct Qdisc *sch, u_int64_t cur_time)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+ u_int64_t next_time = 0;
+ long delay;
+
+ if ((cl = ellist_get_minel(&q->eligible)) != NULL)
+ next_time = cl->cl_e;
+ if (q->root.cl_cfmin != 0) {
+ if (next_time == 0 || next_time > q->root.cl_cfmin)
+ next_time = q->root.cl_cfmin;
+ }
+ ASSERT(next_time != 0);
+ delay = next_time - cur_time;
+ delay = PSCHED_US2JIFFIE(delay);
+
+ sch->flags |= TCQ_F_THROTTLED;
+ mod_timer(&q->wd_timer, jiffies + delay);
+}
+
+static int
+hfsc_init_qdisc(struct Qdisc *sch, struct rtattr *opt)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct tc_hfsc_qopt *qopt;
+ unsigned int i;
+
+ if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt))
+ return -EINVAL;
+ qopt = RTA_DATA(opt);
+
+ memset(q, 0, sizeof(struct hfsc_sched));
+ sch->stats.lock = &sch->dev->queue_lock;
+
+ q->defcls = qopt->defcls;
+ for (i = 0; i < HFSC_HSIZE; i++)
+ INIT_LIST_HEAD(&q->clhash[i]);
+ INIT_LIST_HEAD(&q->eligible);
+ INIT_LIST_HEAD(&q->droplist);
+
+ q->root.refcnt = 1;
+ q->root.classid = sch->handle;
+ q->root.sched = q;
+ q->root.qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+ if (q->root.qdisc == NULL)
+ q->root.qdisc = &noop_qdisc;
+ q->root.stats.lock = &sch->dev->queue_lock;
+ INIT_LIST_HEAD(&q->root.children);
+ INIT_LIST_HEAD(&q->root.actlist);
+
+ list_add(&q->root.hlist, &q->clhash[hfsc_hash(q->root.classid)]);
+
+ init_timer(&q->wd_timer);
+ q->wd_timer.function = hfsc_watchdog;
+ q->wd_timer.data = (unsigned long)sch;
+
+ return 0;
+}
+
+static int
+hfsc_change_qdisc(struct Qdisc *sch, struct rtattr *opt)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct tc_hfsc_qopt *qopt;
+
+ if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt))
+ return -EINVAL;;
+ qopt = RTA_DATA(opt);
+
+ sch_tree_lock(sch);
+ q->defcls = qopt->defcls;
+ sch_tree_unlock(sch);
+
+ return 0;
+}
+
+static void
+hfsc_reset_class(struct hfsc_class *cl)
+{
+ cl->cl_total = 0;
+ cl->cl_cumul = 0;
+ cl->cl_d = 0;
+ cl->cl_e = 0;
+ cl->cl_vt = 0;
+ cl->cl_vtadj = 0;
+ cl->cl_vtoff = 0;
+ cl->cl_cvtmin = 0;
+ cl->cl_cvtmax = 0;
+ cl->cl_vtperiod = 0;
+ cl->cl_parentperiod = 0;
+ cl->cl_f = 0;
+ cl->cl_myf = 0;
+ cl->cl_myfadj = 0;
+ cl->cl_cfmin = 0;
+ cl->cl_nactive = 0;
+ INIT_LIST_HEAD(&cl->actlist);
+ qdisc_reset(cl->qdisc);
+
+ if (cl->cl_flags & HFSC_RSC)
+ rtsc_init(&cl->cl_deadline, &cl->cl_rsc, 0, 0);
+ if (cl->cl_flags & HFSC_FSC)
+ rtsc_init(&cl->cl_virtual, &cl->cl_fsc, 0, 0);
+ if (cl->cl_flags & HFSC_USC)
+ rtsc_init(&cl->cl_ulimit, &cl->cl_usc, 0, 0);
+}
+
+static void
+hfsc_reset_qdisc(struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+ unsigned int i;
+
+ for (i = 0; i < HFSC_HSIZE; i++) {
+ list_for_each_entry(cl, &q->clhash[i], hlist)
+ hfsc_reset_class(cl);
+ }
+
+ INIT_LIST_HEAD(&q->eligible);
+ INIT_LIST_HEAD(&q->droplist);
+ q->last_xmit = NULL;
+ del_timer(&q->wd_timer);
+ sch->flags &= ~TCQ_F_THROTTLED;
+ sch->q.qlen = 0;
+}
+
+static void
+hfsc_destroy_qdisc(struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl, *next;
+ unsigned int i;
+
+ for (i = 0; i < HFSC_HSIZE; i++) {
+ list_for_each_entry_safe(cl, next, &q->clhash[i], hlist)
+ hfsc_destroy_class(sch, cl);
+ }
+
+ del_timer(&q->wd_timer);
+}
+
+static int
+hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ unsigned char *b = skb->tail;
+ struct tc_hfsc_qopt qopt;
+
+ qopt.defcls = q->defcls;
+ RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
+
+ sch->stats.qlen = sch->q.qlen;
+ if (qdisc_copy_stats(skb, &sch->stats) < 0)
+ goto rtattr_failure;
+
+ return skb->len;
+
+ rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int
+hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct hfsc_class *cl = hfsc_classify(skb, sch);
+ unsigned int len = skb->len;
+ int err;
+
+ if (cl == NULL) {
+ kfree_skb(skb);
+ sch->stats.drops++;
+ return NET_XMIT_DROP;
+ }
+
+ err = cl->qdisc->enqueue(skb, cl->qdisc);
+ if (unlikely(err != NET_XMIT_SUCCESS)) {
+ cl->stats.drops++;
+ sch->stats.drops++;
+ return err;
+ }
+
+ if (cl->qdisc->q.qlen == 1)
+ set_active(cl, len);
+
+ cl->stats.packets++;
+ cl->stats.bytes += len;
+ sch->stats.packets++;
+ sch->stats.bytes += len;
+ sch->q.qlen++;
+
+ return NET_XMIT_SUCCESS;
+}
+
+static struct sk_buff *
+hfsc_dequeue(struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+ struct sk_buff *skb;
+ u_int64_t cur_time;
+ unsigned int next_len;
+ int realtime = 0;
+
+ if (sch->q.qlen == 0)
+ return NULL;
+
+ PSCHED_GET_TIME(cur_time);
+
+ /*
+ * if there are eligible classes, use real-time criteria.
+ * find the class with the minimum deadline among
+ * the eligible classes.
+ */
+ if ((cl = ellist_get_mindl(&q->eligible, cur_time)) != NULL) {
+ realtime = 1;
+ } else {
+ /*
+ * use link-sharing criteria
+ * get the class with the minimum vt in the hierarchy
+ */
+ cl = actlist_get_minvt(&q->root, cur_time);
+ if (cl == NULL) {
+ sch->stats.overlimits++;
+ if (!netif_queue_stopped(sch->dev))
+ hfsc_schedule_watchdog(sch, cur_time);
+ return NULL;
+ }
+ }
+
+ skb = cl->qdisc->dequeue(cl->qdisc);
+ ASSERT(skb != NULL);
+
+ update_vf(cl, skb->len, cur_time);
+ if (realtime)
+ cl->cl_cumul += skb->len;
+
+ if (cl->qdisc->q.qlen != 0) {
+ if (cl->cl_flags & HFSC_RSC) {
+ /* update ed */
+ next_len = qdisc_peek_len(cl->qdisc);
+ if (realtime)
+ update_ed(cl, next_len);
+ else
+ update_d(cl, next_len);
+ }
+ } else {
+ /* the class becomes passive */
+ set_passive(cl);
+ }
+
+ q->last_xmit = cl;
+ sch->flags &= ~TCQ_F_THROTTLED;
+ sch->q.qlen--;
+
+ return skb;
+}
+
+static int
+hfsc_requeue(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl = q->last_xmit;
+ unsigned int len = skb->len;
+ int ret;
+
+ if (cl == NULL) {
+ kfree_skb(skb);
+ sch->stats.drops++;
+ return NET_XMIT_DROP;
+ }
+
+ ret = cl->qdisc->ops->requeue(skb, cl->qdisc);
+ if (ret == NET_XMIT_SUCCESS) {
+ if (cl->qdisc->q.qlen == 1)
+ set_active(cl, len);
+ sch->q.qlen++;
+ } else {
+ cl->stats.drops++;
+ sch->stats.drops++;
+ }
+ q->last_xmit = NULL;
+
+ return ret;
+}
+
+static unsigned int
+hfsc_drop(struct Qdisc *sch)
+{
+ struct hfsc_sched *q = (struct hfsc_sched *)sch->data;
+ struct hfsc_class *cl;
+ unsigned int len;
+
+ list_for_each_entry(cl, &q->droplist, dlist) {
+ if (cl->qdisc->ops->drop != NULL &&
+ (len = cl->qdisc->ops->drop(cl->qdisc)) > 0) {
+ if (cl->qdisc->q.qlen == 0) {
+ update_vf(cl, 0, 0);
+ set_passive(cl);
+ } else {
+ list_move_tail(&cl->dlist, &q->droplist);
+ }
+ cl->stats.drops++;
+ sch->stats.drops++;
+ sch->q.qlen--;
+ return len;
+ }
+ }
+ return 0;
+}
+
+static struct Qdisc_class_ops hfsc_class_ops = {
+ .change = hfsc_change_class,
+ .delete = hfsc_delete_class,
+ .graft = hfsc_graft_class,
+ .leaf = hfsc_class_leaf,
+ .get = hfsc_get_class,
+ .put = hfsc_put_class,
+ .bind_tcf = hfsc_bind_tcf,
+ .unbind_tcf = hfsc_unbind_tcf,
+ .tcf_chain = hfsc_tcf_chain,
+ .dump = hfsc_dump_class,
+ .walk = hfsc_walk
+};
+
+struct Qdisc_ops hfsc_qdisc_ops = {
+ .id = "hfsc",
+ .init = hfsc_init_qdisc,
+ .change = hfsc_change_qdisc,
+ .reset = hfsc_reset_qdisc,
+ .destroy = hfsc_destroy_qdisc,
+ .dump = hfsc_dump_qdisc,
+ .enqueue = hfsc_enqueue,
+ .dequeue = hfsc_dequeue,
+ .requeue = hfsc_requeue,
+ .drop = hfsc_drop,
+ .cl_ops = &hfsc_class_ops,
+ .priv_size = sizeof(struct hfsc_sched),
+ .owner = THIS_MODULE
+};
+
+static int __init
+hfsc_init(void)
+{
+ return register_qdisc(&hfsc_qdisc_ops);
+}
+
+static void __exit
+hfsc_cleanup(void)
+{
+ unregister_qdisc(&hfsc_qdisc_ops);
+}
+
+MODULE_LICENSE("GPL");
+module_init(hfsc_init);
+module_exit(hfsc_cleanup);
^ permalink raw reply
* Re: Request: I/O request recording
From: Andrew Morton @ 2004-01-26 11:57 UTC (permalink / raw)
To: Bart Samwel; +Cc: felix-kernel, linux-kernel
In-Reply-To: <4014FF00.1020106@samwel.tk>
Bart Samwel <bart@samwel.tk> wrote:
>
> 2. Swapfiles apparently don't like to be touched. I did an
> ioctl(FIGETBSZ) on a swapfile, and it would simply block until I did a
> swapoff on the file. I didn't even get to the FIBMAP part. :( Is this
> correct behaviour?
yup.
> And is there any way to detect this so that I can work around it?
swapoff -a beforehand, I guess.
^ permalink raw reply
* Re: MPC823E SMC/I2C/SPI micropatch
From: Jan Damborsky @ 2004-01-26 11:52 UTC (permalink / raw)
To: Wolfgang Denk, linuxppc-dev
In-Reply-To: <20040126113201.DBE4CC108D@atlas.denx.de>
Wolfgang Denk wrote:
> In message <4014EC18.2000303@devcom.cz> you wrote:
>
>>>>- it is determined for 850 and 823E processors (I hope).
>>>
>>>Wrong.
>>
>>But I really do not uderstand, why there is following
>>sentence in micropatch_850.c file:
>> The patch applicable to the 850 and 823E processors.
>>And in micropatch_8xx.c:
>> The patch applicable to all 8xx family except 850 and 823E.
>>I think it is rather misleading, is'nt it ?
>
>
> Yes, it is. Sorry about this. Seems we forgot to update this comment
> in the linuxppc_2_4_devel tree (it was fixed in the linux-2.4 tree).
> Fixed now - thanks for pointing out.
>
I thank you for spending your time to persuade me:-)
Sincerely, Jan Damborsky
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
^ permalink raw reply
* Re: Request: I/O request recording
From: Bart Samwel @ 2004-01-26 11:50 UTC (permalink / raw)
To: Andrew Morton; +Cc: felix-kernel, linux-kernel
In-Reply-To: <20040125153803.4d7e1015.akpm@osdl.org>
Andrew Morton wrote:
> Bart Samwel <bart@samwel.tk> wrote:
>
>>>Linux caches disk data on a per-file basis. So if you preload pagecache
>>>via the /dev/hda1 "file", that is of no benefit to the /etc/passwd file.
>>>Each one has its own unique pagecache. When reading pages for /etc/passwd
>>>we don't go looking for the same disk blocks in the cache of /dev/hda1.
>>>
>>>Which is why the userspace cache preloading needs to know the pathnames of
>>>all the relevant files - it needs to open and read each one, applying
>>>knowledge of disk layout while doing it.
>>
>>Hmmm, that explains why this didn't work. :( So if I wanted to do this
>>completely from user space using only block_dump data I'd probably have
>>to go through all files and find out if they had any blocks in common
>>with my preload set -- presuming there is a way to find that out, which
>>there probably isn't. That makes this idea pretty much useless, I'm
>>sorry to have bothered you with it.
>
> You could certainly do that. Given disk block #N you need to search all
> files on the disk asking "who owns this block". The FIBMAP ioctl can be
> used on most filesystems (ext2, ext3, others..) to find out which blocks a
> file is using. See bmap.c in
>
> http://www.zip.com.au/~akpm/linux/patches/stuff/ext3-tools.tar.gz
>
> Unfortunately you cannot determine a directory's blocks in this way.
> Ext3's directories live in the /dev/hda1 pagecache anyway. ext2's
> directories each have their own pagecache.
I found out two things while trying to do this:
1. Many filesystems in linux set f_fsid to zero for statfs. I was trying
to use this to skip over mount points, but that doesn't work. Had to use
the st_dev field from stat instead. :(
2. Swapfiles apparently don't like to be touched. I did an
ioctl(FIGETBSZ) on a swapfile, and it would simply block until I did a
swapoff on the file. I didn't even get to the FIBMAP part. :( Is this
correct behaviour? And is there any way to detect this so that I can
work around it?
-- Bart
^ permalink raw reply
* Announcement: MPHY now supported in mpc860sar
From: Alex Zeffertt @ 2004-01-26 11:41 UTC (permalink / raw)
To: linux-atm-general@lists.sourceforge.net,
linuxppc-embedded@lists.linuxppc.org
Just a quick note to say that multi-phy is now supported by the
mpc860sar ATM driver, thanks to a patch by Rodolfo Giometti.
Page: http://sourceforge.net/projects/mpc860sar/
Alex
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply
* FX8010 on emu10k1
From: holborn @ 2004-01-26 11:36 UTC (permalink / raw)
To: alsa-devel
Hi!
Right now I'm using ld10k1 a wonderful program by Peter Zubaj and i have
chorus effect in my emu10k1 synthesizer, when a MIDI control reverb or chorus
message is sent to the emu10k1 hi route the instrument sound to the FX(12)
and FX(13), then i put 2 chorus in stereo and miracle ....chorus effect....
and not to bad :-)
Now i need a reverb effect, i was see fv10k1 utility (freverb implementation
for FX8010), but .... i can't run, i hacked a little bit, i loaded ....
but ... i only hear delay effect, not reverb. Now I will try to implement a
small reverb made by me.
But my question is .... i see in emu10k1 device3 the 8 first FX8010 buses, but
only for playback, anyway to capture FX(12) and FX(13) buses?
Thanks
Josep
-------------------------------------------------------
The SF.Net email is sponsored by EclipseCon 2004
Premiere Conference on Open Tools Development and Integration
See the breadth of Eclipse activity. February 3-5 in Anaheim, CA.
http://www.eclipsecon.org/osdn
^ permalink raw reply
* Re: [ANNOUNCE] Cooperative Linux
From: Rik van Riel @ 2004-01-26 11:35 UTC (permalink / raw)
To: Karim Yaghmour; +Cc: Nuno Silva, JustFillBug, linux-kernel
In-Reply-To: <4014B573.1020703@opersys.com>
On Mon, 26 Jan 2004, Karim Yaghmour wrote:
> So, for example, Xen assumes that all OSes are going to use the same
> devices for I/O: same disk, same NIC, etc. It therefore implements lots
> of virtual devices for these.
Consolidation means more efficient hardware use ...
> Wouldn't it be just better to reuse the existing work on the hotplug
> hardware (hotplug CPU, hotplug memory, etc.) to have the kernels
> get/return hardware resources to the nanokernel?
That means a loss of flexibility. Furthermore, these hotplug
patches don't seem ready yet.
Rik
--
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it." - Brian W. Kernighan
^ permalink raw reply
* Re: [ANNOUNCE] Cooperative Linux
From: Rik van Riel @ 2004-01-26 11:32 UTC (permalink / raw)
To: JustFillBug; +Cc: linux-kernel
In-Reply-To: <slrnc193vo.42h.mozbugbox@mozbugbox.somehost.org>
On Mon, 26 Jan 2004, JustFillBug wrote:
> How about a bare bone OS whose sole purpose is to run multiple OS on top
> of it? A pure VM OS.
It's easier if that OS is Linux, so we can reuse all the
device drivers.
--
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it." - Brian W. Kernighan
^ permalink raw reply
* Re: MPC823E SMC/I2C/SPI micropatch
From: Wolfgang Denk @ 2004-01-26 11:31 UTC (permalink / raw)
To: Jan Damborsky; +Cc: linuxppc-dev
In-Reply-To: <4014EC18.2000303@devcom.cz>
In message <4014EC18.2000303@devcom.cz> you wrote:
>
> >>- it is determined for 850 and 823E processors (I hope).
> >
> > Wrong.
> But I really do not uderstand, why there is following
> sentence in micropatch_850.c file:
> The patch applicable to the 850 and 823E processors.
> And in micropatch_8xx.c:
> The patch applicable to all 8xx family except 850 and 823E.
> I think it is rather misleading, is'nt it ?
Yes, it is. Sorry about this. Seems we forgot to update this comment
in the linuxppc_2_4_devel tree (it was fixed in the linux-2.4 tree).
Fixed now - thanks for pointing out.
Best regards,
Wolfgang Denk
--
See us @ Embedded World, Nuremberg, Feb 17 - 19, Hall 12.0 Booth 440
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
1st Old Man: Gee, its windy today.
2nd Old Man: No it's not... it's Thursday.
3rd Old Man: Yeh, me too. Let's go for a beer.
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
^ permalink raw reply
* [PATCH] 2/2 DECnet fix crash reading /proc/net/decnet_cache
From: Patrick Caulfield @ 2004-01-26 11:31 UTC (permalink / raw)
To: davem, linux-kernel; +Cc: Steve Whitehouse
This patch fixes an oops if a user reads /proc/net/decnet_cache
--- net/decnet/dn_route.c 24 Nov 2003 03:16:28 -0000 1.3
+++ net/decnet/dn_route.c 17 Dec 2003 09:54:37 -0000
@@ -1720,7 +1720,8 @@
static void dn_rt_cache_seq_stop(struct seq_file *seq, void *v)
{
- rcu_read_unlock();
+ if (v)
+ rcu_read_unlock();
}
static int dn_rt_cache_seq_show(struct seq_file *seq, void *v)
patrick
^ permalink raw reply
* [PATCH] 1/2 DECnet fix SDF_WILD
From: Patrick Caulfield @ 2004-01-26 11:31 UTC (permalink / raw)
To: davem, linux-kernel; +Cc: Steve Whitehouse
This patch fixes the operation of SDF_WILD sockets on Linux 2.6.0/1
(they don't currently work at all).
--- net/decnet/af_decnet.c.orig 2003-12-08 11:27:59.000000000 +0000
+++ net/decnet/af_decnet.c 2003-12-08 11:28:34.000000000 +0000
@@ -163,7 +163,7 @@
struct dn_scp *scp = DN_SK(sk);
if (scp->addr.sdn_flags & SDF_WILD)
- return hlist_empty(&dn_wild_sk) ? NULL : &dn_wild_sk;
+ return hlist_empty(&dn_wild_sk) ? &dn_wild_sk : NULL;
return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK];
}
patrick
^ permalink raw reply
* INT14 waiting for character
From: Donald Arseneau @ 2004-01-26 11:28 UTC (permalink / raw)
To: linux-msdos
Hello,
Attempting to run an old program under dosemu, I and collegues
have run afoul of the INT14 emulation, specifically:
These routines are not flawless since it does not wait
for a character during receive, and this may confuse some
programs.
Is there any hope of getting a version of INT14 that waits for
a character? Either by patching the current code, or by using
a different IO support library? I think something that did
high-level access to the linux port would work fine for us.
If there is some known fix for this, I would love to hear it.
If is not, then I could help with improving it; not that I know
anything on the topic, but I migght be able to scrounge some help.
(Is Mark Rejhon still involved?)
Donald Arseneau asnd@triumf.ca
^ permalink raw reply
* Re: [BENCHMARKS] Namesys VM patches improve kbuild
From: Nick Piggin @ 2004-01-26 11:25 UTC (permalink / raw)
To: Nikita Danilov; +Cc: Andrew Morton, linux-mm
In-Reply-To: <16404.63446.649110.348477@laputa.namesys.com>
Nikita Danilov wrote:
>Nick Piggin writes:
> >
>
>[...]
>
> >
> > But by clearing the referenced bit when below the reclaim_mapped
> > threshold, you're throwing this information away.
> >
> > Say you have 16 mapped pages on the active list, 8 referenced, 8 not.
> > You do a !reclaim_mapped scan. Your 16 pages are now in the same
> > order and none are referenced. You now do a reclaim_mapped scan and
> > reclaim 8 pages. 4 of them were the referenced ones, 4 were not.
> >
> > With my change, you would reclaim all 8 non referenced pages.
>
>Which is wrong, because none of them was referenced _recently_. These
>pages are cold, according to the VM's notion of hotness. (Long time
>probably has passed between !reclaim_mapped and reclaim_mapped scans in
>your example.)
>
Well you'd have to admit the referenced pages are hotter, but
I guess I can't argue with the numbers: it must not be very
significant.
I just wonder why your patch makes such an improvement. You're
basically putting mapped pages to one side until reclaim_mapped,
which is similar to what my patch does, right?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"aart@kvack.org"> aart@kvack.org </a>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.