mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![David Howells](/assets/img/avatar_default.png)
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
93 lines
2.7 KiB
C
93 lines
2.7 KiB
C
|
|
/*
|
|
* Index to functions.
|
|
*/
|
|
|
|
static int el1_probe1(struct net_device *dev, int ioaddr);
|
|
static int el_open(struct net_device *dev);
|
|
static void el_timeout(struct net_device *dev);
|
|
static int el_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
|
static irqreturn_t el_interrupt(int irq, void *dev_id);
|
|
static void el_receive(struct net_device *dev);
|
|
static void el_reset(struct net_device *dev);
|
|
static int el1_close(struct net_device *dev);
|
|
static struct net_device_stats *el1_get_stats(struct net_device *dev);
|
|
static void set_multicast_list(struct net_device *dev);
|
|
static const struct ethtool_ops netdev_ethtool_ops;
|
|
|
|
#define EL1_IO_EXTENT 16
|
|
|
|
#ifndef EL_DEBUG
|
|
#define EL_DEBUG 0 /* use 0 for production, 1 for devel., >2 for debug */
|
|
#endif /* Anything above 5 is wordy death! */
|
|
#define debug el_debug
|
|
static int el_debug = EL_DEBUG;
|
|
|
|
/*
|
|
* Board-specific info in dev->priv.
|
|
*/
|
|
|
|
struct net_local
|
|
{
|
|
struct net_device_stats stats;
|
|
int tx_pkt_start; /* The length of the current Tx packet. */
|
|
int collisions; /* Tx collisions this packet */
|
|
int loading; /* Spot buffer load collisions */
|
|
int txing; /* True if card is in TX mode */
|
|
spinlock_t lock; /* Serializing lock */
|
|
};
|
|
|
|
|
|
#define RX_STATUS (ioaddr + 0x06)
|
|
#define RX_CMD RX_STATUS
|
|
#define TX_STATUS (ioaddr + 0x07)
|
|
#define TX_CMD TX_STATUS
|
|
#define GP_LOW (ioaddr + 0x08)
|
|
#define GP_HIGH (ioaddr + 0x09)
|
|
#define RX_BUF_CLR (ioaddr + 0x0A)
|
|
#define RX_LOW (ioaddr + 0x0A)
|
|
#define RX_HIGH (ioaddr + 0x0B)
|
|
#define SAPROM (ioaddr + 0x0C)
|
|
#define AX_STATUS (ioaddr + 0x0E)
|
|
#define AX_CMD AX_STATUS
|
|
#define DATAPORT (ioaddr + 0x0F)
|
|
#define TX_RDY 0x08 /* In TX_STATUS */
|
|
|
|
#define EL1_DATAPTR 0x08
|
|
#define EL1_RXPTR 0x0A
|
|
#define EL1_SAPROM 0x0C
|
|
#define EL1_DATAPORT 0x0f
|
|
|
|
/*
|
|
* Writes to the ax command register.
|
|
*/
|
|
|
|
#define AX_OFF 0x00 /* Irq off, buffer access on */
|
|
#define AX_SYS 0x40 /* Load the buffer */
|
|
#define AX_XMIT 0x44 /* Transmit a packet */
|
|
#define AX_RX 0x48 /* Receive a packet */
|
|
#define AX_LOOP 0x0C /* Loopback mode */
|
|
#define AX_RESET 0x80
|
|
|
|
/*
|
|
* Normal receive mode written to RX_STATUS. We must intr on short packets
|
|
* to avoid bogus rx lockups.
|
|
*/
|
|
|
|
#define RX_NORM 0xA8 /* 0x68 == all addrs, 0xA8 only to me. */
|
|
#define RX_PROM 0x68 /* Senior Prom, uhmm promiscuous mode. */
|
|
#define RX_MULT 0xE8 /* Accept multicast packets. */
|
|
#define TX_NORM 0x0A /* Interrupt on everything that might hang the chip */
|
|
|
|
/*
|
|
* TX_STATUS register.
|
|
*/
|
|
|
|
#define TX_COLLISION 0x02
|
|
#define TX_16COLLISIONS 0x04
|
|
#define TX_READY 0x08
|
|
|
|
#define RX_RUNT 0x08
|
|
#define RX_MISSED 0x01 /* Missed a packet due to 3c501 braindamage. */
|
|
#define RX_GOOD 0x30 /* Good packet 0x20, or simple overflow 0x10. */
|
|
|